<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 code to expose the DOM to JavaScript through IDL bindings.
//!
//! Exposing a DOM object to JavaScript
//! ===================================
//!
//! As [explained earlier](../index.html#a-dom-object-and-its-reflector), the
//! implementation of an interface `Foo` involves two objects: the DOM object
//! (implemented in Rust) and the reflector (a `JSObject`).
//!
//! In order to expose the interface's members to the web, properties
//! corresponding to the operations and attributes are defined on an object in
//! the reflector's prototype chain or on the reflector itself.
//!
//! Typically, these properties are either value properties whose value is a
//! function (for operations) or accessor properties that have a getter and
//! optionally a setter function (for attributes, depending on whether they are
//! marked `readonly`).
//!
//! All these JavaScript functions are set up such that, when they're called,
//! they call a Rust function in the generated glue code. This glue code does
//! some sanity checks and [argument conversions](conversions/index.html), and
//! calls into API implementation for the DOM object.
//!
//! Rust reflections of WebIDL constructs
//! =====================================
//!
//! WebIDL members are turned into methods on the DOM object (static methods
//! for a static members and instance methods for regular members).
//!
//! The instance methods for an interface `Foo` are defined on a
//! `dom::bindings::codegen::Bindings::FooBindings::FooMethods` trait. This
//! trait is then implemented for `JSRef<'a, Foo>`.
//!
//! The return type and argument types are determined [as described below]
//! (#rust-reflections-of-webidl-types).
//! In addition to those, all methods that are [allowed to throw]
//! (#throwing-exceptions) will have the return value wrapped in
//! [`Fallible<T>`](error/type.Fallible.html).
//! Methods that use certain WebIDL types like `any` or `object` will get a
//! `*mut JSContext` argument prepended to the argument list. Static methods
//! will be passed a [`GlobalRef`](global/enum.GlobalRef.html) for the relevant
//! global. This argument comes before the `*mut JSContext` argument, if any.
//!
//! Rust reflections of WebIDL operations (methods)
//! -----------------------------------------------
//!
//! A WebIDL operation is turned into one method for every overload.
//! The first overload gets the base name, and consecutive overloads have an
//! underscore appended to the name.
//!
//! The base name of the Rust method is simply the name of the WebIDL operation
//! with the first letter converted to uppercase.
//!
//! Rust reflections of WebIDL attributes
//! -------------------------------------
//!
//! A WebIDL attribute is turned into a pair of methods: one for the getter and
//! one for the setter. A readonly attribute only has a getter and no setter.
//!
//! The getter's name is the name of the attribute with the first letter
//! converted to uppercase. It has `Get` prepended to it if the type of the
//! attribute is nullable or if the getter can throw.
//!
//! The method signature for the getter looks just like an operation with no
//! arguments and the attribute's type as the return type.
//!
//! The setter's name is `Set` followed by the name of the attribute with the
//! first letter converted to uppercase. The method signature looks just like
//! an operation with a void return value and a single argument whose type is
//! the attribute's type.
//!
//! Rust reflections of WebIDL constructors
//! ---------------------------------------
//!
//! A WebIDL constructor is turned into a static class method named
//! `Constructor`. The arguments of this method will be the arguments of the
//! WebIDL constructor, with a `GlobalRef` for the relevant global prepended.
//! The return value of the constructor for MyInterface is exactly the same as
//! that of a method returning an instance of MyInterface. Constructors are
//! always [allowed to throw](#throwing-exceptions).
//!
//! Rust reflections of WebIDL types
//! --------------------------------
//!
//! The exact Rust representation for WebIDL types can depend on the precise
//! way that they're being used (e.g., return values and arguments might have
//! different representations).
//!
//! Optional arguments which do not have a default value are represented by
//! wrapping `Option<T>` around the representation of the argument type.
//! Optional arguments which do have a default value are represented by the
//! argument type itself, set to the default value if the argument was not in
//! fact passed in.
//!
//! Variadic WebIDL arguments are represented by wrapping a `Vec<T>` around the
//! representation of the argument type.
//!
//! See [the type mapping for particular types](conversions/index.html).
//!
//! Rust reflections of stringifiers
//! --------------------------------
//!
//! *To be written.*
//!
//! Rust reflections of legacy callers
//! ---------------------------------
//!
//! Legacy callers are not yet implemented.
//!
//! Rust reflections of getters and setters
//! ---------------------------------------
//!
//! *To be written.*
//!
//! Throwing exceptions
//! ===================
//!
//! WebIDL methods, getters, and setters that need to throw exceptions need to
//! be explicitly marked as such with the `[Throws]`, `[GetterThrows]` and
//! `[SetterThrows]` custom attributes.
//!
//! `[Throws]` applies to both methods and attributes; for attributes it means
//! both the getter and the setter (if any) can throw. `[GetterThrows]` applies
//! only to attributes. `[SetterThrows]` applies only to writable attributes.
//!
//! The corresponding Rust methods will have the return value wrapped in
//! [`Fallible<T>`](error/type.Fallible.html). To throw an exception, simply
//! return `Err()` from the method with the appropriate [error value]
//! (error/enum.Error.html).

pub mod callback;
pub mod cell;
pub mod conversions;
pub mod error;
pub mod global;
pub mod js;
pub mod num;
pub mod proxyhandler;
pub mod refcounted;
pub mod str;
pub mod structuredclone;
pub mod trace;
pub mod utils;

/// Generated JS-Rust bindings.
#[allow(missing_docs, non_snake_case)]
pub mod codegen {
    // FIXME(#5853) we shouldn't need to
    // allow moved_no_move here
    #[allow(unrooted_must_root, moved_no_move)]
    pub mod Bindings {
        include!(concat!(env!("OUT_DIR"), "/Bindings/mod.rs"));
    }
    pub mod InterfaceTypes {
        include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
    }
    #[allow(unused_imports)]
    pub mod InheritTypes {
        include!(concat!(env!("OUT_DIR"), "/InheritTypes.rs"));
    }
    pub mod PrototypeList {
        include!(concat!(env!("OUT_DIR"), "/PrototypeList.rs"));
    }
    #[allow(non_camel_case_types, non_upper_case_globals,
            unused_imports, unused_variables, unused_assignments)]
    pub mod RegisterBindings {
        include!(concat!(env!("OUT_DIR"), "/RegisterBindings.rs"));
    }
    #[allow(non_camel_case_types, non_upper_case_globals,
            unused_imports, unused_variables, unused_assignments)]
    pub mod UnionTypes {
        include!(concat!(env!("OUT_DIR"), "/UnionTypes.rs"));
    }
}
<commit_msg>Enable some warnings for generated code.<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 code to expose the DOM to JavaScript through IDL bindings.
//!
//! Exposing a DOM object to JavaScript
//! ===================================
//!
//! As [explained earlier](../index.html#a-dom-object-and-its-reflector), the
//! implementation of an interface `Foo` involves two objects: the DOM object
//! (implemented in Rust) and the reflector (a `JSObject`).
//!
//! In order to expose the interface's members to the web, properties
//! corresponding to the operations and attributes are defined on an object in
//! the reflector's prototype chain or on the reflector itself.
//!
//! Typically, these properties are either value properties whose value is a
//! function (for operations) or accessor properties that have a getter and
//! optionally a setter function (for attributes, depending on whether they are
//! marked `readonly`).
//!
//! All these JavaScript functions are set up such that, when they're called,
//! they call a Rust function in the generated glue code. This glue code does
//! some sanity checks and [argument conversions](conversions/index.html), and
//! calls into API implementation for the DOM object.
//!
//! Rust reflections of WebIDL constructs
//! =====================================
//!
//! WebIDL members are turned into methods on the DOM object (static methods
//! for a static members and instance methods for regular members).
//!
//! The instance methods for an interface `Foo` are defined on a
//! `dom::bindings::codegen::Bindings::FooBindings::FooMethods` trait. This
//! trait is then implemented for `JSRef<'a, Foo>`.
//!
//! The return type and argument types are determined [as described below]
//! (#rust-reflections-of-webidl-types).
//! In addition to those, all methods that are [allowed to throw]
//! (#throwing-exceptions) will have the return value wrapped in
//! [`Fallible<T>`](error/type.Fallible.html).
//! Methods that use certain WebIDL types like `any` or `object` will get a
//! `*mut JSContext` argument prepended to the argument list. Static methods
//! will be passed a [`GlobalRef`](global/enum.GlobalRef.html) for the relevant
//! global. This argument comes before the `*mut JSContext` argument, if any.
//!
//! Rust reflections of WebIDL operations (methods)
//! -----------------------------------------------
//!
//! A WebIDL operation is turned into one method for every overload.
//! The first overload gets the base name, and consecutive overloads have an
//! underscore appended to the name.
//!
//! The base name of the Rust method is simply the name of the WebIDL operation
//! with the first letter converted to uppercase.
//!
//! Rust reflections of WebIDL attributes
//! -------------------------------------
//!
//! A WebIDL attribute is turned into a pair of methods: one for the getter and
//! one for the setter. A readonly attribute only has a getter and no setter.
//!
//! The getter's name is the name of the attribute with the first letter
//! converted to uppercase. It has `Get` prepended to it if the type of the
//! attribute is nullable or if the getter can throw.
//!
//! The method signature for the getter looks just like an operation with no
//! arguments and the attribute's type as the return type.
//!
//! The setter's name is `Set` followed by the name of the attribute with the
//! first letter converted to uppercase. The method signature looks just like
//! an operation with a void return value and a single argument whose type is
//! the attribute's type.
//!
//! Rust reflections of WebIDL constructors
//! ---------------------------------------
//!
//! A WebIDL constructor is turned into a static class method named
//! `Constructor`. The arguments of this method will be the arguments of the
//! WebIDL constructor, with a `GlobalRef` for the relevant global prepended.
//! The return value of the constructor for MyInterface is exactly the same as
//! that of a method returning an instance of MyInterface. Constructors are
//! always [allowed to throw](#throwing-exceptions).
//!
//! Rust reflections of WebIDL types
//! --------------------------------
//!
//! The exact Rust representation for WebIDL types can depend on the precise
//! way that they're being used (e.g., return values and arguments might have
//! different representations).
//!
//! Optional arguments which do not have a default value are represented by
//! wrapping `Option<T>` around the representation of the argument type.
//! Optional arguments which do have a default value are represented by the
//! argument type itself, set to the default value if the argument was not in
//! fact passed in.
//!
//! Variadic WebIDL arguments are represented by wrapping a `Vec<T>` around the
//! representation of the argument type.
//!
//! See [the type mapping for particular types](conversions/index.html).
//!
//! Rust reflections of stringifiers
//! --------------------------------
//!
//! *To be written.*
//!
//! Rust reflections of legacy callers
//! ---------------------------------
//!
//! Legacy callers are not yet implemented.
//!
//! Rust reflections of getters and setters
//! ---------------------------------------
//!
//! *To be written.*
//!
//! Throwing exceptions
//! ===================
//!
//! WebIDL methods, getters, and setters that need to throw exceptions need to
//! be explicitly marked as such with the `[Throws]`, `[GetterThrows]` and
//! `[SetterThrows]` custom attributes.
//!
//! `[Throws]` applies to both methods and attributes; for attributes it means
//! both the getter and the setter (if any) can throw. `[GetterThrows]` applies
//! only to attributes. `[SetterThrows]` applies only to writable attributes.
//!
//! The corresponding Rust methods will have the return value wrapped in
//! [`Fallible<T>`](error/type.Fallible.html). To throw an exception, simply
//! return `Err()` from the method with the appropriate [error value]
//! (error/enum.Error.html).

