<commit_before>//! Support for nightly features in Cargo itself
//!
//! This file is the version of `feature_gate.rs` in upstream Rust for Cargo
//! itself and is intended to be the avenue for which new features in Cargo are
//! gated by default and then eventually stabilized. All known stable and
//! unstable features are tracked in this file.
//!
//! If you're reading this then you're likely interested in adding a feature to
//! Cargo, and the good news is that it shouldn't be too hard! To do this you'll
//! want to follow these steps:
//!
//! 1. Add your feature. Do this by searching for "look here" in this file and
//!    expanding the macro invocation that lists all features with your new
//!    feature.
//!
//! 2. Find the appropriate place to place the feature gate in Cargo itself. If
//!    you're extending the manifest format you'll likely just want to modify
//!    the `Manifest::feature_gate` function, but otherwise you may wish to
//!    place the feature gate elsewhere in Cargo.
//!
//! 3. To actually perform the feature gate, you'll want to have code that looks
//!    like:
//!
//! ```rust,ignore
//! use core::{Feature, Features};
//!
//! let feature = Feature::launch_into_space();
//! package.manifest().features().require(feature).chain_err(|| {
//!     "launching Cargo into space right now is unstable and may result in \
//!      unintended damage to your codebase, use with caution"
//! })?;
//! ```
//!
//! Notably you'll notice the `require` function called with your `Feature`, and
//! then you use `chain_err` to tack on more context for why the feature was
//! required when the feature isn't activated.
//!
//! 4. Update the unstable documentation at
//!    `src/doc/src/reference/unstable.md` to include a short description of
//!    how to use your new feature.  When the feature is stabilized, be sure
//!    that the Cargo Guide or Reference is updated to fully document the
//!    feature and remove the entry from the Unstable section.
//!
//! And hopefully that's it! Bear with us though that this is, at the time of
//! this writing, a very new feature in Cargo. If the process differs from this
//! we'll be sure to update this documentation!

use std::env;
use std::fmt;
use std::str::FromStr;

use failure::Error;

use util::errors::CargoResult;

/// The edition of the compiler (RFC 2052)
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
pub enum Edition {
    /// The 2015 edition
    Edition2015,
    /// The 2018 edition
    Edition2018,
}

impl fmt::Display for Edition {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Edition::Edition2015 => f.write_str("2015"),
            Edition::Edition2018 => f.write_str("2018"),
        }
    }
}
impl FromStr for Edition {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self, Error> {
        match s {
            "2015" => Ok(Edition::Edition2015),
            "2018" => Ok(Edition::Edition2018),
            s => {
                bail!("supported edition values are `2015` or `2018`, but `{}` \
                       is unknown", s)
            }
        }
    }
}

enum Status {
    Stable,
    Unstable,
}

macro_rules! features {
    (
        pub struct Features {
            $([$stab:ident] $feature:ident: bool,)*
        }
    ) => (
        #[derive(Default, Clone, Debug)]
        pub struct Features {
            $($feature: bool,)*
            activated: Vec<String>,
        }

        impl Feature {
            $(
                pub fn $feature() -> &'static Feature {
                    fn get(features: &Features) -> bool {
                        features.$feature
                    }
                    static FEAT: Feature = Feature {
                        name: stringify!($feature),
                        get,
                    };
                    &FEAT
                }
            )*

            fn is_enabled(&self, features: &Features) -> bool {
                (self.get)(features)
            }
        }

        impl Features {
            fn status(&mut self, feature: &str) -> Option<(&mut bool, Status)> {
                if feature.contains("_") {
                    return None
                }
                let feature = feature.replace("-", "_");
                $(
                    if feature == stringify!($feature) {
                        return Some((&mut self.$feature, stab!($stab)))
                    }
                )*
                None
            }
        }
    )
}

macro_rules! stab {
    (stable) => {
        Status::Stable
    };
    (unstable) => {
        Status::Unstable
    };
}

/// A listing of all features in Cargo
///
/// "look here"
///
/// This is the macro that lists all stable and unstable features in Cargo.
/// You'll want to add to this macro whenever you add a feature to Cargo, also
/// following the directions above.
///
/// Note that all feature names here are valid Rust identifiers, but the `_`
/// character is translated to `-` when specified in the `cargo-features`
/// manifest entry in `Cargo.toml`.
features! {
    pub struct Features {

        // A dummy feature that doesn't actually gate anything, but it's used in
        // testing to ensure that we can enable stable features.
        [stable] test_dummy_stable: bool,

        // A dummy feature that gates the usage of the `im-a-teapot` manifest
        // entry. This is basically just intended for tests.
        [unstable] test_dummy_unstable: bool,

        // Downloading packages from alternative registry indexes.
        [unstable] alternative_registries: bool,

        // Using editions
        [unstable] edition: bool,

        // Renaming a package in the manifest via the `package` key
        [unstable] rename_dependency: bool,

        // Whether a lock file is published with this crate
        [unstable] publish_lockfile: bool,

        // Overriding profiles for dependencies.
        [unstable] profile_overrides: bool,

        // Separating the namespaces for features and dependencies
        [unstable] namespaced_features: bool,
    }
}

pub struct Feature {
    name: &'static str,
    get: fn(&Features) -> bool,
}

impl Features {
    pub fn new(features: &[String], warnings: &mut Vec<String>) -> CargoResult<Features> {
        let mut ret = Features::default();
        for feature in features {
            ret.add(feature, warnings)?;
            ret.activated.push(feature.to_string());
        }
        Ok(ret)
    }

    fn add(&mut self, feature: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
        let (slot, status) = match self.status(feature) {
            Some(p) => p,
            None => bail!("unknown cargo feature `{}`", feature),
        };

        if *slot {
            bail!("the cargo feature `{}` has already been activated", feature);
        }

        match status {
            Status::Stable => {
                let warning = format!(
                    "the cargo feature `{}` is now stable \
                     and is no longer necessary to be listed \
                     in the manifest",
                    feature
                );
                warnings.push(warning);
            }
            Status::Unstable if !nightly_features_allowed() => bail!(
                "the cargo feature `{}` requires a nightly version of \
                 Cargo, but this is the `{}` channel",
                feature,
                channel()
            ),
            Status::Unstable => {}
        }

        *slot = true;

        Ok(())
    }

    pub fn activated(&self) -> &[String] {
        &self.activated
    }

    pub fn require(&self, feature: &Feature) -> CargoResult<()> {
        if feature.is_enabled(self) {
            Ok(())
        } else {
            let feature = feature.name.replace("_", "-");
            let mut msg = format!("feature `{}` is required", feature);

            if nightly_features_allowed() {
                let s = format!(
                    "\n\nconsider adding `cargo-features = [\"{0}\"]` \
                     to the manifest",
                    feature
                );
                msg.push_str(&s);
            } else {
                let s = format!(
                    "\n\n\
                     this Cargo does not support nightly features, but if you\n\
                     switch to nightly channel you can add\n\
                     `cargo-features = [\"{}\"]` to enable this feature",
                    feature
                );
                msg.push_str(&s);
            }
            bail!("{}", msg);
        }
    }

    pub fn is_enabled(&self, feature: &Feature) -> bool {
        feature.is_enabled(self)
    }
}

/// A parsed representation of all unstable flags that Cargo accepts.
///
/// Cargo, like `rustc`, accepts a suite of `-Z` flags which are intended for
/// gating unstable functionality to Cargo. These flags are only available on
/// the nightly channel of Cargo.
///
/// This struct doesn't have quite the same convenience macro that the features
/// have above, but the procedure should still be relatively stable for adding a
/// new unstable flag:
///
/// 1. First, add a field to this `CliUnstable` structure. All flags are allowed
///    to have a value as the `-Z` flags are either of the form `-Z foo` or
///    `-Z foo=bar`, and it's up to you how to parse `bar`.
///
/// 2. Add an arm to the match statement in `CliUnstable::add` below to match on
///    your new flag. The key (`k`) is what you're matching on and the value is
///    in `v`.
///
/// 3. (optional) Add a new parsing function to parse your datatype. As of now
///    there's an example for `bool`, but more can be added!
///
/// 4. In Cargo use `config.cli_unstable()` to get a reference to this structure
///    and then test for your flag or your value and act accordingly.
///
/// If you have any trouble with this, please let us know!
#[derive(Default, Debug)]
pub struct CliUnstable {
    pub print_im_a_teapot: bool,
    pub unstable_options: bool,
    pub offline: bool,
    pub no_index_update: bool,
    pub avoid_dev_deps: bool,
    pub minimal_versions: bool,
    pub package_features: bool,
    pub advanced_env: bool,
    pub config_profile: bool,
}

impl CliUnstable {
    pub fn parse(&mut self, flags: &[String]) -> CargoResult<()> {
        if !flags.is_empty() && !nightly_features_allowed() {
            bail!("the `-Z` flag is only accepted on the nightly channel of Cargo")
        }
        for flag in flags {
            self.add(flag)?;
        }
        Ok(())
    }

    fn add(&mut self, flag: &str) -> CargoResult<()> {
        let mut parts = flag.splitn(2, '=');
        let k = parts.next().unwrap();
        let v = parts.next();

        fn parse_bool(value: Option<&str>) -> CargoResult<bool> {
            match value {
                None | Some("yes") => Ok(true),
                Some("no") => Ok(false),
                Some(s) => bail!("expected `no` or `yes`, found: {}", s),
            }
        }

        match k {
            "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(v)?,
            "unstable-options" => self.unstable_options = true,
            "offline" => self.offline = true,
            "no-index-update" => self.no_index_update = true,
            "avoid-dev-deps" => self.avoid_dev_deps = true,
            "minimal-versions" => self.minimal_versions = true,
            "package-features" => self.package_features = true,
            "advanced-env" => self.advanced_env = true,
            "config-profile" => self.config_profile = true,
            _ => bail!("unknown `-Z` flag specified: {}", k),
        }

        Ok(())
    }
}

fn channel() -> String {
    env::var("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS").unwrap_or_else(|_| {
        ::version()
            .cfg_info
            .map(|c| c.release_channel)
            .unwrap_or_else(|| String::from("dev"))
    })
}

fn nightly_features_allowed() -> bool {
    match &channel()[..] {
        "nightly" | "dev" => true,
        _ => false,
    }
}
<commit_msg>Allow rustc bootstrap to use unstable features even though it's using a beta-cargo<commit_after>//! Support for nightly features in Cargo itself
//!
//! This file is the version of `feature_gate.rs` in upstream Rust for Cargo
//! itself and is intended to be the avenue for which new features in Cargo are
//! gated by default and then eventually stabilized. All known stable and
//! unstable features are tracked in this file.
//!
//! If you're reading this then you're likely interested in adding a feature to
//! Cargo, and the good news is that it shouldn't be too hard! To do this you'll
//! want to follow these steps:
//!
//! 1. Add your feature. Do this by searching for "look here" in this file and
//!    expanding the macro invocation that lists all features with your new
//!    feature.
//!
//! 2. Find the appropriate place to place the feature gate in Cargo itself. If
//!    you're extending the manifest format you'll likely just want to modify
//!    the `Manifest::feature_gate` function, but otherwise you may wish to
//!    place the feature gate elsewhere in Cargo.
//!
//! 3. To actually perform the feature gate, you'll want to have code that looks
//!    like:
//!
//! ```rust,ignore
//! use core::{Feature, Features};
//!
//! let feature = Feature::launch_into_space();
//! package.manifest().features().require(feature).chain_err(|| {
//!     "launching Cargo into space right now is unstable and may result in \
//!      unintended damage to your codebase, use with caution"
//! })?;
//! ```
//!
//! Notably you'll notice the `require` function called with your `Feature`, and
//! then you use `chain_err` to tack on more context for why the feature was
//! required when the feature isn't activated.
//!
//! 4. Update the unstable documentation at
//!    `src/doc/src/reference/unstable.md` to include a short description of
//!    how to use your new feature.  When the feature is stabilized, be sure
//!    that the Cargo Guide or Reference is updated to fully document the
//!    feature and remove the entry from the Unstable section.
//!
//! And hopefully that's it! Bear with us though that this is, at the time of
//! this writing, a very new feature in Cargo. If the process differs from this
//! we'll be sure to update this documentation!

use std::env;
use std::fmt;
use std::str::FromStr;

use failure::Error;

use util::errors::CargoResult;

/// The edition of the compiler (RFC 2052)
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
pub enum Edition {
    /// The 2015 edition
    Edition2015,
    /// The 2018 edition
    Edition2018,
}

impl fmt::Display for Edition {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Edition::Edition2015 => f.write_str("2015"),
            Edition::Edition2018 => f.write_str("2018"),
        }
    }
}
impl FromStr for Edition {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self, Error> {
        match s {
            "2015" => Ok(Edition::Edition2015),
            "2018" => Ok(Edition::Edition2018),
            s => {
                bail!("supported edition values are `2015` or `2018`, but `{}` \
                       is unknown", s)
            }
        }
    }
}

enum Status {
    Stable,
    Unstable,
}

macro_rules! features {
    (
        pub struct Features {
            $([$stab:ident] $feature:ident: bool,)*
        }
    ) => (
        #[derive(Default, Clone, Debug)]
        pub struct Features {
            $($feature: bool,)*
            activated: Vec<String>,
        }

        impl Feature {
            $(
                pub fn $feature() -> &'static Feature {
                    fn get(features: &Features) -> bool {
                        features.$feature
                    }
                    static FEAT: Feature = Feature {
                        name: stringify!($feature),
                        get,
                    };
                    &FEAT
                }
            )*

            fn is_enabled(&self, features: &Features) -> bool {
                (self.get)(features)
            }
        }

        impl Features {
            fn status(&mut self, feature: &str) -> Option<(&mut bool, Status)> {
                if feature.contains("_") {
                    return None
                }
                let feature = feature.replace("-", "_");
                $(
                    if feature == stringify!($feature) {
                        return Some((&mut self.$feature, stab!($stab)))
                    }
                )*
                None
            }
        }
    )
}

macro_rules! stab {
    (stable) => {
        Status::Stable
    };
    (unstable) => {
        Status::Unstable
    };
}

/// A listing of all features in Cargo
///
/// "look here"
///
/// This is the macro that lists all stable and unstable features in Cargo.
/// You'll want to add to this macro whenever you add a feature to Cargo, also
/// following the directions above.
///
/// Note that all feature names here are valid Rust identifiers, but the `_`
/// character is translated to `-` when specified in the `cargo-features`
/// manifest entry in `Cargo.toml`.
features! {
    pub struct Features {

        // A dummy feature that doesn't actually gate anything, but it's used in
        // testing to ensure that we can enable stable features.
        [stable] test_dummy_stable: bool,

        // A dummy feature that gates the usage of the `im-a-teapot` manifest
        // entry. This is basically just intended for tests.
        [unstable] test_dummy_unstable: bool,

        // Downloading packages from alternative registry indexes.
        [unstable] alternative_registries: bool,

        // Using editions
        [unstable] edition: bool,

        // Renaming a package in the manifest via the `package` key
        [unstable] rename_dependency: bool,

        // Whether a lock file is published with this crate
        [unstable] publish_lockfile: bool,

        // Overriding profiles for dependencies.
        [unstable] profile_overrides: bool,

        // Separating the namespaces for features and dependencies
        [unstable] namespaced_features: bool,
    }
}

pub struct Feature {
    name: &'static str,
    get: fn(&Features) -> bool,
}

impl Features {
    pub fn new(features: &[String], warnings: &mut Vec<String>) -> CargoResult<Features> {
        let mut ret = Features::default();
        for feature in features {
            ret.add(feature, warnings)?;
            ret.activated.push(feature.to_string());
        }
        Ok(ret)
    }