pub mod callback;
pub mod cell;
pub mod conversions;
pub mod error;
pub mod global;
pub mod js;
pub mod num;
pub mod proxyhandler;
pub mod refcounted;
pub mod str;
pub mod structuredclone;
pub mod trace;
pub mod utils;

/// Generated JS-Rust bindings.
#[allow(missing_docs, non_snake_case)]
pub mod codegen {
    // FIXME(#5853) we shouldn't need to
    // allow moved_no_move here
    #[allow(unrooted_must_root, moved_no_move)]
    pub mod Bindings {
        include!(concat!(env!("OUT_DIR"), "/Bindings/mod.rs"));
    }
    pub mod InterfaceTypes {
        include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
    }
    #[allow(unused_imports)]
    pub mod InheritTypes {
        include!(concat!(env!("OUT_DIR"), "/InheritTypes.rs"));
    }
    pub mod PrototypeList {
        include!(concat!(env!("OUT_DIR"), "/PrototypeList.rs"));
    }
    pub mod RegisterBindings {
        include!(concat!(env!("OUT_DIR"), "/RegisterBindings.rs"));
    }
    #[allow(non_camel_case_types, unused_imports, unused_variables)]
    pub mod UnionTypes {
        include!(concat!(env!("OUT_DIR"), "/UnionTypes.rs"));
    }
}
<|endoftext|>


<commit_before>//! A small TCP server to handle collection of diagnostics information in a
//! cross-platform way for the `cargo fix` command.

use std::collections::HashSet;
use std::env;
use std::io::{BufReader, Read, Write};
use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::{self, JoinHandle};

use failure::{Error, ResultExt};
use log::warn;
use serde::{Deserialize, Serialize};

use crate::util::errors::CargoResult;
use crate::util::{Config, ProcessBuilder};

const DIAGNOSICS_SERVER_VAR: &str = "__CARGO_FIX_DIAGNOSTICS_SERVER";
const PLEASE_REPORT_THIS_BUG: &str =
    "\
     This likely indicates a bug in either rustc or cargo itself,\n\
     and we would appreciate a bug report! You're likely to see \n\
     a number of compiler warnings after this message which cargo\n\
     attempted to fix but failed. If you could open an issue at\n\
     https://github.com/rust-lang/cargo/issues\n\
     quoting the full output of this command we'd be very appreciative!\n\n\
     ";

#[derive(Deserialize, Serialize)]
pub enum Message {
    Fixing {
        file: String,
        fixes: u32,
    },
    FixFailed {
        files: Vec<String>,
        krate: Option<String>,
        errors: Vec<String>,
    },
    ReplaceFailed {
        file: String,
        message: String,
    },
    EditionAlreadyEnabled {
        file: String,
        edition: String,
    },
    IdiomEditionMismatch {
        file: String,
        idioms: String,
        edition: Option<String>,
    },
}

impl Message {
    pub fn post(&self) -> Result<(), Error> {
        let addr =
            env::var(DIAGNOSICS_SERVER_VAR).context("diagnostics collector misconfigured")?;
        let mut client =
            TcpStream::connect(&addr).context("failed to connect to parent diagnostics target")?;

        let s = serde_json::to_string(self).context("failed to serialize message")?;
        client
            .write_all(s.as_bytes())
            .context("failed to write message to diagnostics target")?;
        client
            .shutdown(Shutdown::Write)
            .context("failed to shutdown")?;

        let mut tmp = Vec::new();
        client
            .read_to_end(&mut tmp)
            .context("failed to receive a disconnect")?;

        Ok(())
    }
}

pub struct DiagnosticPrinter<'a> {
    config: &'a Config,
    edition_already_enabled: HashSet<String>,
    idiom_mismatch: HashSet<String>,
}

impl<'a> DiagnosticPrinter<'a> {
    pub fn new(config: &'a Config) -> DiagnosticPrinter<'a> {
        DiagnosticPrinter {
            config,
            edition_already_enabled: HashSet::new(),
            idiom_mismatch: HashSet::new(),
        }
    }

    pub fn print(&mut self, msg: &Message) -> CargoResult<()> {
        match msg {
            Message::Fixing { file, fixes } => {
                let msg = if *fixes == 1 { "fix" } else { "fixes" };
                let msg = format!("{} ({} {})", file, fixes, msg);
                self.config.shell().status("Fixing", msg)
            }
            Message::ReplaceFailed { file, message } => {
                let msg = format!("error applying suggestions to `{}`\n", file);
                self.config.shell().warn(&msg)?;
                write!(
                    self.config.shell().err(),
                    "The full error message was:\n\n> {}\n\n",
                    message,
                )?;
                write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
                Ok(())
            }
            Message::FixFailed {
                files,
                krate,
                errors,
            } => {
                if let Some(ref krate) = *krate {
                    self.config.shell().warn(&format!(
                        "failed to automatically apply fixes suggested by rustc \
                         to crate `{}`",
                        krate,
                    ))?;
                } else {
                    self.config
                        .shell()
                        .warn("failed to automatically apply fixes suggested by rustc")?;
                }
                if !files.is_empty() {
                    writeln!(
                        self.config.shell().err(),
                        "\nafter fixes were automatically applied the compiler \
                         reported errors within these files:\n"
                    )?;
                    for file in files {
                        writeln!(self.config.shell().err(), "  * {}", file)?;
                    }
                    writeln!(self.config.shell().err())?;
                }
                write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
                if !errors.is_empty() {
                    writeln!(
                        self.config.shell().err(),
                        "The following errors were reported:"
                    )?;
                    for error in errors {
                        write!(self.config.shell().err(), "{}", error)?;
                        if !error.ends_with('\n') {
                            writeln!(self.config.shell().err())?;
                        }
                    }
                }
                writeln!(
                    self.config.shell().err(),
                    "Original diagnostics will follow.\n"
                )?;
                Ok(())
            }
            Message::EditionAlreadyEnabled { file, edition } => {
                // Like above, only warn once per file
                if !self.edition_already_enabled.insert(file.clone()) {
                    return Ok(());
                }

                let msg = format!(
                    "\
cannot prepare for the {} edition when it is enabled, so cargo cannot
automatically fix errors in `{}`

To prepare for the {0} edition you should first remove `edition = '{0}'` from
your `Cargo.toml` and then rerun this command. Once all warnings have been fixed
then you can re-enable the `edition` key in `Cargo.toml`. For some more
information about transitioning to the {0} edition see:

  https://rust-lang-nursery.github.io/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html
",
                    edition,
                    file,
                );
                self.config.shell().error(&msg)?;
                Ok(())
            }
            Message::IdiomEditionMismatch {
                file,
                idioms,
                edition,
            } => {
                // Same as above
                if !self.idiom_mismatch.insert(file.clone()) {
                    return Ok(());
                }
                self.config.shell().error(&format!(
                    "\
cannot migrate to the idioms of the {} edition for `{}`
because it is compiled {}, which doesn't match {0}

consider migrating to the {0} edition by adding `edition = '{0}'` to
`Cargo.toml` and then rerunning this command; a more detailed transition
guide can be found at

  https://rust-lang-nursery.github.io/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html
",
                    idioms,
                    file,
                    match edition {
                        Some(s) => format!("with the {} edition", s),
                        None => "without an edition".to_string(),
                    },
                ))?;
                Ok(())
            }
        }
    }
}

#[derive(Debug)]
pub struct RustfixDiagnosticServer {
    listener: TcpListener,
    addr: SocketAddr,
}

pub struct StartedServer {
    addr: SocketAddr,
    done: Arc<AtomicBool>,
    thread: Option<JoinHandle<()>>,
}

impl RustfixDiagnosticServer {
    pub fn new() -> Result<Self, Error> {
        let listener = TcpListener::bind("127.0.0.1:0")
            .with_context(|_| "failed to bind TCP listener to manage locking")?;
        let addr = listener.local_addr()?;

        Ok(RustfixDiagnosticServer { listener, addr })
    }

    pub fn configure(&self, process: &mut ProcessBuilder) {
        process.env(DIAGNOSICS_SERVER_VAR, self.addr.to_string());
    }

    pub fn start<F>(self, on_message: F) -> Result<StartedServer, Error>
    where
        F: Fn(Message) + Send + 'static,
    {
        let addr = self.addr;
        let done = Arc::new(AtomicBool::new(false));
        let done2 = done.clone();
        let thread = thread::spawn(move || {
            self.run(&on_message, &done2);
        });

        Ok(StartedServer {
            addr,
            thread: Some(thread),
            done,
        })
    }

    fn run(self, on_message: &dyn Fn(Message), done: &AtomicBool) {
        while let Ok((client, _)) = self.listener.accept() {
            let client = BufReader::new(client);
            match serde_json::from_reader(client) {
                Ok(message) => on_message(message),
                Err(e) => warn!("invalid diagnostics message: {}", e),
            }
            if done.load(Ordering::SeqCst) {
                break;
            }
        }
    }
}

impl Drop for StartedServer {
    fn drop(&mut self) {
        self.done.store(true, Ordering::SeqCst);
        // Ignore errors here as this is largely best-effort
        if TcpStream::connect(&self.addr).is_err() {
            return;
        }
        drop(self.thread.take().unwrap().join());
    }
}
<commit_msg>Auto merge of #6515 - ehuss:fix-fix-diag-race, r=alexcrichton<commit_after>//! A small TCP server to handle collection of diagnostics information in a
//! cross-platform way for the `cargo fix` command.

use std::collections::HashSet;
use std::env;
use std::io::{BufReader, Read, Write};
use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::{self, JoinHandle};

use failure::{Error, ResultExt};
use log::warn;
use serde::{Deserialize, Serialize};

use crate::util::errors::CargoResult;
use crate::util::{Config, ProcessBuilder};

const DIAGNOSICS_SERVER_VAR: &str = "__CARGO_FIX_DIAGNOSTICS_SERVER";
const PLEASE_REPORT_THIS_BUG: &str =
    "\
     This likely indicates a bug in either rustc or cargo itself,\n\
     and we would appreciate a bug report! You're likely to see \n\
     a number of compiler warnings after this message which cargo\n\
     attempted to fix but failed. If you could open an issue at\n\
     https://github.com/rust-lang/cargo/issues\n\
     quoting the full output of this command we'd be very appreciative!\n\n\
     ";

#[derive(Deserialize, Serialize)]
pub enum Message {
    Fixing {
        file: String,
        fixes: u32,
    },
    FixFailed {
        files: Vec<String>,
        krate: Option<String>,
        errors: Vec<String>,
    },
    ReplaceFailed {
        file: String,
        message: String,
    },
    EditionAlreadyEnabled {
        file: String,
        edition: String,
    },
    IdiomEditionMismatch {
        file: String,
        idioms: String,
        edition: Option<String>,
    },
}

impl Message {
    pub fn post(&self) -> Result<(), Error> {
        let addr =
            env::var(DIAGNOSICS_SERVER_VAR).context("diagnostics collector misconfigured")?;
        let mut client =
            TcpStream::connect(&addr).context("failed to connect to parent diagnostics target")?;

        let s = serde_json::to_string(self).context("failed to serialize message")?;
        client
            .write_all(s.as_bytes())
            .context("failed to write message to diagnostics target")?;
        client
            .shutdown(Shutdown::Write)
            .context("failed to shutdown")?;

        let mut tmp = Vec::new();
        client
            .read_to_end(&mut tmp)
            .context("failed to receive a disconnect")?;

        Ok(())
    }
}

pub struct DiagnosticPrinter<'a> {
    config: &'a Config,
    edition_already_enabled: HashSet<String>,
    idiom_mismatch: HashSet<String>,
}

impl<'a> DiagnosticPrinter<'a> {
    pub fn new(config: &'a Config) -> DiagnosticPrinter<'a> {
        DiagnosticPrinter {
            config,
            edition_already_enabled: HashSet::new(),
            idiom_mismatch: HashSet::new(),
        }
    }

    pub fn print(&mut self, msg: &Message) -> CargoResult<()> {
        match msg {
            Message::Fixing { file, fixes } => {
                let msg = if *fixes == 1 { "fix" } else { "fixes" };
                let msg = format!("{} ({} {})", file, fixes, msg);
                self.config.shell().status("Fixing", msg)
            }
            Message::ReplaceFailed { file, message } => {
                let msg = format!("error applying suggestions to `{}`\n", file);
                self.config.shell().warn(&msg)?;
                write!(
                    self.config.shell().err(),
                    "The full error message was:\n\n> {}\n\n",
                    message,
                )?;
                write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
                Ok(())
            }
            Message::FixFailed {
                files,
                krate,
                errors,
            } => {
                if let Some(ref krate) = *krate {
                    self.config.shell().warn(&format!(
                        "failed to automatically apply fixes suggested by rustc \
                         to crate `{}`",
                        krate,
                    ))?;
                } else {
                    self.config
                        .shell()
                        .warn("failed to automatically apply fixes suggested by rustc")?;
                }
                if !files.is_empty() {
                    writeln!(
                        self.config.shell().err(),
                        "\nafter fixes were automatically applied the compiler \
                         reported errors within these files:\n"
                    )?;
                    for file in files {
                        writeln!(self.config.shell().err(), "  * {}", file)?;
                    }
                    writeln!(self.config.shell().err())?;
                }
                write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
                if !errors.is_empty() {
                    writeln!(
                        self.config.shell().err(),
                        "The following errors were reported:"
                    )?;
                    for error in errors {
                        write!(self.config.shell().err(), "{}", error)?;
                        if !error.ends_with('\n') {
                            writeln!(self.config.shell().err())?;
                        }
                    }
                }
                writeln!(
                    self.config.shell().err(),
                    "Original diagnostics will follow.\n"
                )?;
                Ok(())
            }
            Message::EditionAlreadyEnabled { file, edition } => {
                // Like above, only warn once per file
                if !self.edition_already_enabled.insert(file.clone()) {
                    return Ok(());
                }

                let msg = format!(
                    "\
cannot prepare for the {} edition when it is enabled, so cargo cannot
automatically fix errors in `{}`

To prepare for the {0} edition you should first remove `edition = '{0}'` from
your `Cargo.toml` and then rerun this command. Once all warnings have been fixed
then you can re-enable the `edition` key in `Cargo.toml`. For some more
information about transitioning to the {0} edition see:

  https://rust-lang-nursery.github.io/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html
",
                    edition,
                    file,
                );
                self.config.shell().error(&msg)?;
                Ok(())
            }
            Message::IdiomEditionMismatch {
                file,
                idioms,
                edition,
            } => {
                // Same as above
                if !self.idiom_mismatch.insert(file.clone()) {
                    return Ok(());
                }
                self.config.shell().error(&format!(
                    "\
cannot migrate to the idioms of the {} edition for `{}`
because it is compiled {}, which doesn't match {0}

consider migrating to the {0} edition by adding `edition = '{0}'` to
`Cargo.toml` and then rerunning this command; a more detailed transition
guide can be found at

  https://rust-lang-nursery.github.io/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html
",
                    idioms,
                    file,
                    match edition {
                        Some(s) => format!("with the {} edition", s),
                        None => "without an edition".to_string(),
                    },
                ))?;
                Ok(())
            }
        }
    }
}

#[derive(Debug)]
pub struct RustfixDiagnosticServer {
    listener: TcpListener,
    addr: SocketAddr,
}

pub struct StartedServer {
    addr: SocketAddr,
    done: Arc<AtomicBool>,
    thread: Option<JoinHandle<()>>,
}

impl RustfixDiagnosticServer {
    pub fn new() -> Result<Self, Error> {
        let listener = TcpListener::bind("127.0.0.1:0")
            .with_context(|_| "failed to bind TCP listener to manage locking")?;
        let addr = listener.local_addr()?;

        Ok(RustfixDiagnosticServer { listener, addr })
    }

    pub fn configure(&self, process: &mut ProcessBuilder) {
        process.env(DIAGNOSICS_SERVER_VAR, self.addr.to_string());
    }

    pub fn start<F>(self, on_message: F) -> Result<StartedServer, Error>
    where
        F: Fn(Message) + Send + 'static,
    {
        let addr = self.addr;
        let done = Arc::new(AtomicBool::new(false));
        let done2 = done.clone();
        let thread = thread::spawn(move || {
            self.run(&on_message, &done2);
        });

        Ok(StartedServer {
            addr,
            thread: Some(thread),
            done,
        })
    }

    fn run(self, on_message: &dyn Fn(Message), done: &AtomicBool) {
        while let Ok((client, _)) = self.listener.accept() {
            if done.load(Ordering::SeqCst) {
                break;
            }
            let mut client = BufReader::new(client);
            let mut s = String::new();
            if let Err(e) = client.read_to_string(&mut s) {
                warn!("diagnostic server failed to read: {}", e);
            } else {
                match serde_json::from_str(&s) {
                    Ok(message) => on_message(message),
                    Err(e) => warn!("invalid diagnostics message: {}", e),
                }
            }
            // The client should be kept alive until after `on_message` is
            // called to ensure that the client doesn't exit too soon (and
            // Message::Finish getting posted before Message::FixDiagnostic).
            drop(client);
        }
    }
}

impl Drop for StartedServer {
    fn drop(&mut self) {
        self.done.store(true, Ordering::SeqCst);
        // Ignore errors here as this is largely best-effort
        if TcpStream::connect(&self.addr).is_err() {
            return;
        }
        drop(self.thread.take().unwrap().join());
    }
}
<|endoftext|>


<commit_before>//! Paths and Unix shells
//!
//! MacOS, Linux, FreeBSD, and many other OS model their design on Unix,
//! so handling them is relatively consistent. But only relatively.
//! POSIX postdates Unix by 20 years, and each "Unix-like" shell develops
//! unique quirks over time.
//!
//!
//! Windowing Managers, Desktop Environments, GUI Terminals, and PATHs
//!
//! Duplicating paths in PATH can cause performance issues when the OS searches
//! the same place multiple times. Traditionally, Unix configurations have
//! resolved this by setting up PATHs in the shell's login profile.
//!
//! This has its own issues. Login profiles are only intended to run once, but
//! changing the PATH is common enough that people may run it twice. Desktop
//! environments often choose to NOT start login shells in GUI terminals. Thus,
//! a trend has emerged to place PATH updates in other run-commands (rc) files,
//! leaving Rustup with few assumptions to build on for fulfilling its promise
//! to set up PATH appropriately.
//!
//! Rustup addresses this by:
//! 1) using a shell script that updates PATH if the path is not in PATH
//! 2) sourcing this script in any known and appropriate rc file

use super::canonical_cargo_home;
use super::*;
use crate::process;
use error_chain::bail;
use std::path::PathBuf;

pub type Shell = Box<dyn UnixShell>;

#[derive(Debug, PartialEq)]
pub struct ShellScript {
    content: &'static str,
    name: &'static str,
}

impl ShellScript {
    pub fn write(&self) -> Result<()> {
        let home = utils::cargo_home()?;
        let cargo_bin = match home.to_str() {
            Some(s) => format!("{}/bin", s),
            None => bail!("Non-Unicode path found."),
        };
        let env_name = home.join(self.name);
        let env_file = self.content.replace("{cargo_bin}", &cargo_bin);
        utils::write_file(self.name, &env_name, &env_file)?;
        Ok(())
    }
}

#[allow(dead_code)] // For some reason.
const POSIX_ENV: &str = include_str!("env");

macro_rules! support_shells {
    ( $($shell:ident,)* ) => {
        fn enumerate_shells() -> Vec<Shell> {
            vec![$( Box::new($shell), )*]
        }
    }
}

// TODO: Tcsh (BSD)
// TODO?: Make a decision on Ion Shell, Power Shell, Nushell
// Cross-platform non-POSIX shells have not been assessed for integration yet
support_shells! {
    Posix,
    Bash,
    Zsh,
}

pub fn get_available_shells() -> impl Iterator<Item = Shell> {
    enumerate_shells().into_iter().filter(|sh| sh.does_exist())
}

pub trait UnixShell {
    // Detects if a shell "exists". Users have multiple shells, so an "eager"
    // heuristic should be used, assuming shells exist if any traces do.
    fn does_exist(&self) -> bool;

    // Gives all rcfiles of a given shell that rustup is concerned with.
    // Used primarily in checking rcfiles for cleanup.
    fn rcfiles(&self) -> Vec<PathBuf>;

    // Gives rcs that should be written to.
    fn update_rcs(&self) -> Vec<PathBuf>;

    // Writes the relevant env file.
    fn env_script(&self) -> ShellScript {
        ShellScript {
            name: "env",
            content: POSIX_ENV,
        }
    }