    fn add(&mut self, feature: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
        let (slot, status) = match self.status(feature) {
            Some(p) => p,
            None => bail!("unknown cargo feature `{}`", feature),
        };

        if *slot {
            bail!("the cargo feature `{}` has already been activated", feature);
        }

        match status {
            Status::Stable => {
                let warning = format!(
                    "the cargo feature `{}` is now stable \
                     and is no longer necessary to be listed \
                     in the manifest",
                    feature
                );
                warnings.push(warning);
            }
            Status::Unstable if !nightly_features_allowed() => bail!(
                "the cargo feature `{}` requires a nightly version of \
                 Cargo, but this is the `{}` channel",
                feature,
                channel()
            ),
            Status::Unstable => {}
        }

        *slot = true;

        Ok(())
    }

    pub fn activated(&self) -> &[String] {
        &self.activated
    }

    pub fn require(&self, feature: &Feature) -> CargoResult<()> {
        if feature.is_enabled(self) {
            Ok(())
        } else {
            let feature = feature.name.replace("_", "-");
            let mut msg = format!("feature `{}` is required", feature);

            if nightly_features_allowed() {
                let s = format!(
                    "\n\nconsider adding `cargo-features = [\"{0}\"]` \
                     to the manifest",
                    feature
                );
                msg.push_str(&s);
            } else {
                let s = format!(
                    "\n\n\
                     this Cargo does not support nightly features, but if you\n\
                     switch to nightly channel you can add\n\
                     `cargo-features = [\"{}\"]` to enable this feature",
                    feature
                );
                msg.push_str(&s);
            }
            bail!("{}", msg);
        }
    }

    pub fn is_enabled(&self, feature: &Feature) -> bool {
        feature.is_enabled(self)
    }
}

/// A parsed representation of all unstable flags that Cargo accepts.
///
/// Cargo, like `rustc`, accepts a suite of `-Z` flags which are intended for
/// gating unstable functionality to Cargo. These flags are only available on
/// the nightly channel of Cargo.
///
/// This struct doesn't have quite the same convenience macro that the features
/// have above, but the procedure should still be relatively stable for adding a
/// new unstable flag:
///
/// 1. First, add a field to this `CliUnstable` structure. All flags are allowed
///    to have a value as the `-Z` flags are either of the form `-Z foo` or
///    `-Z foo=bar`, and it's up to you how to parse `bar`.
///
/// 2. Add an arm to the match statement in `CliUnstable::add` below to match on
///    your new flag. The key (`k`) is what you're matching on and the value is
///    in `v`.
///
/// 3. (optional) Add a new parsing function to parse your datatype. As of now
///    there's an example for `bool`, but more can be added!
///
/// 4. In Cargo use `config.cli_unstable()` to get a reference to this structure
///    and then test for your flag or your value and act accordingly.
///
/// If you have any trouble with this, please let us know!
#[derive(Default, Debug)]
pub struct CliUnstable {
    pub print_im_a_teapot: bool,
    pub unstable_options: bool,
    pub offline: bool,
    pub no_index_update: bool,
    pub avoid_dev_deps: bool,
    pub minimal_versions: bool,
    pub package_features: bool,
    pub advanced_env: bool,
    pub config_profile: bool,
}

impl CliUnstable {
    pub fn parse(&mut self, flags: &[String]) -> CargoResult<()> {
        if !flags.is_empty() && !nightly_features_allowed() {
            bail!("the `-Z` flag is only accepted on the nightly channel of Cargo")
        }
        for flag in flags {
            self.add(flag)?;
        }
        Ok(())
    }

    fn add(&mut self, flag: &str) -> CargoResult<()> {
        let mut parts = flag.splitn(2, '=');
        let k = parts.next().unwrap();
        let v = parts.next();

        fn parse_bool(value: Option<&str>) -> CargoResult<bool> {
            match value {
                None | Some("yes") => Ok(true),
                Some("no") => Ok(false),
                Some(s) => bail!("expected `no` or `yes`, found: {}", s),
            }
        }

        match k {
            "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(v)?,
            "unstable-options" => self.unstable_options = true,
            "offline" => self.offline = true,
            "no-index-update" => self.no_index_update = true,
            "avoid-dev-deps" => self.avoid_dev_deps = true,
            "minimal-versions" => self.minimal_versions = true,
            "package-features" => self.package_features = true,
            "advanced-env" => self.advanced_env = true,
            "config-profile" => self.config_profile = true,
            _ => bail!("unknown `-Z` flag specified: {}", k),
        }

        Ok(())
    }
}

fn channel() -> String {
    if let Ok(staging) = env::var("RUSTC_BOOTSTRAP") {
        if staging == "1" {
            return "dev".to_string();
        }
    }
    if let Ok(override_channel) = env::var("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS") {
        return override_channel;
    }
    ::version()
        .cfg_info
        .map(|c| c.release_channel)
        .unwrap_or_else(|| String::from("dev"))
}

fn nightly_features_allowed() -> bool {
    match &channel()[..] {
        "nightly" | "dev" => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>#![crate_id = "ansi_term#0.1dev"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![desc = "A rust library for ANSI terminal colours and styles (bold, underline)"]

pub enum Colour {
    Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed(u8),
}

// These are the standard numeric sequences.
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html

impl Colour {
    fn foreground_code(&self) -> String {
        match *self {
            Black => "30".to_string(),
            Red => "31".to_string(),
            Green => "32".to_string(),
            Yellow => "33".to_string(),
            Blue => "34".to_string(),
            Purple => "35".to_string(),
            Cyan => "36".to_string(),
            White => "37".to_string(),
            Fixed(num) => format!("38;5;{}", num),
        }
    }

    fn background_code(&self) -> String {
        match *self {
            Black => "40".to_string(),
            Red => "41".to_string(),
            Green => "42".to_string(),
            Yellow => "43".to_string(),
            Blue => "44".to_string(),
            Purple => "45".to_string(),
            Cyan => "46".to_string(),
            White => "47".to_string(),
            Fixed(num) => format!("48;5;{}", num),
        }
    }
}

// There are only three different styles: plain (no formatting), only
// a foreground colour, and a catch-all for anything more complicated
// than that. It's technically possible to write other cases such as
// "bold foreground", but probably isn't worth writing all the code.

pub enum Style {
    Plain,
    Foreground(Colour),
    Style(StyleStruct),
}

// Having a struct inside an enum is currently unfinished in Rust, but
// should be put in there when that feature is complete.

pub struct StyleStruct {
    foreground: Colour,
    background: Option<Colour>,
    bold: bool,
    underline: bool,
}

impl Style {
    pub fn paint(&self, input: &str) -> String {
        match *self {
            Plain => input.to_string(),
            Foreground(c) => c.paint(input),
            Style(s) => match s {
                StyleStruct { foreground, background, bold, underline } => {
                    let bg = match background {
                        Some(c) => format!("{};", c.background_code()),
                        None => "".to_string()
                    };
                    let bo = if bold { "1;" } else { "" };
                    let un = if underline { "4;" } else { "" };
                    let painted = format!("\x1B[{}{}{}{}m{}\x1B[0m", bo, un, bg, foreground.foreground_code(), input.to_string());
                    return painted.to_string();
                }
            }
        }
    }
}

impl Style {
    pub fn bold(&self) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: None,          bold: true, underline: false }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: None,          bold: true, underline: false }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: st.background, bold: true, underline: st.underline }),
        }
    }

    pub fn underline(&self) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: None,          bold: false,   underline: true }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: None,          bold: false,   underline: true }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: st.background, bold: st.bold, underline: true }),
        }
    }

    pub fn on(&self, background: Colour) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: Some(background), bold: false,   underline: false }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: Some(background), bold: false,   underline: false }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: Some(background), bold: st.bold, underline: st.underline }),
        }
    }
}

impl Colour {
    // This is a short-cut so you don't have to use Blue.normal() just
    // to turn Blue into a Style. Annoyingly, this means that Blue and
    // Blue.normal() aren't of the same type, but this hasn't been an
    // issue so far.