    fn source_string(&self) -> Result<String> {
        Ok(format!(r#"source "{}/env""#, canonical_cargo_home()?))
    }
}

struct Posix;
impl UnixShell for Posix {
    fn does_exist(&self) -> bool {
        true
    }

    fn rcfiles(&self) -> Vec<PathBuf> {
        match utils::home_dir() {
            Some(dir) => vec![dir.join(".profile")],
            _ => vec![],
        }
    }

    fn update_rcs(&self) -> Vec<PathBuf> {
        // Write to .profile even if it doesn't exist.
        self.rcfiles()
    }
}

struct Bash;

impl Bash {
    // Bash will load only one of these, the first one that exists, when
    // a login shell starts. BUT not all shells start inside a login shell!
    fn profiles() -> Vec<PathBuf> {
        [".bash_profile", ".bash_login", ".profile"]
            .iter()
            .filter_map(|rc| utils::home_dir().map(|dir| dir.join(rc)))
            .collect()
    }

    // Bash will load only .bashrc on the start of most GUI terminals.
    fn rc() -> Option<PathBuf> {
        utils::home_dir().map(|dir| dir.join(".bashrc"))
    }
}

impl UnixShell for Bash {
    fn does_exist(&self) -> bool {
        // Checking to see if we can find any traces of Bash in $SHELL
        // or a file or binary on disk.
        matches!(process().var("SHELL"), Ok(sh) if sh.contains("bash"))
            || self.rcfiles().iter().any(|rc| rc.is_file())
            || matches!(utils::find_cmd(&["bash"]), Some(_))
    }

    fn rcfiles(&self) -> Vec<PathBuf> {
        let mut profiles = Bash::profiles();
        if let Some(rc) = Bash::rc() {
            profiles.push(rc);
        }
        profiles
    }

    fn update_rcs(&self) -> Vec<PathBuf> {
        Bash::profiles()
            .into_iter()
            .filter(|rc| rc.is_file())
            // bash only reads one "login profile" so pick the one that exists
            .take(1)
            // Pick .bashrc if it exists for GUI terminals
            .chain(Bash::rc().filter(|rc| rc.is_file()))
            .collect()
    }
}

struct Zsh;
impl UnixShell for Zsh {
    fn does_exist(&self) -> bool {
        // Checking to see if we can find any traces of zsh, either in env vars,
        // an rc of concern, or the binary.
        matches!(process().var("SHELL"), Ok(sh) if sh.contains("zsh"))
            || matches!(process().var("ZDOTDIR"), Ok(dir) if dir.len() > 0)
            || self.rcfiles().iter().any(|rc| rc.is_file())
            || matches!(utils::find_cmd(&["zsh"]), Some(_))
    }

    fn rcfiles(&self) -> Vec<PathBuf> {
        // FIXME: if zsh exists but is not in the process tree of the shell
        // on install, $ZDOTDIR may not be loaded and give the wrong result.
        let zdotdir = match process().var("ZDOTDIR") {
            Ok(dir) => Some(PathBuf::from(dir)),
            _ => utils::home_dir(),
        };

        // .zshenv is always sourced
        match zdotdir.map(|dir| dir.join(".zshenv")) {
            Some(zshenv) => vec![zshenv],
            _ => vec![],
        }
    }

    fn update_rcs(&self) -> Vec<PathBuf> {
        // .zshenv is preferred for path mods, always write it.
        self.rcfiles()
    }
}
<commit_msg>Simplify Bash logic<commit_after>//! Paths and Unix shells
//!
//! MacOS, Linux, FreeBSD, and many other OS model their design on Unix,
//! so handling them is relatively consistent. But only relatively.
//! POSIX postdates Unix by 20 years, and each "Unix-like" shell develops
//! unique quirks over time.
//!
//!
//! Windowing Managers, Desktop Environments, GUI Terminals, and PATHs
//!
//! Duplicating paths in PATH can cause performance issues when the OS searches
//! the same place multiple times. Traditionally, Unix configurations have
//! resolved this by setting up PATHs in the shell's login profile.
//!
//! This has its own issues. Login profiles are only intended to run once, but
//! changing the PATH is common enough that people may run it twice. Desktop
//! environments often choose to NOT start login shells in GUI terminals. Thus,
//! a trend has emerged to place PATH updates in other run-commands (rc) files,
//! leaving Rustup with few assumptions to build on for fulfilling its promise
//! to set up PATH appropriately.
//!
//! Rustup addresses this by:
//! 1) using a shell script that updates PATH if the path is not in PATH
//! 2) sourcing this script in any known and appropriate rc file

use super::canonical_cargo_home;
use super::*;
use crate::process;
use error_chain::bail;
use std::path::PathBuf;

pub type Shell = Box<dyn UnixShell>;

#[derive(Debug, PartialEq)]
pub struct ShellScript {
    content: &'static str,
    name: &'static str,
}

impl ShellScript {
    pub fn write(&self) -> Result<()> {
        let home = utils::cargo_home()?;
        let cargo_bin = match home.to_str() {
            Some(s) => format!("{}/bin", s),
            None => bail!("Non-Unicode path found."),
        };
        let env_name = home.join(self.name);
        let env_file = self.content.replace("{cargo_bin}", &cargo_bin);
        utils::write_file(self.name, &env_name, &env_file)?;
        Ok(())
    }
}

#[allow(dead_code)] // For some reason.
const POSIX_ENV: &str = include_str!("env");

macro_rules! support_shells {
    ( $($shell:ident,)* ) => {
        fn enumerate_shells() -> Vec<Shell> {
            vec![$( Box::new($shell), )*]
        }
    }
}

// TODO: Tcsh (BSD)
// TODO?: Make a decision on Ion Shell, Power Shell, Nushell
// Cross-platform non-POSIX shells have not been assessed for integration yet
support_shells! {
    Posix,
    Bash,
    Zsh,
}

pub fn get_available_shells() -> impl Iterator<Item = Shell> {
    enumerate_shells().into_iter().filter(|sh| sh.does_exist())
}

pub trait UnixShell {
    // Detects if a shell "exists". Users have multiple shells, so an "eager"
    // heuristic should be used, assuming shells exist if any traces do.
    fn does_exist(&self) -> bool;

    // Gives all rcfiles of a given shell that rustup is concerned with.
    // Used primarily in checking rcfiles for cleanup.
    fn rcfiles(&self) -> Vec<PathBuf>;

    // Gives rcs that should be written to.
    fn update_rcs(&self) -> Vec<PathBuf>;

    // Writes the relevant env file.
    fn env_script(&self) -> ShellScript {
        ShellScript {
            name: "env",
            content: POSIX_ENV,
        }
    }

    fn source_string(&self) -> Result<String> {
        Ok(format!(r#"source "{}/env""#, canonical_cargo_home()?))
    }
}

struct Posix;
impl UnixShell for Posix {
    fn does_exist(&self) -> bool {
        true
    }

    fn rcfiles(&self) -> Vec<PathBuf> {
        match utils::home_dir() {
            Some(dir) => vec![dir.join(".profile")],
            _ => vec![],
        }
    }

    fn update_rcs(&self) -> Vec<PathBuf> {
        // Write to .profile even if it doesn't exist.
        self.rcfiles()
    }
}

struct Bash;

impl Bash {
    fn profiles() -> impl Iterator<Item = PathBuf> {
        [".bash_profile", ".bash_login", ".profile", ".bashrc"]
            .iter()
            .filter_map(|rc| utils::home_dir().map(|dir| dir.join(rc)))
    }
}

impl UnixShell for Bash {
    fn does_exist(&self) -> bool {
        // Checking to see if we can find any traces of Bash in $SHELL
        // or a file or binary on disk.
        matches!(process().var("SHELL"), Ok(sh) if sh.contains("bash"))
            || self.rcfiles().iter().any(|rc| rc.is_file())
            || matches!(utils::find_cmd(&["bash"]), Some(_))
    }

    fn rcfiles(&self) -> Vec<PathBuf> {
        Bash::profiles().collect()
    }

    fn update_rcs(&self) -> Vec<PathBuf> {
        Bash::profiles().filter(|rc| rc.is_file()).collect()
    }
}

struct Zsh;
impl UnixShell for Zsh {
    fn does_exist(&self) -> bool {
        // Checking to see if we can find any traces of zsh, either in env vars,
        // an rc of concern, or the binary.
        matches!(process().var("SHELL"), Ok(sh) if sh.contains("zsh"))
            || matches!(process().var("ZDOTDIR"), Ok(dir) if dir.len() > 0)
            || self.rcfiles().iter().any(|rc| rc.is_file())
            || matches!(utils::find_cmd(&["zsh"]), Some(_))
    }

    fn rcfiles(&self) -> Vec<PathBuf> {
        // FIXME: if zsh exists but is not in the process tree of the shell
        // on install, $ZDOTDIR may not be loaded and give the wrong result.
        let zdotdir = match process().var("ZDOTDIR") {
            Ok(dir) => Some(PathBuf::from(dir)),
            _ => utils::home_dir(),
        };

        // .zshenv is always sourced
        match zdotdir.map(|dir| dir.join(".zshenv")) {
            Some(zshenv) => vec![zshenv],
            _ => vec![],
        }
    }

    fn update_rcs(&self) -> Vec<PathBuf> {
        // .zshenv is preferred for path mods, always write it.
        self.rcfiles()
    }
}
<|endoftext|>


<commit_before>// =======================================================================
// LIBRARY IMPORTS
// =======================================================================
use rocket;
use rocket::{ Request, Data };
use rocket::data::{ FromData, Outcome };
use rocket::http::Status;
use rocket::outcome::IntoOutcome;
use rocket::response::Responder;

use serde;
use ::serde::de::Deserialize;
use serde::de::DeserializeOwned;
use serde_json;
use serde_json::Value;

use util::ContainsKeys;

// =======================================================================
// STRUCT & TRAIT DEFINITION
// =======================================================================
/// JSON wrapper for a JSON response from a REST route
/// It wraps an optional generic type `T` that just needs to implement [serde's Deserialize](https://docs.serde.rs/serde/de/trait.Deserializer.html)
///
/// It derives Rocket's [Responder trait](https://api.rocket.rs/rocket/response/trait.Responder.html) so it can be used as such in a Rocket's route as illustrated below
///
/// ```rust,ignore
/// #[get("/")]
/// fn index() -> ResponseJSON<T> { ... }
/// ```
pub struct ResponseJSON<T: DeserializeOwned> {
    success: bool,
    http_code: u16,
    data: Option<T>,
    message: Option<String>, // required for error JSON
    resource: Option<String>,
    method: Option<String>,
}

/// Test if the underlying structure is a valid ResponseJSON
pub trait IsResponseJSON {
    fn is_valid_json(&self) -> bool;
    fn is_ok_json(&self) -> bool;
    fn is_error_json(&self) -> bool;
}

// =======================================================================
// STRUCT IMPLEMENTATION
// =======================================================================
/// Default values for ResponseJSON are
///     - data: None
///     - message: None
///     - resource: None
///     - method: None
impl<T> Default for ResponseJSON<T>
    where T: DeserializeOwned
{
    fn default () -> ResponseJSON<T> {
        ResponseJSON {
            success: true,
            http_code: 200,
            data: None,
            message: None,
            resource: None,
            method: None,
        }
    }
}

impl<T: DeserializeOwned> ResponseJSON<T> {
    // Create an empty OK ResponseJSON
    pub fn ok() -> ResponseJSON<T> {
        ResponseJSON{
            success: true,
            http_code: 200,
            ..Default::default()
        }
    }

    // Create an empty OK ResponseJSON
    pub fn error() -> ResponseJSON<T> {
        ResponseJSON{
            success: false,
            http_code: 500,
            message: Some("Unexpected error".to_string()),
            ..Default::default()
        }
    }

    /// Finalize ResponseJSON and transfer ownership to caller
    pub fn finalize(self) -> ResponseJSON<T> {
        self
    }

    /// Set the HTTP Code of this ResponseJSON
    pub fn http_code(&mut self, code: u16) -> &mut ResponseJSON<T> {
        self.http_code = code;
        self
    }

    /// Set the data of this ResponseJSON
    pub fn data(&mut self, data: T) -> &mut ResponseJSON<T> {
        self.data = Some(data);
        self
    }

    /// Set the error message.
    /// For Error JSON only (does nothing if `success == ok`)
    pub fn message(&mut self, string: String) -> &mut ResponseJSON<T> {
        if !self.success {
            self.message = Some(string);
        } else {
            warn!("::AMIWO::RESPONSEJSON::MESSAGE::WARNING Trying to set `message` on an Ok JSON => ignored")
        }
        self
    }

    /// Set the resource that we tried to access.
    /// For Error JSON only (does nothing if `success == ok`)
    pub fn resource(&mut self, string: String) -> &mut ResponseJSON<T> {
        if !self.success {
            self.resource = Some(string);
        } else {
            warn!("::AMIWO::RESPONSEJSON::MESSAGE::WARNING Trying to set `resource` on an Ok JSON => ignored")
        }
        self
    }

    /// Set the method that was used (GET, POST, ...).
    /// For Error JSON only (does nothing if `success == ok`)
    pub fn method(&mut self, string: String) -> &mut ResponseJSON<T> {
        if !self.success {
            self.method = Some(string);
        } else {
            warn!("::AMIWO::RESPONSEJSON::METHOD::WARNING Trying to set `method` on an Ok JSON => ignored")
        }
        self
    }
}

impl<T> IsResponseJSON for ResponseJSON<T>
    where T: DeserializeOwned
{
    /// Check if the JSON described as a String is a valid ResponseJSON
    fn is_valid_json(&self) -> bool {
        true
    }

    /// Check if the JSON described as a String is an Error JSON
    fn is_error_json(&self) -> bool
    {
        !self.is_ok_json()
    }

    /// Check if the JSON described as a String is an OK JSON
    fn is_ok_json(&self) -> bool {
        self.success == true &&
        self.method == None &&
        self.message == None &&
        self.resource == None
    }
}

impl IsResponseJSON for serde_json::map::Map<String, Value> {
    fn is_valid_json(&self) -> bool {
        self.contains_keys(&["success", "http_code"])
    }

    fn is_ok_json(&self) -> bool {
        self.is_valid_json() &&
        self["success"] == Value::Bool(true) &&
        self.get("http_code").is_some() && self.get("http_code").unwrap().is_number() &&
        self["method"] == Value::Null &&
        self["resource"] == Value::Null &&
        self["message"] == Value::Null
    }

    fn is_error_json(&self) -> bool {
        !self.is_ok_json()
    }
}

/// ResponseJSON<T> can be created from a serde_json::JSON typed as `serde_json::Value`
/// If the input JSON is like { success: false, http_code, message, resource, method } it creates an Error ResponseJSON
/// Else it creates an Ok ResponseJSON with it's data property set to the input JSON
impl From<serde_json::Value> for ResponseJSON<serde_json::Value>
{
    fn from(json: serde_json::Value) -> Self {
        json.as_object() // Option<&Map<String, Value>>
            .map_or_else( // compute the data to be wrapped in the ResponseJSON
                // None => not an object
                || Err(&json),
                // Some(json) => check if is a valid ResponseJSON
                |obj| {
                    if obj.is_valid_json() {
                        Ok(
                            ResponseJSON::ok()
                                .http_code(obj["http_code"].as_u64().unwrap() as u16)
                                .data(obj["data"])
                                .finalize()
                        )
                    } else {
                        Err(&*obj as serde_json::Value)
                    }
                }
            ).unwrap_or_else(|data| ResponseJSON::ok().data(data).finalize() )
    }
}
/*
/// Implement Rocket's FormData to parse a ResponseJSON from incoming POST/... form data.
///
/// - If the content type of the request data is not
/// `application/json`, `Forward`s the request.
///
/// All relevant warnings and errors are written to the console
impl<T: DeserializeOwned> FromData for ResponseJSON<T> {
    type Error = serde_json::error::Error;

    fn from_data(request: &Request, data: Data) -> Outcome<Self, serde_json::error::Error> {
        if !request.content_type().map_or(false, |ct| ct.is_json()) {
            error!("Content-Type is not JSON.");
            return Outcome::Forward(data);
        }

        let size_limit = rocket::config::active()
            .and_then(|c| c.extras.get("limits.json")) // TODO: remove placeholder when upgrading to rocket version > 0.2.6
            // .and_then(|c| c.limits.get("json") // In next version
            .and_then(|limit| limit.as_integer())
            .unwrap_or(32768) as u64;

        serde_json::from_reader(data.open().take(size_limit))
            .map(|val| JSON(val))
            .map_err(|e| { error!("Couldn't parse JSON body: {:?}", e); e })
            .into_outcome(Status::BadRequest)

    }
}
*/

/*
/// Serializes the wrapped value into JSON. Returns a response with Content-Type
/// JSON and a fixed-size body with the serialized value. If serialization
/// fails, an `Err` of `Status::InternalServerError` is returned.
impl<T: Serialize> Responder<'static> for JSON<T> {
    fn respond(self) -> response::Result<'static> {
        serde_json::to_string(&self.0).map(|string| {
            content::JSON(string).respond().unwrap()
        }).map_err(|e| {
            error_!("JSON failed to serialize: {:?}", e);
            Status::InternalServerError
        })
    }
}
*/

// =======================================================================
// UNIT TESTS
// =======================================================================
#[cfg(test)]
mod tests {
    use super::ResponseJSON;

    #[test]
    fn test_is_ok_and_error_json() {
        let json = r#"{
            "success": false,
            "http_code": 500,
            "resource": "some resource requested",
            "method": "GET",
            "message": "error message"
        }"#;
        assert_eq!(ResponseJSON::<String>::is_error_json(json), true);
        assert_eq!(ResponseJSON::<String>::is_ok_json(json), false);

        let json = r#"{
            "success": true,
            "http_code": 200,
            "resource": "some resource requested",
            "method": "GET",
            "message": "error message"
        }"#;
        assert_eq!(ResponseJSON::<String>::is_error_json(json), true);
        assert_eq!(ResponseJSON::<String>::is_ok_json(json), false);
    }

    #[test]
    fn test_builder_ok() {
        let mut json = ResponseJSON::ok();
        assert_eq!(json.success, true);
        assert_eq!(json.http_code, 200);
        assert_eq!(json.data, None);
        assert_eq!(json.message, None);
        assert_eq!(json.method, None);
        assert_eq!(json.resource, None);

        json.http_code(201).data("Some data".to_string()).method("GET".to_string()).resource("some path".to_string()).message("error message".to_string());
        assert_eq!(json.http_code, 201);
        assert_eq!(json.data, Some("Some data".to_string()));
        assert_eq!(json.message, None);
        assert_eq!(json.method, None);
        assert_eq!(json.resource, None);

        assert_eq!(json.is_ok_json(), true);
        assert_eq!(json.is_error_json(), false);
    }

    #[test]
    fn test_builder_error() {
        let mut json = ResponseJSON::error();
        assert_eq!(json.success, false);
        assert_eq!(json.http_code, 500);
        assert_eq!(json.data, None);
        assert_eq!(json.message, Some("Unexpected error".to_string()));
        assert_eq!(json.method, None);
        assert_eq!(json.resource, None);

        json.http_code(401).data("Some data".to_string()).method("GET".to_string()).resource("some path".to_string()).message("error message".to_string());
        assert_eq!(json.http_code, 401);
        assert_eq!(json.data, Some("Some data".to_string()));
        assert_eq!(json.message, Some("error message".to_string()));
        assert_eq!(json.method, Some("GET".to_string()));
        assert_eq!(json.resource, Some("some path".to_string()));

        assert_eq!(json.is_ok_json(), false);
        assert_eq!(json.is_error_json(), true);
    }

    // TODO add test `from` Serde JSON
    // TODO add tests for Serde's implem of isResponseJSON

    // TODO add test with POST & GET routes taking a ResponseJSON as param
}   <commit_msg>ResponseJSON > initial commit (all tests pass)<commit_after>// =======================================================================
// LIBRARY IMPORTS
// =======================================================================
use rocket;
use rocket::{ Request, Data };
use rocket::data::{ FromData, Outcome };
use rocket::http::Status;
use rocket::outcome::IntoOutcome;
use rocket::response::Responder;
use std::marker::PhantomData;

use serde;
use ::serde::de::Deserialize;
use serde_json;
use serde_json::Value;

use util::ContainsKeys;

// =======================================================================
// CONSTANTS
// =======================================================================
static NULL: Value = Value::Null;

// =======================================================================
// STRUCT & TRAIT DEFINITION
// =======================================================================
/// JSON wrapper for a JSON response from a REST route
/// It wraps an optional generic type `T` that just needs to implement [serde's Deserialize](https://docs.serde.rs/serde/de/trait.Deserializer.html)
///
/// It derives Rocket's [Responder trait](https://api.rocket.rs/rocket/response/trait.Responder.html) so it can be used as such in a Rocket's route as illustrated below
///
/// ```rust,ignore
/// #[get("/")]
/// fn index() -> ResponseJSON<T> { ... }
/// ```
pub struct ResponseJSON<'v> {
    success: bool,
    http_code: u16,
    data: &'v Value,
    message: Option<String>, // required for error JSON
    resource: Option<String>,
    method: Option<String>,
    _phantom: PhantomData<&'v Value>
}

/// Test if the underlying structure is a valid ResponseJSON
pub trait IsResponseJSON {
    fn is_valid_json(&self) -> bool;
    fn is_ok_json(&self) -> bool;
    fn is_error_json(&self) -> bool;
}

// =======================================================================
// STRUCT IMPLEMENTATION
// =======================================================================
/// Default values for ResponseJSON are
///     - success: true
///     - http_code: 200
///     - data: Value::Null
///     - message: None
///     - resource: None
///     - method: None
impl<'v> Default for ResponseJSON<'v>
{
    fn default () -> ResponseJSON<'v> {
        ResponseJSON {
            success: true,
            http_code: 200,
            data: &NULL, // Returns a reference to `NULL` where its `'static` lifetime is coerced to 'v'
            message: None,
            resource: None,
            method: None,
            _phantom: PhantomData,
        }
    }
}

impl<'v> ResponseJSON<'v>
{
    // Create an empty OK ResponseJSON
    pub fn ok() -> ResponseJSON<'v> {
        ResponseJSON{
            success: true,
            http_code: 200,
            ..Default::default()
        }
    }