    pub fn paint(&self, input: &str) -> String {
        let re = format!("\x1B[{}m{}\x1B[0m", self.foreground_code(), input);
        return re.to_string();
    }

    pub fn underline(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: false, underline: true })
    }

    pub fn bold(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: true, underline: false })
    }

    pub fn normal(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: false, underline: false })
    }

    pub fn on(&self, background: Colour) -> Style {
        Style(StyleStruct { foreground: *self, background: Some(background), bold: false, underline: false })
    }
}
#[cfg(test)]
mod tests {
    use super::{Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed};

    #[test]
    fn test_red() {
        let hi = Red.paint("hi");
        assert!(hi == "\x1B[31mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_black() {
        let hi = Black.normal().paint("hi");
        assert!(hi == "\x1B[30mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_yellow_bold() {
        let hi = Yellow.bold().paint("hi");
        assert!(hi == "\x1B[1;33mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_yellow_bold_2() {
        let hi = Yellow.normal().bold().paint("hi");
        assert!(hi == "\x1B[1;33mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_blue_underline() {
        let hi = Blue.underline().paint("hi");
        assert!(hi == "\x1B[4;34mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_green_bold_underline() {
        let hi = Green.bold().underline().paint("hi");
        assert!(hi == "\x1B[1;4;32mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_green_bold_underline_2() {
        let hi = Green.underline().bold().paint("hi");
        assert!(hi == "\x1B[1;4;32mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_purple_on_white() {
        let hi = Purple.on(White).paint("hi");
        assert!(hi == "\x1B[47;35mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_purple_on_white_2() {
        let hi = Purple.normal().on(White).paint("hi");
        assert!(hi == "\x1B[47;35mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_bold_on_white() {
        let hi = Cyan.bold().on(White).paint("hi");
        assert!(hi == "\x1B[1;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_underline_on_white() {
        let hi = Cyan.underline().on(White).paint("hi");
        assert!(hi == "\x1B[4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_bold_underline_on_white() {
        let hi = Cyan.bold().underline().on(White).paint("hi");
        assert!(hi == "\x1B[1;4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_underline_bold_on_white() {
        let hi = Cyan.underline().bold().on(White).paint("hi");
        assert!(hi == "\x1B[1;4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed() {
        let hi = Fixed(100).paint("hi");
        assert!(hi == "\x1B[38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed_on_purple() {
        let hi = Fixed(100).on(Purple).paint("hi");
        assert!(hi == "\x1B[45;38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed_on_fixed() {
        let hi = Fixed(100).on(Fixed(200)).paint("hi");
        assert!(hi == "\x1B[48;5;200;38;5;100mhi\x1B[0m".to_string());
    }
}
<commit_msg>Add strip_formatting function<commit_after>#![crate_id = "ansi_term#0.1dev"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![desc = "A rust library for ANSI terminal colours and styles (bold, underline)"]

#![feature(phase)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;

pub enum Colour {
    Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed(u8),
}

// These are the standard numeric sequences.
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html

impl Colour {
    fn foreground_code(&self) -> String {
        match *self {
            Black => "30".to_string(),
            Red => "31".to_string(),
            Green => "32".to_string(),
            Yellow => "33".to_string(),
            Blue => "34".to_string(),
            Purple => "35".to_string(),
            Cyan => "36".to_string(),
            White => "37".to_string(),
            Fixed(num) => format!("38;5;{}", num),
        }
    }

    fn background_code(&self) -> String {
        match *self {
            Black => "40".to_string(),
            Red => "41".to_string(),
            Green => "42".to_string(),
            Yellow => "43".to_string(),
            Blue => "44".to_string(),
            Purple => "45".to_string(),
            Cyan => "46".to_string(),
            White => "47".to_string(),
            Fixed(num) => format!("48;5;{}", num),
        }
    }
}

// There are only three different styles: plain (no formatting), only
// a foreground colour, and a catch-all for anything more complicated
// than that. It's technically possible to write other cases such as
// "bold foreground", but probably isn't worth writing all the code.

pub enum Style {
    Plain,
    Foreground(Colour),
    Style(StyleStruct),
}

// Having a struct inside an enum is currently unfinished in Rust, but
// should be put in there when that feature is complete.

pub struct StyleStruct {
    foreground: Colour,
    background: Option<Colour>,
    bold: bool,
    underline: bool,
}

impl Style {
    pub fn paint(&self, input: &str) -> String {
        match *self {
            Plain => input.to_string(),
            Foreground(c) => c.paint(input),
            Style(s) => match s {
                StyleStruct { foreground, background, bold, underline } => {
                    let bg = match background {
                        Some(c) => format!("{};", c.background_code()),
                        None => "".to_string()
                    };
                    let bo = if bold { "1;" } else { "" };
                    let un = if underline { "4;" } else { "" };
                    let painted = format!("\x1B[{}{}{}{}m{}\x1B[0m", bo, un, bg, foreground.foreground_code(), input.to_string());
                    return painted.to_string();
                }
            }
        }
    }
}

impl Style {
    pub fn bold(&self) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: None,          bold: true, underline: false }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: None,          bold: true, underline: false }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: st.background, bold: true, underline: st.underline }),
        }
    }

    pub fn underline(&self) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: None,          bold: false,   underline: true }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: None,          bold: false,   underline: true }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: st.background, bold: st.bold, underline: true }),
        }
    }

    pub fn on(&self, background: Colour) -> Style {
        match *self {
            Plain => Style(StyleStruct         { foreground: White,         background: Some(background), bold: false,   underline: false }),
            Foreground(c) => Style(StyleStruct { foreground: c,             background: Some(background), bold: false,   underline: false }),
            Style(st) => Style(StyleStruct     { foreground: st.foreground, background: Some(background), bold: st.bold, underline: st.underline }),
        }
    }
}

impl Colour {
    // This is a short-cut so you don't have to use Blue.normal() just
    // to turn Blue into a Style. Annoyingly, this means that Blue and
    // Blue.normal() aren't of the same type, but this hasn't been an
    // issue so far.

    pub fn paint(&self, input: &str) -> String {
        let re = format!("\x1B[{}m{}\x1B[0m", self.foreground_code(), input);
        return re.to_string();
    }

    pub fn underline(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: false, underline: true })
    }

    pub fn bold(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: true, underline: false })
    }

    pub fn normal(&self) -> Style {
        Style(StyleStruct { foreground: *self, background: None, bold: false, underline: false })
    }

    pub fn on(&self, background: Colour) -> Style {
        Style(StyleStruct { foreground: *self, background: Some(background), bold: false, underline: false })
    }
}
#[cfg(test)]
mod tests {
    use super::{Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed};