    // Create an empty OK ResponseJSON
    pub fn error() -> ResponseJSON<'v> {
        ResponseJSON{
            success: false,
            http_code: 500,
            message: Some("Unexpected error".to_string()),
            ..Default::default()
        }
    }

    /// Finalize ResponseJSON and transfer ownership to caller
    pub fn finalize(self) -> ResponseJSON<'v> {
        self
    }

    /// Set the HTTP Code of this ResponseJSON
    pub fn http_code(&mut self, code: u16) -> &mut ResponseJSON<'v> {
        self.http_code = code;
        self
    }

    /// Set the data of this ResponseJSON
    pub fn data(&mut self, ref_data: &'v Value) -> &mut ResponseJSON<'v>
    {
        self.data = ref_data;
        self
    }

    /// Set the error message.
    /// For Error JSON only (does nothing if `success == ok`)
    pub fn message(&mut self, string: String) -> &mut ResponseJSON<'v> {
        if !self.success {
            self.message = Some(string);
        } else {
            warn!("::AMIWO::RESPONSEJSON::MESSAGE::WARNING Trying to set `message` on an Ok JSON => ignored")
        }
        self
    }

    /// Set the resource that we tried to access.
    /// For Error JSON only (does nothing if `success == ok`)
    pub fn resource(&mut self, string: String) -> &mut ResponseJSON<'v> {
        if !self.success {
            self.resource = Some(string);
        } else {
            warn!("::AMIWO::RESPONSEJSON::MESSAGE::WARNING Trying to set `resource` on an Ok JSON => ignored")
        }
        self
    }

    /// Set the method that was used (GET, POST, ...).
    /// For Error JSON only (does nothing if `success == ok`)
    pub fn method(&mut self, string: String) -> &mut ResponseJSON<'v> {
        if !self.success {
            self.method = Some(string);
        } else {
            warn!("::AMIWO::RESPONSEJSON::METHOD::WARNING Trying to set `method` on an Ok JSON => ignored")
        }
        self
    }
}