    #[test]
    fn test_red() {
        let hi = Red.paint("hi");
        assert!(hi == "\x1B[31mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_black() {
        let hi = Black.normal().paint("hi");
        assert!(hi == "\x1B[30mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_yellow_bold() {
        let hi = Yellow.bold().paint("hi");
        assert!(hi == "\x1B[1;33mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_yellow_bold_2() {
        let hi = Yellow.normal().bold().paint("hi");
        assert!(hi == "\x1B[1;33mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_blue_underline() {
        let hi = Blue.underline().paint("hi");
        assert!(hi == "\x1B[4;34mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_green_bold_underline() {
        let hi = Green.bold().underline().paint("hi");
        assert!(hi == "\x1B[1;4;32mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_green_bold_underline_2() {
        let hi = Green.underline().bold().paint("hi");
        assert!(hi == "\x1B[1;4;32mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_purple_on_white() {
        let hi = Purple.on(White).paint("hi");
        assert!(hi == "\x1B[47;35mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_purple_on_white_2() {
        let hi = Purple.normal().on(White).paint("hi");
        assert!(hi == "\x1B[47;35mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_bold_on_white() {
        let hi = Cyan.bold().on(White).paint("hi");
        assert!(hi == "\x1B[1;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_underline_on_white() {
        let hi = Cyan.underline().on(White).paint("hi");
        assert!(hi == "\x1B[4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_bold_underline_on_white() {
        let hi = Cyan.bold().underline().on(White).paint("hi");
        assert!(hi == "\x1B[1;4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_cyan_underline_bold_on_white() {
        let hi = Cyan.underline().bold().on(White).paint("hi");
        assert!(hi == "\x1B[1;4;47;36mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed() {
        let hi = Fixed(100).paint("hi");
        assert!(hi == "\x1B[38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed_on_purple() {
        let hi = Fixed(100).on(Purple).paint("hi");
        assert!(hi == "\x1B[45;38;5;100mhi\x1B[0m".to_string());
    }

    #[test]
    fn test_fixed_on_fixed() {
        let hi = Fixed(100).on(Fixed(200)).paint("hi");
        assert!(hi == "\x1B[48;5;200;38;5;100mhi\x1B[0m".to_string());
    }
}

pub fn strip_formatting(input: &String) -> String {
    let re = regex!("\x1B\\[.+?m");
    re.replace_all(input.as_slice(), "").to_string()
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use hyper::method::Method;
use hyper::uri::RequestUri;
use serde_json;
use serde::Serialize;

use super::config::Config;
use super::memdbstash::MemDbStash;
use super::{Result, ResultExt};

struct ServerContext {
    pub config: Config,
    pub stash: MemDbStash,
}

pub struct ApiServer {
    ctx: Arc<ServerContext>,
}

#[derive(Serialize)]
struct ApiError {
    #[serde(rename="type")]
    pub ty: String,
    pub message: String,
}

#[derive(Serialize)]
struct HealthCheckResult {
    is_healthy: bool,
    sync_lag: u32,
}

impl ApiServer {
    pub fn new(config: &Config) -> Result<ApiServer> {
        Ok(ApiServer {
            ctx: Arc::new(ServerContext {
                config: config.clone(),
                stash: MemDbStash::new(config)?,
            }),
        })
    }

    pub fn run(&self) -> Result<()> {
        let ctx = self.ctx.clone();
        Server::http(self.ctx.config.get_http_socket_addr()?)?
            .handle(move |req: Request, resp: Response|
        {
            let handler = match req.method {
                Method::Get => {
                    if let RequestUri::AbsolutePath(ref path) = req.uri {
                        match path.as_str() {
                            "/health" => healthcheck_handler,
                            _ => not_found_handler,
                        }
                    } else {
                        bad_request_handler
                    }
                }
                _ => {
                    method_not_allowed_handler
                }
            };
            handler(&*ctx.clone(), req, resp).unwrap();
        })?;
        Ok(())
    }
}

fn respond<T: Serialize>(mut resp: Response, obj: T, status: StatusCode) -> Result<()> {
    *resp.status_mut() = status;
    let mut body : Vec<u8> = vec![];
    serde_json::to_writer(&mut body, &obj)
        .chain_err(|| "Failed to serialize response for client")?;
    resp.send(&body[..])?;
    Ok(())
}

fn healthcheck_handler(ctx: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    // TODO: cache this
    let state = ctx.stash.get_sync_status()?;
    respond(resp, HealthCheckResult {
        is_healthy: state.is_healthy(),
        sync_lag: state.lag(),
    }, if state.is_healthy() {
        StatusCode::Ok
    } else {
        StatusCode::ServiceUnavailable
    })
}

fn not_found_handler(_: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "not_found".into(),
        message: "The requested resource was not found".into()
    }, StatusCode::NotFound)
}

fn bad_request_handler(_: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "bad_request".into(),
        message: "The request could not be handled".into()
    }, StatusCode::BadRequest)
}

fn method_not_allowed_handler(_: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "method_not_allowed".into(),
        message: "The server cannot handle this method".into()
    }, StatusCode::MethodNotAllowed)
}
<commit_msg>Set application/json content type<commit_after>use std::sync::Arc;

use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use hyper::method::Method;
use hyper::header::ContentType;
use hyper::uri::RequestUri;
use serde_json;
use serde::Serialize;

use super::config::Config;
use super::memdbstash::MemDbStash;
use super::{Result, ResultExt};

struct ServerContext {
    pub config: Config,
    pub stash: MemDbStash,
}

pub struct ApiServer {
    ctx: Arc<ServerContext>,
}

#[derive(Serialize)]
struct ApiError {
    #[serde(rename="type")]
    pub ty: String,
    pub message: String,
}

#[derive(Serialize)]
struct HealthCheckResult {
    is_healthy: bool,
    sync_lag: u32,
}

impl ApiServer {
    pub fn new(config: &Config) -> Result<ApiServer> {
        Ok(ApiServer {
            ctx: Arc::new(ServerContext {
                config: config.clone(),
                stash: MemDbStash::new(config)?,
            }),
        })
    }

    pub fn run(&self) -> Result<()> {
        let ctx = self.ctx.clone();
        Server::http(self.ctx.config.get_http_socket_addr()?)?
            .handle(move |req: Request, resp: Response|
        {
            let handler = match req.method {
                Method::Get => {
                    if let RequestUri::AbsolutePath(ref path) = req.uri {
                        match path.as_str() {
                            "/health" => healthcheck_handler,
                            _ => not_found_handler,
                        }
                    } else {
                        bad_request_handler
                    }
                }
                _ => {
                    method_not_allowed_handler
                }
            };
            handler(&*ctx.clone(), req, resp).unwrap();
        })?;
        Ok(())
    }
}

fn respond<T: Serialize>(mut resp: Response, obj: T, status: StatusCode) -> Result<()> {
    *resp.status_mut() = status;
    let mut body : Vec<u8> = vec![];
    resp.headers_mut().set(ContentType::json());
    serde_json::to_writer(&mut body, &obj)
        .chain_err(|| "Failed to serialize response for client")?;
    resp.send(&body[..])?;
    Ok(())
}

fn healthcheck_handler(ctx: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    // TODO: cache this
    let state = ctx.stash.get_sync_status()?;
    respond(resp, HealthCheckResult {
        is_healthy: state.is_healthy(),
        sync_lag: state.lag(),
    }, if state.is_healthy() {
        StatusCode::Ok
    } else {
        StatusCode::ServiceUnavailable
    })
}

fn not_found_handler(_: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "not_found".into(),
        message: "The requested resource was not found".into()
    }, StatusCode::NotFound)
}

fn bad_request_handler(_: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "bad_request".into(),
        message: "The request could not be handled".into()
    }, StatusCode::BadRequest)
}

fn method_not_allowed_handler(_: &ServerContext, _: Request, resp: Response)
    -> Result<()>
{
    respond(resp, ApiError {
        ty: "method_not_allowed".into(),
        message: "The server cannot handle this method".into()
    }, StatusCode::MethodNotAllowed)
}
<|endoftext|>


<commit_before>//! [json_typegen](https://typegen.vestera.as/) as just a library,
//! for use in build scripts and other crates.
//! If you want an actual interface, like a website, CLI or procedural macro, check the repo:
//! [github.com/evestera/json_typegen](https://github.com/evestera/json_typegen)
//!
//! Note: This crate is to a certain extent considered internal API of the `json_typegen` tools.
//! If you want to use this crate directly, be prepared for breaking changes to happen, and consider
//! [opening an issue](https://github.com/evestera/json_typegen/issues/new)
//! to let me know what you are using. (Breaking changes may still happen,
//! but then I'll at least try to keep your use-case in mind if possible.
//! This has happened enough by now that there are parts I already consider public API.)

use thiserror::Error;

mod generation;
mod hints;
mod inference;
mod options;
#[cfg(feature = "option-parsing")]
pub mod parse;
#[cfg(feature = "progress")]
mod progress;
mod shape;
mod to_singular;
mod util;

use crate::hints::Hints;
use crate::inference::shape_from_json;
pub use crate::options::{ImportStyle, Options, OutputMode, StringTransform};
pub use crate::shape::Shape;

/// The errors that json_typegen_shared may produce
///
/// No stability guarantees are made with for this type
/// except that it is a type that implements `std::error::Error`
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum JTError {
    #[cfg(feature = "remote-samples")]
    #[error("An error occurred while fetching JSON")]
    SampleFetchingError(#[from] reqwest::Error),
    #[cfg(feature = "local-samples")]
    #[error("An error occurred while reading JSON from file")]
    SampleReadingError(#[from] std::io::Error),
    #[error("An error occurred while parsing JSON")]
    JsonParsingError(#[from] inference::JsonInputErr),
    #[error("An error occurred while parsing a macro or macro input: {0}")]
    MacroParsingError(String),
}

/// Utilities exposed only to be available inside the `json_typegen` workspace. Internal API.
pub mod internal_util {
    pub fn display_error_with_causes(error: &dyn std::error::Error) -> String {
        let mut message = format!("{}", error);
        let mut err = error;
        while let Some(source) = err.source() {
            message += &format!("\n  Caused by: {}", source);
            err = source;
        }
        message
    }
}

enum SampleSource<'a> {
    #[cfg(feature = "remote-samples")]
    Url(&'a str),
    #[cfg(feature = "local-samples")]
    File(&'a str),
    Text(&'a str),
}

#[cfg(feature = "option-parsing")]
/// Generate code from a `json_typegen` macro invocation
pub fn codegen_from_macro(input: &str) -> Result<String, JTError> {
    let macro_input = parse::full_macro(input).map_err(JTError::MacroParsingError)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

#[cfg(feature = "option-parsing")]
/// Generate code from the arguments to a `json_typegen` macro invocation
pub fn codegen_from_macro_input(input: &str) -> Result<String, JTError> {
    let macro_input = parse::macro_input(input).map_err(JTError::MacroParsingError)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

/// The main code generation function for `json_typegen`
pub fn codegen(name: &str, input: &str, mut options: Options) -> Result<String, JTError> {
    let source = infer_source_type(input);
    let name = handle_pub_in_name(name, &mut options);

    let mut hints_vec = Vec::new();
    std::mem::swap(&mut options.hints, &mut hints_vec);

    let mut hints = Hints::new();
    for (pointer, hint) in hints_vec.iter() {
        hints.add(pointer, hint);
    }

    let shape = infer_from_sample(&source, &options, &hints)?;

    codegen_from_shape(name, &shape, options)
}

/// Just code generation, no inference
pub fn codegen_from_shape(name: &str, shape: &Shape, options: Options) -> Result<String, JTError> {
    let mut generated_code = match options.output_mode {
        OutputMode::Rust => generation::rust::rust_types(name, shape, options),
        OutputMode::JsonSchema => generation::json_schema::json_schema(name, shape, options),
        OutputMode::KotlinJackson | OutputMode::KotlinKotlinx => {
            generation::kotlin::kotlin_types(name, shape, options)
        }
        OutputMode::Shape => generation::shape::shape_string(name, shape, options),
        OutputMode::Typescript => generation::typescript::typescript_types(name, shape, options),
        OutputMode::TypescriptTypeAlias => {
            generation::typescript_type_alias::typescript_type_alias(name, shape, options)
        }
    };

    // Ensure generated code ends with exactly one newline
    generated_code.truncate(generated_code.trim_end().len());
    generated_code.push('\n');

    Ok(generated_code)
}

/// Parse "names" like `pub(crate) Foo` into a name and a visibility option
fn handle_pub_in_name<'a>(name: &'a str, options: &mut Options) -> &'a str {
    if let Some(suffix) = name.strip_prefix("pub ") {
        options.type_visibility = "pub".to_string();
        return suffix;
    }
    if name.starts_with("pub(") {
        if let Some((visibility, rest)) = name.split_once(") ") {
            options.type_visibility = format!("{})", visibility);
            return rest;
        }
    }
    name
}

fn infer_source_type(s: &str) -> SampleSource {
    let s = s.trim();
    if s.starts_with('{') || s.starts_with('[') {
        return SampleSource::Text(s);
    }
    #[cfg(feature = "remote-samples")]
    if s.starts_with("http://") || s.starts_with("https://") {
        return SampleSource::Url(s);
    }
    #[cfg(feature = "local-samples")]
    return SampleSource::File(s);
    #[cfg(not(feature = "local-samples"))]
    return SampleSource::Text(s);
}

fn infer_from_sample(
    source: &SampleSource,
    options: &Options,
    hints: &Hints,
) -> Result<Shape, JTError> {
    let parse_result = match *source {
        #[cfg(feature = "remote-samples")]
        SampleSource::Url(url) => {
            shape_from_json(reqwest::get(url)?.error_for_status()?, options, hints)
        }

        #[cfg(all(feature = "local-samples", feature = "progress"))]
        SampleSource::File(path) => shape_from_json(
            crate::progress::FileWithProgress::open(path)?,
            options,
            hints,
        ),
        #[cfg(all(feature = "local-samples", not(feature = "progress")))]
        SampleSource::File(path) => shape_from_json(std::fs::File::open(path)?, options, hints),

        SampleSource::Text(text) => shape_from_json(text.as_bytes(), options, hints),
    };
    Ok(parse_result?)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_handle_pub_in_name() {
        let mut options = Options::default();
        let name = handle_pub_in_name("Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, Options::default().type_visibility);
        let name = handle_pub_in_name("pub Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub".to_string());
        let name = handle_pub_in_name("pub(crate) Foo Bar", &mut options);
        assert_eq!(name, "Foo Bar");
        assert_eq!(options.type_visibility, "pub(crate)".to_string());
        let name = handle_pub_in_name("pub(some::path) Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub(some::path)".to_string());
    }
}
<commit_msg>Remove use of split_once to lower MSRV<commit_after>//! [json_typegen](https://typegen.vestera.as/) as just a library,
//! for use in build scripts and other crates.
//! If you want an actual interface, like a website, CLI or procedural macro, check the repo:
//! [github.com/evestera/json_typegen](https://github.com/evestera/json_typegen)
//!
//! Note: This crate is to a certain extent considered internal API of the `json_typegen` tools.
//! If you want to use this crate directly, be prepared for breaking changes to happen, and consider
//! [opening an issue](https://github.com/evestera/json_typegen/issues/new)
//! to let me know what you are using. (Breaking changes may still happen,
//! but then I'll at least try to keep your use-case in mind if possible.
//! This has happened enough by now that there are parts I already consider public API.)

use thiserror::Error;

mod generation;
mod hints;
mod inference;
mod options;
#[cfg(feature = "option-parsing")]
pub mod parse;
#[cfg(feature = "progress")]
mod progress;
mod shape;
mod to_singular;
mod util;

use crate::hints::Hints;
use crate::inference::shape_from_json;
pub use crate::options::{ImportStyle, Options, OutputMode, StringTransform};
pub use crate::shape::Shape;

/// The errors that json_typegen_shared may produce
///
/// No stability guarantees are made with for this type
/// except that it is a type that implements `std::error::Error`
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum JTError {
    #[cfg(feature = "remote-samples")]
    #[error("An error occurred while fetching JSON")]
    SampleFetchingError(#[from] reqwest::Error),
    #[cfg(feature = "local-samples")]
    #[error("An error occurred while reading JSON from file")]
    SampleReadingError(#[from] std::io::Error),
    #[error("An error occurred while parsing JSON")]
    JsonParsingError(#[from] inference::JsonInputErr),
    #[error("An error occurred while parsing a macro or macro input: {0}")]
    MacroParsingError(String),
}

/// Utilities exposed only to be available inside the `json_typegen` workspace. Internal API.
pub mod internal_util {
    pub fn display_error_with_causes(error: &dyn std::error::Error) -> String {
        let mut message = format!("{}", error);
        let mut err = error;
        while let Some(source) = err.source() {
            message += &format!("\n  Caused by: {}", source);
            err = source;
        }
        message
    }
}

enum SampleSource<'a> {
    #[cfg(feature = "remote-samples")]
    Url(&'a str),
    #[cfg(feature = "local-samples")]
    File(&'a str),
    Text(&'a str),
}

#[cfg(feature = "option-parsing")]
/// Generate code from a `json_typegen` macro invocation
pub fn codegen_from_macro(input: &str) -> Result<String, JTError> {
    let macro_input = parse::full_macro(input).map_err(JTError::MacroParsingError)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

#[cfg(feature = "option-parsing")]
/// Generate code from the arguments to a `json_typegen` macro invocation
pub fn codegen_from_macro_input(input: &str) -> Result<String, JTError> {
    let macro_input = parse::macro_input(input).map_err(JTError::MacroParsingError)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

/// The main code generation function for `json_typegen`
pub fn codegen(name: &str, input: &str, mut options: Options) -> Result<String, JTError> {
    let source = infer_source_type(input);
    let name = handle_pub_in_name(name, &mut options);

    let mut hints_vec = Vec::new();
    std::mem::swap(&mut options.hints, &mut hints_vec);

    let mut hints = Hints::new();
    for (pointer, hint) in hints_vec.iter() {
        hints.add(pointer, hint);
    }

    let shape = infer_from_sample(&source, &options, &hints)?;

    codegen_from_shape(name, &shape, options)
}

/// Just code generation, no inference
pub fn codegen_from_shape(name: &str, shape: &Shape, options: Options) -> Result<String, JTError> {
    let mut generated_code = match options.output_mode {
        OutputMode::Rust => generation::rust::rust_types(name, shape, options),
        OutputMode::JsonSchema => generation::json_schema::json_schema(name, shape, options),
        OutputMode::KotlinJackson | OutputMode::KotlinKotlinx => {
            generation::kotlin::kotlin_types(name, shape, options)
        }
        OutputMode::Shape => generation::shape::shape_string(name, shape, options),
        OutputMode::Typescript => generation::typescript::typescript_types(name, shape, options),
        OutputMode::TypescriptTypeAlias => {
            generation::typescript_type_alias::typescript_type_alias(name, shape, options)
        }
    };

    // Ensure generated code ends with exactly one newline
    generated_code.truncate(generated_code.trim_end().len());
    generated_code.push('\n');

    Ok(generated_code)
}

/// Parse "names" like `pub(crate) Foo` into a name and a visibility option
fn handle_pub_in_name<'a>(name: &'a str, options: &mut Options) -> &'a str {
    if let Some(suffix) = name.strip_prefix("pub ") {
        options.type_visibility = "pub".to_string();
        return suffix;
    }
    if name.starts_with("pub(") {
        // MSRV: after 1.52 use split_once
        let split = name.splitn(2, ") ").collect::<Vec<_>>();
        if split.len() == 2 {
            options.type_visibility = format!("{})", split[0]);
            return split[1];
        }
    }
    name
}

fn infer_source_type(s: &str) -> SampleSource {
    let s = s.trim();
    if s.starts_with('{') || s.starts_with('[') {
        return SampleSource::Text(s);
    }
    #[cfg(feature = "remote-samples")]
    if s.starts_with("http://") || s.starts_with("https://") {
        return SampleSource::Url(s);
    }
    #[cfg(feature = "local-samples")]
    return SampleSource::File(s);
    #[cfg(not(feature = "local-samples"))]
    return SampleSource::Text(s);
}

fn infer_from_sample(
    source: &SampleSource,
    options: &Options,
    hints: &Hints,
) -> Result<Shape, JTError> {
    let parse_result = match *source {
        #[cfg(feature = "remote-samples")]
        SampleSource::Url(url) => {
            shape_from_json(reqwest::get(url)?.error_for_status()?, options, hints)
        }

        #[cfg(all(feature = "local-samples", feature = "progress"))]
        SampleSource::File(path) => shape_from_json(
            crate::progress::FileWithProgress::open(path)?,
            options,
            hints,
        ),
        #[cfg(all(feature = "local-samples", not(feature = "progress")))]
        SampleSource::File(path) => shape_from_json(std::fs::File::open(path)?, options, hints),

        SampleSource::Text(text) => shape_from_json(text.as_bytes(), options, hints),
    };
    Ok(parse_result?)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_handle_pub_in_name() {
        let mut options = Options::default();
        let name = handle_pub_in_name("Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, Options::default().type_visibility);
        let name = handle_pub_in_name("pub Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub".to_string());
        let name = handle_pub_in_name("pub(crate) Foo Bar", &mut options);
        assert_eq!(name, "Foo Bar");
        assert_eq!(options.type_visibility, "pub(crate)".to_string());
        let name = handle_pub_in_name("pub(some::path) Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub(some::path)".to_string());
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use regex::{Captures};
use string::constants::UNACCONTABLE_WORDS;

const SINGULAR_RULES: [&'static str; 27] = [
    "ews",
    "",
    "um",
    "sis",
    "sis",
    "fe",
    "",
    "",
    "f",
    "y",
    "eries",
    "ovie",
    "",
    "ouse",
    "",
    "",
    "is",
    "xis",
    "us",
    "",
    "",
    "ex",
    "ix",
    "",
    "",
    "",
    ""
];

/// Converts a `String` to singularized `String`
///
/// #Examples
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // singularize_foo_bars_to_foo_bar() {
///     let mock_string: String = "foo_bars".to_string();
///     let expected_string: String = "foo_bar".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // singularizes_oxen_to_ox() {
///     let mock_string: String = "oxen".to_string();
///     let expected_string: String = "ox".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // singularizes_crates_to_crate() {
///     let mock_string: String = "crates".to_string();
///     let expected_string: String = "crate".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // singularizes_boxes_to_box() {
///     let mock_string: String = "boxes".to_string();
///     let expected_string: String = "box".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // does_not_singularize_vengeance {
///     let mock_string: String = "vengeance".to_string();
///     let expected_string: String = "vengeance".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // does_not_singularize_yoga() {
///     let mock_string: String = "yoga".to_string();
///     let expected_string: String = "yoga".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
///
//TODO: Refactor
pub fn to_singular<'a>(non_singular_string: String) -> String {
    if UNACCONTABLE_WORDS.contains(&non_singular_string.as_ref()) {
        return non_singular_string;
    } else {
        let singular_regexes: [Regex; 27] = singular_rules_regexes();
        for singular_index in 0..singular_regexes.len() {
            if singular_regexes[singular_index].is_match(&non_singular_string.as_ref()) {
                let cap = singular_regexes[singular_index].captures(&non_singular_string.as_ref()).unwrap();
                if cap.len() > 2 {
                    return format!("{}{}{}", cap.at(1).unwrap_or(""), cap.at(2).unwrap_or(""), SINGULAR_RULES[singular_index]);
                } else {
                    return format!("{}{}", cap.at(1).unwrap_or(""), SINGULAR_RULES[singular_index]);
                }
            }
        }

        return non_singular_string;
    }
}
    fn singular_rules_regexes<'a>() -> [Regex; 27] {
        return [
            Regex::new(r"(n)ews$").unwrap(),
            Regex::new(r"(\w*)(o)es$").unwrap(),
            Regex::new(r"(\w*)([ti])a$").unwrap(),
            Regex::new(r"((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$").unwrap(),
            Regex::new(r"(^analy)(sis|ses)$").unwrap(),
            Regex::new(r"(\w*)([^f])ves$").unwrap(),
            Regex::new(r"(\w*)(hive)s$").unwrap(),
            Regex::new(r"(\w*)(tive)s$").unwrap(),
            Regex::new(r"(\w*)([lr])ves$").unwrap(),
            Regex::new(r"(\w*)([^aeiouy]|qu)ies$").unwrap(),
            Regex::new(r"(s)eries$").unwrap(),
            Regex::new(r"(m)ovies$").unwrap(),
            Regex::new(r"(\w*)(x|ch|ss|sh)es$").unwrap(),
            Regex::new(r"(m|l)ice$").unwrap(),
            Regex::new(r"(bus)(es)?$").unwrap(),
            Regex::new(r"(shoe)s$").unwrap(),
            Regex::new(r"(cris|test)(is|es)$").unwrap(),
            Regex::new(r"^(a)x[ie]s$").unwrap(),
            Regex::new(r"(octop|vir)(us|i)$").unwrap(),
            Regex::new(r"(alias|status)(es)?$").unwrap(),
            Regex::new(r"^(ox)en").unwrap(),
            Regex::new(r"(vert|ind)ices$").unwrap(),
            Regex::new(r"(matr)ices$").unwrap(),
            Regex::new(r"(quiz)zes$").unwrap(),
            Regex::new(r"(database)s$").unwrap(),
            Regex::new(r"(\w*)s$").unwrap(),
            Regex::new(r"(\w*)(ss)$").unwrap()
        ];

    }
<commit_msg>Refactored to_singular to be cleaner<commit_after>use regex::Regex;
use regex::{Captures};
use string::constants::UNACCONTABLE_WORDS;

const SINGULAR_RULES: [&'static str; 27] = [
    "ews",
    "",
    "um",
    "sis",
    "sis",
    "fe",
    "",
    "",
    "f",
    "y",
    "eries",
    "ovie",
    "",
    "ouse",
    "",
    "",
    "is",
    "xis",
    "us",
    "",
    "",
    "ex",
    "ix",
    "",
    "",
    "",
    ""
];

/// Converts a `String` to singularized `String`
///
/// #Examples
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // singularize_foo_bars_to_foo_bar() {
///     let mock_string: String = "foo_bars".to_string();
///     let expected_string: String = "foo_bar".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // singularizes_oxen_to_ox() {
///     let mock_string: String = "oxen".to_string();
///     let expected_string: String = "ox".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // singularizes_crates_to_crate() {
///     let mock_string: String = "crates".to_string();
///     let expected_string: String = "crate".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // singularizes_boxes_to_box() {
///     let mock_string: String = "boxes".to_string();
///     let expected_string: String = "box".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // does_not_singularize_vengeance {
///     let mock_string: String = "vengeance".to_string();
///     let expected_string: String = "vengeance".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
/// ```
/// use inflector::string::singularize::to_singular;
///
///
/// // does_not_singularize_yoga() {
///     let mock_string: String = "yoga".to_string();
///     let expected_string: String = "yoga".to_string();
///     let asserted_string: String = to_singular(mock_string);
///     assert!(asserted_string == expected_string);
///
/// ```
///
pub fn to_singular<'a>(non_singular_string: String) -> String {
    if UNACCONTABLE_WORDS.contains(&non_singular_string.as_ref()) {
        return non_singular_string;
    } else {
        let safe_out_string: String = non_singular_string.to_string();
        let maybe_regex_match: Option<String> = singularize_string(non_singular_string);
        return maybe_regex_match.unwrap_or(safe_out_string);
    }
}

    fn singularize_string<'a>(non_singular_string: String) -> Option<String> {
        let singular_regexes: [Regex; 27] = singular_rules_regexes();
        for singular_index in 0..singular_regexes.len() {
            let maybe_match: Option<String> = singularize_string_from_capture_groups_if_match(
                non_singular_string.to_string(), singular_index.clone());

            if maybe_match.is_some() {
                return Some(maybe_match.unwrap_or(non_singular_string));
            }
        }
        return None;
    }

        fn singularize_string_from_capture_groups_if_match<'a>(non_singular_string: String,
                                                        singular_index: usize) -> Option<String> {
            let singular_regexes: [Regex; 27] = singular_rules_regexes();

            if singular_regexes[singular_index].is_match(&non_singular_string.as_ref()) {

                let cap: Captures = singular_regexes[singular_index].captures(
                    &non_singular_string.as_ref()).unwrap();

                return Some(singularize_string_from_capture_groups(cap, singular_index));

            } else {
                return None;
            }
        }

            fn singularize_string_from_capture_groups<'a>(capture_groups: Captures,
                                                            singular_index: usize) -> String {
                if capture_groups.len() > 2 {

                    return format!("{}{}{}",
                                capture_groups.at(1).unwrap_or(""),
                                capture_groups.at(2).unwrap_or(""),
                                SINGULAR_RULES[singular_index]);
                } else {

                    return format!("{}{}",
                                capture_groups.at(1).unwrap_or(""),
                                SINGULAR_RULES[singular_index]);
                }
            }

            fn singular_rules_regexes<'a>() -> [Regex; 27] {
                return [
                    Regex::new(r"(n)ews$").unwrap(),
                    Regex::new(r"(\w*)(o)es$").unwrap(),
                    Regex::new(r"(\w*)([ti])a$").unwrap(),
                    Regex::new(r"((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$").unwrap(),
                    Regex::new(r"(^analy)(sis|ses)$").unwrap(),
                    Regex::new(r"(\w*)([^f])ves$").unwrap(),
                    Regex::new(r"(\w*)(hive)s$").unwrap(),
                    Regex::new(r"(\w*)(tive)s$").unwrap(),
                    Regex::new(r"(\w*)([lr])ves$").unwrap(),
                    Regex::new(r"(\w*)([^aeiouy]|qu)ies$").unwrap(),
                    Regex::new(r"(s)eries$").unwrap(),
                    Regex::new(r"(m)ovies$").unwrap(),
                    Regex::new(r"(\w*)(x|ch|ss|sh)es$").unwrap(),
                    Regex::new(r"(m|l)ice$").unwrap(),
                    Regex::new(r"(bus)(es)?$").unwrap(),
                    Regex::new(r"(shoe)s$").unwrap(),
                    Regex::new(r"(cris|test)(is|es)$").unwrap(),
                    Regex::new(r"^(a)x[ie]s$").unwrap(),
                    Regex::new(r"(octop|vir)(us|i)$").unwrap(),
                    Regex::new(r"(alias|status)(es)?$").unwrap(),
                    Regex::new(r"^(ox)en").unwrap(),
                    Regex::new(r"(vert|ind)ices$").unwrap(),
                    Regex::new(r"(matr)ices$").unwrap(),
                    Regex::new(r"(quiz)zes$").unwrap(),
                    Regex::new(r"(database)s$").unwrap(),
                    Regex::new(r"(\w*)s$").unwrap(),
                    Regex::new(r"(\w*)(ss)$").unwrap()
                ];

            }

#[test]
fn singularize_string_if_a_regex_will_match() {
    let expected_string: String = "oxen".to_string();
    let asserted_string: Option<String> = singularize_string(
        expected_string);

    assert!(asserted_string.is_some());

}

#[test]
fn singularize_string_returns_none_option_if_no_match() {
    let expected_string: String = "bacon".to_string();
    let asserted_string: Option<String> = singularize_string(
        expected_string);

    assert!(asserted_string.is_none());

}

#[test]
fn singularize_string_from_capture_groups_if_match_should_return_a_string_if_a_match() {
    let expected_string: String = "oxen".to_string();
    let singular_index: usize = 20;
    let asserted_string: Option<String> = singularize_string_from_capture_groups_if_match(
        expected_string, singular_index);

    assert!(asserted_string.is_some());

}

#[test]
fn singularize_string_from_capture_groups_if_match_should_return_an_option_none_if_no_match() {
    let expected_string: String = "bacon".to_string();
    let singular_index: usize = 20;
    let asserted_string: Option<String> = singularize_string_from_capture_groups_if_match(
        expected_string, singular_index);

    assert!(asserted_string.is_none());

}

#[test]
fn singularize_string_from_capture_groups_should_return_a_singularized_string() {
    let expected_string: String = "ox".to_string();
    let test_string: String = "oxen".to_string();
    let singular_regexes: [Regex; 27] = singular_rules_regexes();
    let singular_index: usize = 20;
    let cap: Captures = singular_regexes[singular_index].captures(
        &test_string.as_ref()).unwrap();
    let asserted_string: String = singularize_string_from_capture_groups(cap,singular_index);
    assert!(expected_string == asserted_string);

}

#[test]
fn singular_rules_regexes_should_return_27_regex() {
    let regexes: [Regex; 27] = singular_rules_regexes();
    assert!(regexes.len() == 27);
}

<|endoftext|>