// =======================================================================
// TRAIT IMPLEMENTATION
// ======================================================================
impl<'v> IsResponseJSON for ResponseJSON<'v> {
    /// Check if the JSON described as a String is a valid ResponseJSON
    fn is_valid_json(&self) -> bool {
        true
    }

    /// Check if the JSON described as a String is an Error JSON
    fn is_error_json(&self) -> bool
    {
        self.success == false
    }

    /// Check if the JSON described as a String is an OK JSON
    fn is_ok_json(&self) -> bool {
        self.success == true &&
        self.method.is_none() &&
        self.message.is_none() &&
        self.resource.is_none()
    }
}

impl IsResponseJSON for serde_json::map::Map<String, Value> {
    fn is_valid_json(&self) -> bool {
        self.contains_keys(&["success", "http_code"])
    }

    fn is_ok_json(&self) -> bool {
        self.is_valid_json() &&
        self["success"] == Value::Bool(true) &&
        self.get("http_code").is_some() && self.get("http_code").unwrap().is_number() &&
        self["method"].is_null() &&
        self["resource"].is_null() &&
        self["message"].is_null()
    }

    fn is_error_json(&self) -> bool {
        self.is_valid_json() &&
        self["success"] == Value::Bool(false) &&
        self.get("http_code").is_some() && self.get("http_code").unwrap().is_number()
    }
}

impl IsResponseJSON for Value {
    fn is_valid_json(&self) -> bool {
        self.contains_keys(&["success", "http_code"])
    }

    fn is_ok_json(&self) -> bool {
        self.is_valid_json() &&
        self["success"] == Value::Bool(true) &&
        self.get("http_code").is_some() && self.get("http_code").unwrap().is_number() &&
        self["method"].is_null() &&
        self["resource"].is_null() &&
        self["message"].is_null()
    }

    fn is_error_json(&self) -> bool {
        self.is_valid_json() &&
        self["success"] == Value::Bool(false) &&
        self.get("http_code").is_some() && self.get("http_code").unwrap().is_number()
    }
}

impl IsResponseJSON for String {
    fn is_valid_json(&self) -> bool{
        serde_json::from_str(&self)
            .ok()
            .map_or(
                false,
                |json : Value| json.is_valid_json()
            )
    }

    fn is_ok_json(&self) -> bool {
        serde_json::from_str(&self)
            .ok()
            .map_or(
                false,
                |json : Value| json.is_ok_json()
            )
    }

    fn is_error_json(&self) -> bool {
        serde_json::from_str(&self)
            .ok()
            .map_or(
                false,
                |json : Value| json.is_error_json()
            )
    }
}

impl IsResponseJSON for str {
    fn is_valid_json(&self) -> bool{
        serde_json::from_str(&self)
            .ok()
            .map_or(
                false,
                |json : Value| json.is_valid_json()
            )
    }

    fn is_ok_json(&self) -> bool {
        serde_json::from_str(&self)
            .ok()
            .map_or(
                false,
                |json : Value| json.is_ok_json()
            )
    }

    fn is_error_json(&self) -> bool {
        serde_json::from_str(&self)
            .ok()
            .map_or(
                false,
                |json : Value| json.is_error_json()
            )
    }
}

/*
/// ResponseJSON<T> can be created from a serde_json::JSON typed as `Value`
/// If the input JSON is like { success: false, http_code, message, resource, method } it creates an Error ResponseJSON
/// Else it creates an Ok ResponseJSON with it's data property set to the input JSON
impl From<Value> for ResponseJSON<Value>
{
    fn from(json: Value) -> Self {
        json.as_object() // Option<&Map<String, Value>>
            .map_or_else( // compute the data to be wrapped in the ResponseJSON
                // None => not an object
                || Err(&json),
                // Some(json) => check if is a valid ResponseJSON
                |obj| {
                    if obj.is_valid_json() {
                        Ok(
                            ResponseJSON::ok()
                                .http_code(obj["http_code"].as_u64().unwrap() as u16)
                                .data(obj["data"])
                                .finalize()
                        )
                    } else {
                        Err(&*obj as Value)
                    }
                }
            ).unwrap_or_else(|data| ResponseJSON::ok().data(data).finalize() )
    }
}
*/

/*
/// Implement Rocket's FormData to parse a ResponseJSON from incoming POST/... form data.
///
/// - If the content type of the request data is not
/// `application/json`, `Forward`s the request.
///
/// All relevant warnings and errors are written to the console
impl<T: DeserializeOwned> FromData for ResponseJSON<T> {
    type Error = serde_json::error::Error;

    fn from_data(request: &Request, data: Data) -> Outcome<Self, serde_json::error::Error> {
        if !request.content_type().map_or(false, |ct| ct.is_json()) {
            error!("Content-Type is not JSON.");
            return Outcome::Forward(data);
        }

        let size_limit = rocket::config::active()
            .and_then(|c| c.extras.get("limits.json")) // TODO: remove placeholder when upgrading to rocket version > 0.2.6
            // .and_then(|c| c.limits.get("json") // In next version
            .and_then(|limit| limit.as_integer())
            .unwrap_or(32768) as u64;

        serde_json::from_reader(data.open().take(size_limit))
            .map(|val| JSON(val))
            .map_err(|e| { error!("Couldn't parse JSON body: {:?}", e); e })
            .into_outcome(Status::BadRequest)

    }
}
*/

/*
/// Serializes the wrapped value into JSON. Returns a response with Content-Type
/// JSON and a fixed-size body with the serialized value. If serialization
/// fails, an `Err` of `Status::InternalServerError` is returned.
impl<T: Serialize> Responder<'static> for JSON<T> {
    fn respond(self) -> response::Result<'static> {
        serde_json::to_string(&self.0).map(|string| {
            content::JSON(string).respond().unwrap()
        }).map_err(|e| {
            error_!("JSON failed to serialize: {:?}", e);
            Status::InternalServerError
        })
    }
}
*/

// =======================================================================
// UNIT TESTS
// =======================================================================
#[cfg(test)]
mod tests {
    use super::ResponseJSON;
    use super::IsResponseJSON;
    use serde_json::Value;

    #[allow(non_snake_case)]
    #[test]
    fn test_IsResponseJSON_implem() {
        let json = r#"{
            "success": false,
            "http_code": 500,
            "resource": "some resource requested",
            "method": "GET",
            "message": "error message"
        }"#;
        assert_eq!(json.is_valid_json(), true);
        assert_eq!(json.is_ok_json(), false);
        assert_eq!(json.is_error_json(), true);

        let json = r#"{
            "success": true,
            "http_code": 200,
            "resource": "some resource requested",
            "method": "GET",
            "message": "error message"
        }"#;
        assert_eq!(json.is_valid_json(), true);
        assert_eq!(json.is_error_json(), false);
        assert_eq!(json.is_ok_json(), false);

        let json = r#"{
            "http_code": 200,
            "resource": "some resource requested",
            "method": "GET",
            "message": "error message"
        }"#;
        assert_eq!(json.is_valid_json(), false);
        assert_eq!(json.is_error_json(), false);
        assert_eq!(json.is_ok_json(), false);

        let json = r#"{
            "success": true,
            "resource": "some resource requested",
            "method": "GET",
            "message": "error message"
        }"#;
        assert_eq!(json.is_valid_json(), false);
        assert_eq!(json.is_error_json(), false);
        assert_eq!(json.is_ok_json(), false);
    }

    #[test]
    fn test_builder_ok() {
        let data : Value = "Some data".into();
        let mut json = ResponseJSON::ok();
        assert_eq!(json.success, true);
        assert_eq!(json.http_code, 200);
        assert!(json.data.is_null());
        assert_eq!(json.message, None);
        assert_eq!(json.method, None);
        assert_eq!(json.resource, None);

        json.http_code(201).data(&data).method("GET".to_string()).resource("some path".to_string()).message("error message".to_string());
        assert_eq!(json.http_code, 201);
        assert_eq!(json.data.as_str(), Some("Some data"));
        assert_eq!(json.message, None);
        assert_eq!(json.method, None);
        assert_eq!(json.resource, None);

        assert_eq!(json.is_valid_json(), true);
        assert_eq!(json.is_ok_json(), true);
        assert_eq!(json.is_error_json(), false);
    }

    #[test]
    fn test_builder_error() {
        let data : Value = "Some data".into();
        let mut json = ResponseJSON::error();
        assert_eq!(json.success, false);
        assert_eq!(json.http_code, 500);
        assert!(json.data.is_null());
        assert_eq!(json.message, Some("Unexpected error".to_string()));
        assert_eq!(json.method, None);
        assert_eq!(json.resource, None);

        json.http_code(401).data(&data).method("GET".to_string()).resource("some path".to_string()).message("error message".to_string());
        assert_eq!(json.http_code, 401);
        assert_eq!(json.data.as_str(), Some("Some data"));
        assert_eq!(json.message, Some("error message".to_string()));
        assert_eq!(json.method, Some("GET".to_string()));
        assert_eq!(json.resource, Some("some path".to_string()));

        assert_eq!(json.is_valid_json(), true);
        assert_eq!(json.is_ok_json(), false);
        assert_eq!(json.is_error_json(), true);
    }

    // TODO add test `from` Serde JSON

    // TODO add test with POST & GET routes taking a ResponseJSON as param
}   <|endoftext|>


<commit_before>//! Atom-class implementation.

use std::ops::{BitXor, Sub, Range, RangeInclusive};
use std::iter::FromIterator;
use std::fmt::{self, Debug};

use regex_syntax;
use itertools::Itertools;

use super::{Atom, ByteOrChar, Element, Repetition, Sequence, Union};
use super::{Distance, Step};
use util::set::{self, Contains};

// ----------------------------------------------------------------
// Class

/// Any member of a class of atoms
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum ClassMember<T: Atom> {
    /// A single atom
    Atom(T),

    /// A range of atoms
    Range(T, T)
}

impl<T: Atom> ClassMember<T> {
    /// Map the class-member's atoms to a different type
    pub fn map_atoms<U, F>(self, f: F) -> ClassMember<U>
        where F: Fn(T) -> U,
              U: Atom
    {
        match self {
            ClassMember::Atom(a) => ClassMember::Atom(f(a)),
            ClassMember::Range(a, b) => ClassMember::Range(f(a), f(b))
        }
    }

    /// Get the number of individual atoms that this class member represents.
    pub fn len(&self) -> usize {
        match self {
            &ClassMember::Atom(_) => 1,
            &ClassMember::Range(first, last) => last.distance(&first),
        }
    }

    /// Fetch an iterator over each discrete value contained by this member.
    #[inline]
     pub fn iter(&self) -> impl Iterator<Item=T>
        where T: Step {
        match self {
            &ClassMember::Atom(a) => AtomIter(a...a),
            &ClassMember::Range(a, b) => AtomIter(a...b),
        }
    }
}

impl<T: Atom> set::Contains<T> for ClassMember<T> {
    /// Check if the member is or contains a particular atom.
    fn contains(&self, x: T) -> bool {
        match self {
            &ClassMember::Atom(a) => x == a,
            &ClassMember::Range(a, b) => (a...b).contains(x),
        }
    }
}

/// Iterator over the atoms specified by a class member.
struct AtomIter<T: Atom>(RangeInclusive<T>);

impl<T: Atom> Iterator for AtomIter<T>
        where T: Step
{
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.0 {
            RangeInclusive::NonEmpty{start, end} => {
                let next_start = start.increment();
                if next_start > end {
                    self.0 = RangeInclusive::Empty{at: end};
                } else {
                    self.0 = RangeInclusive::NonEmpty{start: next_start, end: end};
                }
                Some(start) },
            RangeInclusive::Empty{..} => None
        }
    }
}

/*impl<T: Atom> Debug for ClassMember<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ClassMember::Atom(a) => write!(f, "{:?}", a),
            ClassMember::Range(first, last) => write!(f, "{:?}-{:?}", first, last)
        }
    }
}*/

impl<T: Atom> From<T> for ClassMember<T> {
    fn from(a: T) -> Self { ClassMember::Atom(a) }
}

impl From<regex_syntax::ClassRange> for ClassMember<ByteOrChar> {
    fn from(cr: regex_syntax::ClassRange) -> Self {
        ClassMember::Range(cr.start.into(), cr.end.into())
    }
}

impl From<regex_syntax::ByteRange> for ClassMember<ByteOrChar> {
    fn from(cr: regex_syntax::ByteRange) -> Self {
        ClassMember::Range(cr.start.into(), cr.end.into())
    }
}

impl From<regex_syntax::ClassRange> for ClassMember<char> {
    fn from(cr: regex_syntax::ClassRange) -> Self {
        ClassMember::Range(cr.start, cr.end)
    }
}

impl From<regex_syntax::ByteRange> for ClassMember<u8> {
    fn from(cr: regex_syntax::ByteRange) -> Self {
        ClassMember::Range(cr.start, cr.end)
    }
}

impl<T: Atom> From<Range<T>> for ClassMember<T>
    where T: Sub<usize,Output=T> + Sub<T,Output=usize>
{
    fn from(r: Range<T>) -> Self {
        if r.end - r.start > 1 { ClassMember::Range(r.start, r.end - 1usize) }
        else { ClassMember::Atom(r.start) }
    }
}
// ----------------------------------------------------------------

/// A set of atoms and/or ranges of atoms.
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct Class<T: Atom> {
    polarity: Polarity,
    members: Vec<ClassMember<T>>
}

// FIXME: [optimize] All operations on a Class<T>, including observer and
// predicate methods, currently require at least O(N) time.  We could probably
// improve this by changing the storage representation.
impl<T: Atom> Class<T> {
    /// Create a new Class instance with specified polarity and members taken
    /// from the given iterator.
    pub fn new<I,U>(p: Polarity, items: I) -> Self
        where I: IntoIterator<Item=U>,
              ClassMember<T>: From<U> {
        Class{polarity: p, members: Vec::from_iter(items.into_iter().map(|x| x.into()))}
    }

    /// Map the atoms in the class to a different atom type.
    pub fn map_atoms<U, F>(self, f: F) -> Class<U>
        where F: Fn(T) -> U,
              U: Atom
    {
        Class::new(self.polarity, self.members.into_iter().map(|m| m.map_atoms(&f)))
    }

    /// Check whether the class would match a particular atom.
    pub fn matches(&self, x: T) -> bool {
        self.has_member(x).bitxor(self.polarity == Polarity::NORMAL)
    }

    /// Check whether a particular atom is a member of this class.
    /// Unlike `matches`, this method does not take the class's polarity
    /// into account.
    pub fn has_member(&self, x: T) -> bool {
        self.members.iter().any(|&range| range.contains(x))
    }

    /// Get the number of members (atoms) in the class.
    ///
    /// Ranges of atoms count as the number of atoms in each range.
    // FIXME: [bug] handle inverted polarity?
    pub fn len(&self) -> usize {
        self.members.iter().map(|m| m.len()).sum()
    }

    /// Get the class's polarity.
    #[inline]
    pub fn polarity(&self) -> Polarity {
        self.polarity
    }

    /// Check if the class contains zero members.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.members.is_empty()
    }

    /// Fetch an iterator over the members specified in the class.
    ///
    /// Note that if the class has inverted polarity, the atoms in these
    /// members will be those which are **not** matched by the class.
    pub fn iter_members<'a>(&'a self) -> impl Iterator<Item=&'a ClassMember<T>> {
        self.members.iter()
    }
    }
}

impl<T: Atom> Debug for Class<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.polarity == Polarity::NORMAL {
            write!(f, "[{}]", self.members.iter().map(|m| format!("{:?}", m)).join(","))
        } else {
            write!(f, "[^{}]", self.members.iter().map(|m| format!("{:?}", m)).join(","))
        }
    }
}

impl<'a, T: 'a + Atom> set::Contains<&'a ClassMember<T>> for Class<T> {
    fn contains(&self, cm: &'a ClassMember<T>) -> bool {
        self.iter_members().any(|m| cm == m)
    }
}
impl<T: Atom> set::Contains<T> for Class<T> {
    fn contains(&self, x: T) -> bool {
        self.matches(x)
    }
}

// FIXME [optimize] I suspect that we need some sort of specialized data
// structure or clever algorithm in order to perform this test in a time better
// than O(N·M).
impl<T: Atom> set::IsSubsetOf<Class<T>> for Class<T> {
    fn is_subset_of(&self, other: &Self) -> bool {
        self.iter_members().flat_map(|m| m.iter()).all(|a| other.contains(a))
    }
}
impl<T: Atom> set::IsSubsetOf<Sequence<T>> for Class<T> {
    /// Any single-atom pattern like Class cannot match the same set of inputs
    /// as a Sequence, because a Sequence is guaranteed to contain at least
    /// two elements.
    #[inline]
    fn is_subset_of(&self, _: &Sequence<T>) -> bool {
        false
    }
}

impl<T: Atom> set::IsSubsetOf<Union<T>> for Class<T> {
    /// A class is a subset of a union if all members of the class are subsets
    /// of the union.
    fn is_subset_of(&self, _: &Union<T>) -> bool {
        unimplemented!()
    }
}

impl<T: Atom> set::IsSubsetOf<Repetition<T>> for Class<T> {
    fn is_subset_of(&self, _: &Repetition<T>) -> bool {
        unimplemented!()
    }
}

impl<T: Atom> set::IsSubsetOf<Element<T>> for Class<T> {
    fn is_subset_of(&self, other: &Element<T>) -> bool {
        match other {
            // Anything we can do, Wildcard can do betteeeeer...
            &Element::Wildcard => true,

            // A class *can* be a subset of an atom -- as long as that atom is
            // the only member of the class.
            &Element::Atom(a) => self.contains(a) && self.polarity == Polarity::NORMAL && self.len() == 1,
            &Element::Class(ref d) => self.is_subset_of(d),
            &Element::Tagged{ref element, ..} => self.is_subset_of(&**element),
            &Element::Sequence(ref seq) => self.is_subset_of(seq),
            &Element::Union(ref union) => self.is_subset_of(union),
            &Element::Repeat(ref rep) => self.is_subset_of(rep),
            &Element::Anchor(_) => false,
        }
    }
}

impl<T: Atom, U> FromIterator<U> for Class<T>
where ClassMember<T>: From<U> {
    fn from_iter<I>(iter: I) -> Self
        where I: IntoIterator<Item=U> {
        Class{polarity: Polarity::NORMAL, members: Vec::from_iter(iter.into_iter().map(|x| x.into()))}
    }
}

macro_rules! impl_class_from {
    ($T: ty, $from: ty) => {
        impl<'a> From<$from> for Class<$T> {
            fn from(c: $from) -> Self {
                Self::new(Polarity::NORMAL,
                          c.into_iter().map(|cr| if cr.end != cr.start { ClassMember::Range(cr.start.into(), cr.end.into()) }
                                            else { ClassMember::Atom(cr.start.into()) }))
            }
        }
    };
}

impl_class_from!(char, regex_syntax::CharClass);
impl_class_from!(u8, regex_syntax::ByteClass);
impl_class_from!(ByteOrChar, regex_syntax::CharClass);
impl_class_from!(ByteOrChar, regex_syntax::ByteClass);

// ----------------------------------------------------------------

/** Flag type used to differentiate between normal and inverting matches on
 * pattern elements. */
#[derive(Debug,Copy,Clone,PartialEq, PartialOrd, Eq, Ord)]
pub enum Polarity {
    /// Normal match behavior.
    NORMAL,
    /// Invert the match result.
    INVERTED
}
<commit_msg>pattern/class: fix for `len` on `ClassMember::Range`<commit_after>//! Atom-class implementation.

use std::ops::{BitXor, Sub, Range, RangeInclusive};
use std::iter::FromIterator;
use std::fmt::{self, Debug};

use regex_syntax;
use itertools::Itertools;

use super::{Atom, ByteOrChar, Element, Repetition, Sequence, Union};
use super::{Distance, Step};
use util::set::{self, Contains};

// ----------------------------------------------------------------
// Class

/// Any member of a class of atoms
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum ClassMember<T: Atom> {
    /// A single atom
    Atom(T),

    /// A range of atoms
    Range(T, T)
}

impl<T: Atom> ClassMember<T> {
    /// Map the class-member's atoms to a different type
    pub fn map_atoms<U, F>(self, f: F) -> ClassMember<U>
        where F: Fn(T) -> U,
              U: Atom
    {
        match self {
            ClassMember::Atom(a) => ClassMember::Atom(f(a)),
            ClassMember::Range(a, b) => ClassMember::Range(f(a), f(b))
        }
    }

    /// Get the number of individual atoms that this class member represents.
    pub fn len(&self) -> usize
        where T: Distance
    {
        match self {
            &ClassMember::Atom(_) => 1,
            &ClassMember::Range(first, last) => last.distance(&first) + 1,
        }
    }

    /// Fetch an iterator over each discrete value contained by this member.
    #[inline]
     pub fn iter(&self) -> impl Iterator<Item=T>
        where T: Step {
        match self {
            &ClassMember::Atom(a) => AtomIter(a...a),
            &ClassMember::Range(a, b) => AtomIter(a...b),
        }
    }
}

impl<T: Atom> set::Contains<T> for ClassMember<T> {
    /// Check if the member is or contains a particular atom.
    fn contains(&self, x: T) -> bool {
        match self {
            &ClassMember::Atom(a) => x == a,
            &ClassMember::Range(a, b) => (a...b).contains(x),
        }
    }
}

/// Iterator over the atoms specified by a class member.
struct AtomIter<T: Atom>(RangeInclusive<T>);

impl<T: Atom> Iterator for AtomIter<T>
        where T: Step
{
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.0 {
            RangeInclusive::NonEmpty{start, end} => {
                let next_start = start.increment();
                if next_start > end {
                    self.0 = RangeInclusive::Empty{at: end};
                } else {
                    self.0 = RangeInclusive::NonEmpty{start: next_start, end: end};
                }
                Some(start) },
            RangeInclusive::Empty{..} => None
        }
    }
}

/*impl<T: Atom> Debug for ClassMember<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ClassMember::Atom(a) => write!(f, "{:?}", a),
            ClassMember::Range(first, last) => write!(f, "{:?}-{:?}", first, last)
        }
    }
}*/

impl<T: Atom> From<T> for ClassMember<T> {
    fn from(a: T) -> Self { ClassMember::Atom(a) }
}

impl From<regex_syntax::ClassRange> for ClassMember<ByteOrChar> {
    fn from(cr: regex_syntax::ClassRange) -> Self {
        ClassMember::Range(cr.start.into(), cr.end.into())
    }
}

impl From<regex_syntax::ByteRange> for ClassMember<ByteOrChar> {
    fn from(cr: regex_syntax::ByteRange) -> Self {
        ClassMember::Range(cr.start.into(), cr.end.into())
    }
}

impl From<regex_syntax::ClassRange> for ClassMember<char> {
    fn from(cr: regex_syntax::ClassRange) -> Self {
        ClassMember::Range(cr.start, cr.end)
    }
}

impl From<regex_syntax::ByteRange> for ClassMember<u8> {
    fn from(cr: regex_syntax::ByteRange) -> Self {
        ClassMember::Range(cr.start, cr.end)
    }
}

impl<T: Atom> From<Range<T>> for ClassMember<T>
    where T: Sub<usize,Output=T> + Sub<T,Output=usize>
{
    fn from(r: Range<T>) -> Self {
        if r.end - r.start > 1 { ClassMember::Range(r.start, r.end - 1usize) }
        else { ClassMember::Atom(r.start) }
    }
}
// ----------------------------------------------------------------

/// A set of atoms and/or ranges of atoms.
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct Class<T: Atom> {
    polarity: Polarity,
    members: Vec<ClassMember<T>>
}

// FIXME: [optimize] All operations on a Class<T>, including observer and
// predicate methods, currently require at least O(N) time.  We could probably
// improve this by changing the storage representation.
impl<T: Atom> Class<T> {
    /// Create a new Class instance with specified polarity and members taken
    /// from the given iterator.
    pub fn new<I,U>(p: Polarity, items: I) -> Self
        where I: IntoIterator<Item=U>,
              ClassMember<T>: From<U> {
        Class{polarity: p, members: Vec::from_iter(items.into_iter().map(|x| x.into()))}
    }

    /// Map the atoms in the class to a different atom type.
    pub fn map_atoms<U, F>(self, f: F) -> Class<U>
        where F: Fn(T) -> U,
              U: Atom
    {
        Class::new(self.polarity, self.members.into_iter().map(|m| m.map_atoms(&f)))
    }

    /// Check whether the class would match a particular atom.
    pub fn matches(&self, x: T) -> bool {
        self.has_member(x).bitxor(self.polarity == Polarity::NORMAL)
    }

    /// Check whether a particular atom is a member of this class.
    /// Unlike `matches`, this method does not take the class's polarity
    /// into account.
    pub fn has_member(&self, x: T) -> bool {
        self.members.iter().any(|&range| range.contains(x))
    }

    /// Get the number of members (atoms) in the class.
    ///
    /// Ranges of atoms count as the number of atoms in each range.
    // FIXME: [bug] handle inverted polarity?
    pub fn len(&self) -> usize {
        self.members.iter().map(|m| m.len()).sum()
    }

    /// Get the class's polarity.
    #[inline]
    pub fn polarity(&self) -> Polarity {
        self.polarity
    }

    /// Check if the class contains zero members.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.members.is_empty()
    }

    /// Fetch an iterator over the members specified in the class.
    ///
    /// Note that if the class has inverted polarity, the atoms in these
    /// members will be those which are **not** matched by the class.
    pub fn iter_members<'a>(&'a self) -> impl Iterator<Item=&'a ClassMember<T>> {
        self.members.iter()
    }
    }
}

impl<T: Atom> Debug for Class<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.polarity == Polarity::NORMAL {
            write!(f, "[{}]", self.members.iter().map(|m| format!("{:?}", m)).join(","))
        } else {
            write!(f, "[^{}]", self.members.iter().map(|m| format!("{:?}", m)).join(","))
        }
    }
}

impl<'a, T: 'a + Atom> set::Contains<&'a ClassMember<T>> for Class<T> {
    fn contains(&self, cm: &'a ClassMember<T>) -> bool {
        self.iter_members().any(|m| cm == m)
    }
}
impl<T: Atom> set::Contains<T> for Class<T> {
    fn contains(&self, x: T) -> bool {
        self.matches(x)
    }
}

// FIXME [optimize] I suspect that we need some sort of specialized data
// structure or clever algorithm in order to perform this test in a time better
// than O(N·M).
impl<T: Atom> set::IsSubsetOf<Class<T>> for Class<T> {
    fn is_subset_of(&self, other: &Self) -> bool {
        self.iter_members().flat_map(|m| m.iter()).all(|a| other.contains(a))
    }
}
impl<T: Atom> set::IsSubsetOf<Sequence<T>> for Class<T> {
    /// Any single-atom pattern like Class cannot match the same set of inputs
    /// as a Sequence, because a Sequence is guaranteed to contain at least
    /// two elements.
    #[inline]
    fn is_subset_of(&self, _: &Sequence<T>) -> bool {
        false
    }
}

impl<T: Atom> set::IsSubsetOf<Union<T>> for Class<T> {
    /// A class is a subset of a union if all members of the class are subsets
    /// of the union.
    fn is_subset_of(&self, _: &Union<T>) -> bool {
        unimplemented!()
    }
}

impl<T: Atom> set::IsSubsetOf<Repetition<T>> for Class<T> {
    fn is_subset_of(&self, _: &Repetition<T>) -> bool {
        unimplemented!()
    }
}

impl<T: Atom> set::IsSubsetOf<Element<T>> for Class<T> {
    fn is_subset_of(&self, other: &Element<T>) -> bool {
        match other {
            // Anything we can do, Wildcard can do betteeeeer...
            &Element::Wildcard => true,

            // A class *can* be a subset of an atom -- as long as that atom is
            // the only member of the class.
            &Element::Atom(a) => self.contains(a) && self.polarity == Polarity::NORMAL && self.len() == 1,
            &Element::Class(ref d) => self.is_subset_of(d),
            &Element::Tagged{ref element, ..} => self.is_subset_of(&**element),
            &Element::Sequence(ref seq) => self.is_subset_of(seq),
            &Element::Union(ref union) => self.is_subset_of(union),
            &Element::Repeat(ref rep) => self.is_subset_of(rep),
            &Element::Anchor(_) => false,
        }
    }
}

impl<T: Atom, U> FromIterator<U> for Class<T>
where ClassMember<T>: From<U> {
    fn from_iter<I>(iter: I) -> Self
        where I: IntoIterator<Item=U> {
        Class{polarity: Polarity::NORMAL, members: Vec::from_iter(iter.into_iter().map(|x| x.into()))}
    }
}

macro_rules! impl_class_from {
    ($T: ty, $from: ty) => {
        impl<'a> From<$from> for Class<$T> {
            fn from(c: $from) -> Self {
                Self::new(Polarity::NORMAL,
                          c.into_iter().map(|cr| if cr.end != cr.start { ClassMember::Range(cr.start.into(), cr.end.into()) }
                                            else { ClassMember::Atom(cr.start.into()) }))
            }
        }
    };
}

impl_class_from!(char, regex_syntax::CharClass);
impl_class_from!(u8, regex_syntax::ByteClass);
impl_class_from!(ByteOrChar, regex_syntax::CharClass);
impl_class_from!(ByteOrChar, regex_syntax::ByteClass);

// ----------------------------------------------------------------

/** Flag type used to differentiate between normal and inverting matches on
 * pattern elements. */
#[derive(Debug,Copy,Clone,PartialEq, PartialOrd, Eq, Ord)]
pub enum Polarity {
    /// Normal match behavior.
    NORMAL,
    /// Invert the match result.
    INVERTED
}
<|endoftext|>