<commit_before>use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::path::Path;

/// Build configuration. See [CFG].
pub struct Cfg<'a> {
    pub include_prefix: &'a str,
    // Not implemented yet. https://github.com/dtolnay/cxx/issues/417
    #[doc(hidden)]
    pub exported_header_dirs: Vec<&'a Path>,
    #[doc(hidden)]
    pub exported_header_prefixes: Vec<&'a str>,
    #[doc(hidden)]
    pub exported_header_links: Vec<&'a str>,
    marker: PhantomData<*const ()>, // !Send + !Sync
}

/// Global configuration of the current build.
///
///

///
///
&str

///
/// ## **`CFG.include_prefix`**
///
/// The prefix at which C++ code from your crate as well as directly dependent
/// crates can access the code generated during this build.
///
/// By default, the `include_prefix` is equal to the name of the current crate.
/// That means if your crate is called `demo` and has Rust source files in a
/// *src/* directory and maybe some handwritten C++ header files in an
/// *include/* directory, then the current crate as well as downstream crates
/// might include them as follows:
///
/// ```
/// # const _: &str = stringify! {
///   // include one of the handwritten headers:
/// #include "demo/include/wow.h"
///
///   // include a header generated from Rust cxx::bridge:
/// #include "demo/src/lib.rs.h"
/// # };
/// ```
///
/// By modifying `CFG.include_prefix` we can substitute a prefix that is
/// different from the crate name if desired. Here we'll change it to
/// `"path/to"` which will make import paths take the form
/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
///     CFG.include_prefix = "path/to";
///
///     cxx_build::bridge("src/lib.rs")
///         .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"`
///         /* ... */
///         .compile("demo");
/// }
/// ```
///
/// Note that cross-crate imports are only made available between **direct
/// dependencies**. Another crate must directly depend on your crate in order to
/// #include its headers; a transitive dependency is not sufficient.
/// Additionally, headers from a direct dependency are only importable if the
/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers
/// will not be importable from outside of the same crate.
///
///

///
///
Vec<&Path>

///
/// ## **`CFG.exported_header_dirs`**
///
/// A vector of absolute paths. The current crate, directly dependent crates,
/// and further crates to which this crate's headers are exported (see below)
/// will be able to `#include` headers from these directories.
///
/// Adding a directory to `exported_header_dirs` is similar to adding it to the
/// current build via the `cc` crate's [`Build::include`][cc::Build::include],
/// but *also* makes the directory available to downstream crates that want to
/// `#include` one of the headers from your crate. If the dir were added only
/// using `Build::include`, the downstream crate including your header would
/// need to manually add the same directory to their own build as well.
///
/// When using `exported_header_dirs`, your crate must also set a `links` key
/// for itself in Cargo.toml. See [*the `links` manifest key*][links]. The
/// reason is that Cargo imposes no ordering on the execution of build scripts
/// without a `links` key, which means the downstream crate's build script might
/// execute before yours decides what to put into `exported_header_dirs`.
///
/// [links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
///
/// ### Example
///
/// One of your crate's headers wants to include a system library, such as
/// `#include "Python.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
/// use std::path::PathBuf;
///
/// fn main() {
///     let python3 = pkg_config::probe_library("python3").unwrap();
///     let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
///     CFG.exported_header_dirs.extend(python_include_paths);
///
///     cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
///
/// ### Example
///
/// Your crate wants to rearrange the headers that it exports vs how they're
/// laid out locally inside the crate's source directory.
///
/// Suppose the crate as published contains a file at `./include/myheader.h` but
/// wants it available to downstream crates as `#include "foo/v1/public.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
/// use std::path::Path;
/// use std::{env, fs};
///
/// fn main() {
///     let out_dir = env::var_os("OUT_DIR").unwrap();
///     let headers = Path::new(&out_dir).join("headers");
///     CFG.exported_header_dirs.push(&headers);
///
///     // We contain `include/myheader.h` locally, but
///     // downstream will use `#include "foo/v1/public.h"`
///     let foo = headers.join("foo").join("v1");
///     fs::create_dir_all(&foo).unwrap();
///     fs::copy("include/myheader.h", foo.join("public.h")).unwrap();
///
///     cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
///
///

///
///
Vec<&str>

///
/// ## **`CFG.exported_header_prefixes`**
///
/// Vector of strings. These each refer to the `include_prefix` of one of your
/// direct dependencies, or a prefix thereof. They describe which of your
/// dependencies participate in your crate's C++ public API, as opposed to
/// private use by your crate's implementation.
///
/// As a general rule, if one of your headers `#include`s something from one of
/// your dependencies, you need to put that dependency's `include_prefix` into
/// `CFG.exported_header_prefixes` (*or* their `links` key into
/// `CFG.exported_header_links`; see below). On the other hand if only your C++
/// implementation files and *not* your headers are importing from the
/// dependency, you do not export that dependency.
///
/// The significance of exported headers is that if downstream code (crate 𝒜)
/// contains an `#include` of a header from your crate (ℬ) and your header
/// contains an `#include` of something from your dependency (𝒞), the exported
/// dependency 𝒞 becomes available during the downstream crate 𝒜's build.
/// Otherwise the downstream crate 𝒜 doesn't know about 𝒞 and wouldn't be able
/// to find what header your header is referring to, and would fail to build.
///
/// When using `exported_header_prefixes`, your crate must also set a `links`
/// key for itself in Cargo.toml.
///
/// ### Example
///
/// Suppose you have a crate with 5 direct dependencies and the `include_prefix`
/// for each one are:
///
/// - "crate0"
/// - "group/api/crate1"
/// - "group/api/crate2"
/// - "group/api/contrib/crate3"
/// - "detail/crate4"
///
/// Your header involves types from the first four so we re-export those as part
/// of your public API, while crate4 is only used internally by your cc file not
/// your header, so we do not export:
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
///     CFG.exported_header_prefixes = vec!["crate0", "group/api"];
///
///     cxx_build::bridge("src/bridge.rs")
///         .file("src/impl.cc")
///         .compile("demo");
/// }
/// ```
///
///

///
///
Vec<&str>

///
/// ## **`CFG.exported_header_links`**
///
/// Vector of strings. These each refer to the `links` attribute ([*the `links`
/// manifest key*][links]) of one of your crate's direct dependencies.
///
/// This achieves an equivalent result to `CFG.exported_header_prefixes` by
/// re-exporting a dependency as part of your crate's public API, except with
/// finer grained control for cases when multiple crates might be sharing the
/// same `include_prefix` and you'd like to export some but not others. Links
/// attributes are guaranteed to be unique identifiers by Cargo.
///
/// When using `exported_header_links`, your crate must also set a `links` key
/// for itself in Cargo.toml.
///
/// ### Example
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
///     CFG.exported_header_links.push("git2");
///
///     cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
#[cfg(doc)]
pub static mut CFG: Cfg = Cfg {
    include_prefix: "",
    exported_header_dirs: Vec::new(),
    exported_header_prefixes: Vec::new(),
    exported_header_links: Vec::new(),
    marker: PhantomData,
};

impl<'a> Debug for Cfg<'a> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter
            .debug_struct("Cfg")
            .field("include_prefix", &self.include_prefix)
            .finish()
    }
}

#[cfg(not(doc))]
pub use self::r#impl::Cfg::CFG;

#[cfg(not(doc))]
mod r#impl {
    use crate::intern::{intern, InternedString};
    use crate::vec::{self, InternedVec as _};
    use lazy_static::lazy_static;
    use std::cell::RefCell;
    use std::collections::HashMap;
    use std::fmt::{self, Debug};
    use std::marker::PhantomData;
    use std::ops::{Deref, DerefMut};
    use std::sync::{PoisonError, RwLock};

    struct CurrentCfg {
        include_prefix: InternedString,
        exported_header_dirs: Vec<InternedString>,
        exported_header_prefixes: Vec<InternedString>,
        exported_header_links: Vec<InternedString>,
    }

    impl CurrentCfg {
        fn default() -> Self {
            let include_prefix = crate::env_os("CARGO_PKG_NAME")
                .map(|pkg| intern(&pkg.to_string_lossy()))
                .unwrap_or_default();
            let exported_header_dirs = Vec::new();
            let exported_header_prefixes = Vec::new();
            let exported_header_links = Vec::new();
            CurrentCfg {
                include_prefix,
                exported_header_dirs,
                exported_header_prefixes,
                exported_header_links,
            }
        }
    }

    lazy_static! {
        static ref CURRENT: RwLock<CurrentCfg> = RwLock::new(CurrentCfg::default());
    }

    thread_local! {
        // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
        // we can delete this thread local side table and instead make each CFG
        // instance directly own the associated super::Cfg.
        //
        //     #[allow(const_item_mutation)]
        //     pub const CFG: Cfg = Cfg {
        //         cfg: AtomicPtr::new(ptr::null_mut()),
        //     };
        //     pub struct Cfg {
        //         cfg: AtomicPtr<super::Cfg>,
        //     }
        //
        static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
    }

    #[derive(Eq, PartialEq, Hash)]
    struct Handle(*const Cfg<'static>);

    impl<'a> Cfg<'a> {
        fn current() -> super::Cfg<'a> {
            let current = CURRENT.read().unwrap_or_else(PoisonError::into_inner);
            let include_prefix = current.include_prefix.str();
            let exported_header_dirs = current.exported_header_dirs.vec();
            let exported_header_prefixes = current.exported_header_prefixes.vec();
            let exported_header_links = current.exported_header_links.vec();
            super::Cfg {
                include_prefix,
                exported_header_dirs,
                exported_header_prefixes,
                exported_header_links,
                marker: PhantomData,
            }
        }

        const fn handle(self: &Cfg<'a>) -> Handle {
            Handle(<*const Cfg>::cast(self))
        }
    }

    // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
    // drop on the same thread where they were created.
    pub enum Cfg<'a> {
        Mut(super::Cfg<'a>),
        CFG,
    }

    impl<'a> Debug for Cfg<'a> {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            if let Cfg::Mut(cfg) = self {
                Debug::fmt(cfg, formatter)
            } else {
                Debug::fmt(&Cfg::current(), formatter)
            }
        }
    }

    impl<'a> Deref for Cfg<'a> {
        type Target = super::Cfg<'a>;

        fn deref(&self) -> &Self::Target {
            if let Cfg::Mut(cfg) = self {
                cfg
            } else {
                let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
                    &mut **derefs
                        .borrow_mut()
                        .entry(self.handle())
                        .or_insert_with(|| Box::new(Cfg::current()))
                });
                unsafe { &mut *cfg }
            }
        }
    }

    impl<'a> DerefMut for Cfg<'a> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            if let Cfg::CFG = self {
                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
                *self = Cfg::Mut(Cfg::current());
            }
            match self {
                Cfg::Mut(cfg) => cfg,
                Cfg::CFG => unreachable!(),
            }
        }
    }

    impl<'a> Drop for Cfg<'a> {
        fn drop(&mut self) {
            if let Cfg::Mut(cfg) = self {
                let mut current = CURRENT.write().unwrap_or_else(PoisonError::into_inner);
                current.include_prefix = intern(cfg.include_prefix);
                current.exported_header_dirs = vec::intern(&cfg.exported_header_dirs);
                current.exported_header_prefixes = vec::intern(&cfg.exported_header_prefixes);
                current.exported_header_links = vec::intern(&cfg.exported_header_links);
            } else {
                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
            }
        }
    }
}
<commit_msg>Link Cfg fields to their explanation in CFG<commit_after>use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::path::Path;

/// Build configuration. See [CFG].
pub struct Cfg<'a> {
    /// See [`CFG.include_prefix`][CFG#cfginclude_prefix].
    pub include_prefix: &'a str,
    /// See [`CFG.exported_header_dirs`][CFG#cfgexported_header_dirs].
    pub exported_header_dirs: Vec<&'a Path>,
    /// See [`CFG.exported_header_prefixes`][CFG#cfgexported_header_prefixes].
    pub exported_header_prefixes: Vec<&'a str>,
    /// See [`CFG.exported_header_links`][CFG#cfgexported_header_links].
    pub exported_header_links: Vec<&'a str>,
    marker: PhantomData<*const ()>, // !Send + !Sync
}

/// Global configuration of the current build.
///
///

///
///
&str

///
/// ## **`CFG.include_prefix`**
///
/// The prefix at which C++ code from your crate as well as directly dependent
/// crates can access the code generated during this build.
///
/// By default, the `include_prefix` is equal to the name of the current crate.
/// That means if your crate is called `demo` and has Rust source files in a
/// *src/* directory and maybe some handwritten C++ header files in an
/// *include/* directory, then the current crate as well as downstream crates
/// might include them as follows:
///
/// ```
/// # const _: &str = stringify! {
///   // include one of the handwritten headers:
/// #include "demo/include/wow.h"
///
///   // include a header generated from Rust cxx::bridge:
/// #include "demo/src/lib.rs.h"
/// # };
/// ```
///
/// By modifying `CFG.include_prefix` we can substitute a prefix that is
/// different from the crate name if desired. Here we'll change it to
/// `"path/to"` which will make import paths take the form
/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
///     CFG.include_prefix = "path/to";
///
///     cxx_build::bridge("src/lib.rs")
///         .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"`
///         /* ... */
///         .compile("demo");
/// }
/// ```
///
/// Note that cross-crate imports are only made available between **direct
/// dependencies**. Another crate must directly depend on your crate in order to
/// #include its headers; a transitive dependency is not sufficient.
/// Additionally, headers from a direct dependency are only importable if the
/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers
/// will not be importable from outside of the same crate.
///
///

///
///
Vec<&Path>

///
/// ## **`CFG.exported_header_dirs`**
///
/// A vector of absolute paths. The current crate, directly dependent crates,
/// and further crates to which this crate's headers are exported (see below)
/// will be able to `#include` headers from these directories.
///
/// Adding a directory to `exported_header_dirs` is similar to adding it to the
/// current build via the `cc` crate's [`Build::include`][cc::Build::include],
/// but *also* makes the directory available to downstream crates that want to
/// `#include` one of the headers from your crate. If the dir were added only
/// using `Build::include`, the downstream crate including your header would
/// need to manually add the same directory to their own build as well.
///
/// When using `exported_header_dirs`, your crate must also set a `links` key
/// for itself in Cargo.toml. See [*the `links` manifest key*][links]. The
/// reason is that Cargo imposes no ordering on the execution of build scripts
/// without a `links` key, which means the downstream crate's build script might
/// execute before yours decides what to put into `exported_header_dirs`.
///
/// [links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
///
/// ### Example
///
/// One of your crate's headers wants to include a system library, such as
/// `#include "Python.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
/// use std::path::PathBuf;
///
/// fn main() {
///     let python3 = pkg_config::probe_library("python3").unwrap();
///     let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
///     CFG.exported_header_dirs.extend(python_include_paths);
///
///     cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
///
/// ### Example
///
/// Your crate wants to rearrange the headers that it exports vs how they're
/// laid out locally inside the crate's source directory.
///
/// Suppose the crate as published contains a file at `./include/myheader.h` but
/// wants it available to downstream crates as `#include "foo/v1/public.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
/// use std::path::Path;
/// use std::{env, fs};
///
/// fn main() {
///     let out_dir = env::var_os("OUT_DIR").unwrap();
///     let headers = Path::new(&out_dir).join("headers");
///     CFG.exported_header_dirs.push(&headers);
///
///     // We contain `include/myheader.h` locally, but
///     // downstream will use `#include "foo/v1/public.h"`
///     let foo = headers.join("foo").join("v1");
///     fs::create_dir_all(&foo).unwrap();
///     fs::copy("include/myheader.h", foo.join("public.h")).unwrap();
///
///     cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
///
///

///
///
Vec<&str>

///
/// ## **`CFG.exported_header_prefixes`**
///
/// Vector of strings. These each refer to the `include_prefix` of one of your
/// direct dependencies, or a prefix thereof. They describe which of your
/// dependencies participate in your crate's C++ public API, as opposed to
/// private use by your crate's implementation.
///
/// As a general rule, if one of your headers `#include`s something from one of
/// your dependencies, you need to put that dependency's `include_prefix` into
/// `CFG.exported_header_prefixes` (*or* their `links` key into
/// `CFG.exported_header_links`; see below). On the other hand if only your C++
/// implementation files and *not* your headers are importing from the
/// dependency, you do not export that dependency.
///
/// The significance of exported headers is that if downstream code (crate 𝒜)
/// contains an `#include` of a header from your crate (ℬ) and your header
/// contains an `#include` of something from your dependency (𝒞), the exported
/// dependency 𝒞 becomes available during the downstream crate 𝒜's build.
/// Otherwise the downstream crate 𝒜 doesn't know about 𝒞 and wouldn't be able
/// to find what header your header is referring to, and would fail to build.
///
/// When using `exported_header_prefixes`, your crate must also set a `links`
/// key for itself in Cargo.toml.
///
/// ### Example
///
/// Suppose you have a crate with 5 direct dependencies and the `include_prefix`
/// for each one are:
///
/// - "crate0"
/// - "group/api/crate1"
/// - "group/api/crate2"
/// - "group/api/contrib/crate3"
/// - "detail/crate4"
///
/// Your header involves types from the first four so we re-export those as part
/// of your public API, while crate4 is only used internally by your cc file not
/// your header, so we do not export:
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
///     CFG.exported_header_prefixes = vec!["crate0", "group/api"];
///
///     cxx_build::bridge("src/bridge.rs")
///         .file("src/impl.cc")
///         .compile("demo");
/// }
/// ```
///
///

///
///
Vec<&str>

///
/// ## **`CFG.exported_header_links`**
///
/// Vector of strings. These each refer to the `links` attribute ([*the `links`
/// manifest key*][links]) of one of your crate's direct dependencies.
///
/// This achieves an equivalent result to `CFG.exported_header_prefixes` by
/// re-exporting a dependency as part of your crate's public API, except with
/// finer grained control for cases when multiple crates might be sharing the
/// same `include_prefix` and you'd like to export some but not others. Links
/// attributes are guaranteed to be unique identifiers by Cargo.
///
/// When using `exported_header_links`, your crate must also set a `links` key
/// for itself in Cargo.toml.
///
/// ### Example
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
///     CFG.exported_header_links.push("git2");
///
///     cxx_build::bridge("src/bridge.rs").compile("demo");
/// }
/// ```
#[cfg(doc)]
pub static mut CFG: Cfg = Cfg {
    include_prefix: "",
    exported_header_dirs: Vec::new(),
    exported_header_prefixes: Vec::new(),
    exported_header_links: Vec::new(),
    marker: PhantomData,
};

impl<'a> Debug for Cfg<'a> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter
            .debug_struct("Cfg")
            .field("include_prefix", &self.include_prefix)
            .finish()
    }
}

#[cfg(not(doc))]
pub use self::r#impl::Cfg::CFG;

#[cfg(not(doc))]
mod r#impl {
    use crate::intern::{intern, InternedString};
    use crate::vec::{self, InternedVec as _};
    use lazy_static::lazy_static;
    use std::cell::RefCell;
    use std::collections::HashMap;
    use std::fmt::{self, Debug};
    use std::marker::PhantomData;
    use std::ops::{Deref, DerefMut};
    use std::sync::{PoisonError, RwLock};

    struct CurrentCfg {
        include_prefix: InternedString,
        exported_header_dirs: Vec<InternedString>,
        exported_header_prefixes: Vec<InternedString>,
        exported_header_links: Vec<InternedString>,
    }

    impl CurrentCfg {
        fn default() -> Self {
            let include_prefix = crate::env_os("CARGO_PKG_NAME")
                .map(|pkg| intern(&pkg.to_string_lossy()))
                .unwrap_or_default();
            let exported_header_dirs = Vec::new();
            let exported_header_prefixes = Vec::new();
            let exported_header_links = Vec::new();
            CurrentCfg {
                include_prefix,
                exported_header_dirs,
                exported_header_prefixes,
                exported_header_links,
            }
        }
    }

    lazy_static! {
        static ref CURRENT: RwLock<CurrentCfg> = RwLock::new(CurrentCfg::default());
    }

    thread_local! {
        // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
        // we can delete this thread local side table and instead make each CFG
        // instance directly own the associated super::Cfg.
        //
        //     #[allow(const_item_mutation)]
        //     pub const CFG: Cfg = Cfg {
        //         cfg: AtomicPtr::new(ptr::null_mut()),
        //     };
        //     pub struct Cfg {
        //         cfg: AtomicPtr<super::Cfg>,
        //     }
        //
        static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
    }

    #[derive(Eq, PartialEq, Hash)]
    struct Handle(*const Cfg<'static>);

    impl<'a> Cfg<'a> {
        fn current() -> super::Cfg<'a> {
            let current = CURRENT.read().unwrap_or_else(PoisonError::into_inner);
            let include_prefix = current.include_prefix.str();
            let exported_header_dirs = current.exported_header_dirs.vec();
            let exported_header_prefixes = current.exported_header_prefixes.vec();
            let exported_header_links = current.exported_header_links.vec();
            super::Cfg {
                include_prefix,
                exported_header_dirs,
                exported_header_prefixes,
                exported_header_links,
                marker: PhantomData,
            }
        }

        const fn handle(self: &Cfg<'a>) -> Handle {
            Handle(<*const Cfg>::cast(self))
        }
    }

    // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
    // drop on the same thread where they were created.
    pub enum Cfg<'a> {
        Mut(super::Cfg<'a>),
        CFG,
    }

    impl<'a> Debug for Cfg<'a> {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            if let Cfg::Mut(cfg) = self {
                Debug::fmt(cfg, formatter)
            } else {
                Debug::fmt(&Cfg::current(), formatter)
            }
        }
    }

    impl<'a> Deref for Cfg<'a> {
        type Target = super::Cfg<'a>;

        fn deref(&self) -> &Self::Target {
            if let Cfg::Mut(cfg) = self {
                cfg
            } else {
                let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
                    &mut **derefs
                        .borrow_mut()
                        .entry(self.handle())
                        .or_insert_with(|| Box::new(Cfg::current()))
                });
                unsafe { &mut *cfg }
            }
        }
    }

    impl<'a> DerefMut for Cfg<'a> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            if let Cfg::CFG = self {
                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
                *self = Cfg::Mut(Cfg::current());
            }
            match self {
                Cfg::Mut(cfg) => cfg,
                Cfg::CFG => unreachable!(),
            }
        }
    }

    impl<'a> Drop for Cfg<'a> {
        fn drop(&mut self) {
            if let Cfg::Mut(cfg) = self {
                let mut current = CURRENT.write().unwrap_or_else(PoisonError::into_inner);
                current.include_prefix = intern(cfg.include_prefix);
                current.exported_header_dirs = vec::intern(&cfg.exported_header_dirs);
                current.exported_header_prefixes = vec::intern(&cfg.exported_header_prefixes);
                current.exported_header_links = vec::intern(&cfg.exported_header_links);
            } else {
                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
            }
        }
    }
}
<|endoftext|>


<commit_before>// Suppress all warnings from casts which overflow.
#![allow(overflowing_literals)]

fn main() {
    let decimal = 65.4321_f32;

    // Error! No implicit conversion
    let integer: u8 = decimal;
    // FIXME ^ Comment out this line

    // Explicit conversion
    let integer = decimal as u8;
    let character = integer as char;

    println!("Casting: {} -> {} -> {}", decimal, integer, character);

    // when casting any value to an unsigned type, T,
    // std::T::MAX + 1 is added or subtracted until the value
    // fits into the new type

    // 1000 already fits in a u16
    println!("1000 as a u16 is: {}", 1000 as u16);

    // 1000 - 256 - 256 - 256 = 232
    println!("1000 as a u8 is : {}", 1000 as u8);
    // -1 + 256 = 255
    println!("  -1 as a u8 is : {}", (-1i8) as u8);

    // For positive numbers, this is the same as the modulus
    println!("1000 mod 256 is : {}", 1000 % 256);

    // When casting to a signed type, the result is the same as
    // first casting to the corresponding unsigned type then
    // taking the two's complement.

    // Unless it already fits, of course.
    println!(" 128 as a i16 is: {}", 128 as i16);
    // 128 as u8 -> 128, whose two's complement in eight bits is:
    println!(" 128 as a i8 is : {}", 128 as i8);

    // repeating the example above
    // 1000 as u8 -> 232
    println!("1000 as a i8 is : {}", 1000 as i8);
    // and the two's complement of 232 is -24
    println!(" 232 as a i8 is : {}", 232 as i8);

}
<commit_msg>Update cast.rs<commit_after>// Suppress all warnings from casts which overflow.
#![allow(overflowing_literals)]

fn main() {
    let decimal = 65.4321_f32;

    // Error! No implicit conversion
    let integer: u8 = decimal;
    // FIXME ^ Comment out this line

    // Explicit conversion
    let integer = decimal as u8;
    let character = integer as char;

    println!("Casting: {} -> {} -> {}", decimal, integer, character);

    // when casting any value to an unsigned type, T,
    // std::T::MAX + 1 is added or subtracted until the value
    // fits into the new type

    // 1000 already fits in a u16
    println!("1000 as a u16 is: {}", 1000 as u16);

    // 1000 - 256 - 256 - 256 = 232
    // Basically, it just uses the 8 bits from the least significant bit, and truncates the MSB
    println!("1000 as a u8 is : {}", 1000 as u8);
    // -1 + 256 = 255
    println!("  -1 as a u8 is : {}", (-1i8) as u8);

    // For positive numbers, this is the same as the modulus
    println!("1000 mod 256 is : {}", 1000 % 256);

    // When casting to a signed type, the result is the same as
    // first casting to the corresponding unsigned type then
    // taking the two's complement.

    // Unless it already fits, of course.
    println!(" 128 as a i16 is: {}", 128 as i16);
    // 128 as u8 -> 128, whose two's complement in eight bits is:
    println!(" 128 as a i8 is : {}", 128 as i8);

    // repeating the example above
    // 1000 as u8 -> 232
    println!("1000 as a i8 is : {}", 1000 as i8);
    // and the two's complement of 232 is -24
    println!(" 232 as a i8 is : {}", 232 as i8);

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate qmlrs;

struct Factorial;
impl Factorial {
    fn calculate(&self, x: i64) -> i64 {
        (1..x+1).fold(1, |t,c| t * c)
    }
}

Q_OBJECT! { Factorial:
    slot fn calculate(i64);
//    signal fn test();
}

fn main() {
    let engine = qmlrs::Engine::new();

    engine.set_property("factorial", Factorial);
    engine.load_local_file("examples/factorial_ui.qml");

    engine.exec();
}
<commit_msg>The Engine must be mutable, doh :-(<commit_after>#[macro_use]
extern crate qmlrs;

struct Factorial;
impl Factorial {
    fn calculate(&self, x: i64) -> i64 {
        (1..x+1).fold(1, |t,c| t * c)
    }
}

Q_OBJECT! { Factorial:
    slot fn calculate(i64);
//    signal fn test();
}

fn main() {
    let mut engine = qmlrs::Engine::new();

    engine.set_property("factorial", Factorial);
    engine.load_local_file("examples/factorial_ui.qml");

    engine.exec();
}
<|endoftext|>


<commit_before>use reqwest::blocking::Client;
use serde_json::{Map, Number, Value};
use std::env::args;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

/// Read individual lines from a file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
    P: AsRef<Path>,
{
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

/// Log the url to view the trace in the browser.
fn log_web_url(jaeger_web_ui_url: &str, trace_id: &str) {
    println!(
        "Jaeger trace will be available on {}/trace/{}",
        jaeger_web_ui_url.to_string(),
        trace_id.to_string()
    )
}

/// Send trace JSON to Jaeger using ZipKin API.
fn send_json_to_zipkin(zipkin_api: &str, value: String) {
    let client = Client::new();

    let res = client
        .post(zipkin_api)
        .header("Content-Type", "application/json")
        .body(value)
        .send()
        .expect("Failed to send request");

    println!("body = {:?}", res.text());
}

fn main() {
    let service_name = "nextjs";
    let ipv4 = "127.0.0.1";
    let port = 9411;
    let zipkin_url = format!("http://{}:{}", ipv4, port);
    let jaeger_web_ui_url = format!("http://{}:16686", ipv4);
    let zipkin_api = format!("{}/api/v2/spans", zipkin_url);
    let mut logged_url = false;

    let mut local_endpoint = Map::new();
    local_endpoint.insert(
        "serviceName".to_string(),
        Value::String(service_name.to_string()),
    );
    local_endpoint.insert("ipv4".to_string(), Value::String(ipv4.to_string()));
    local_endpoint.insert("port".to_string(), Value::Number(Number::from(port)));

    let first_arg = args().nth(1).expect("Please provide a file name");

    if let Ok(lines) = read_lines(first_arg) {
        for line in lines {
            if let Ok(json_to_parse) = line {
                let v = match serde_json::from_str::<Vec<Value>>(&json_to_parse) {
                    Ok(v) => v
                        .into_iter()
                        .map(|mut data| {
                            if !logged_url {
                                log_web_url(&jaeger_web_ui_url, &data["traceId"].to_string());
                                logged_url = true;
                            }
                            data["localEndpoint"] = Value::Object(local_endpoint.clone());
                            data
                        })
                        .collect::<Value>(),
                    Err(e) => {
                        println!("{}", e);
                        continue;
                    }
                };

                let json_map = serde_json::to_string(&v).expect("Failed to serialize");

                send_json_to_zipkin(&zipkin_api, json_map);
            }
        }
    }
}
<commit_msg>Update send-to-jaeger URL output (#31563)<commit_after>use reqwest::blocking::Client;
use serde_json::{Map, Number, Value};
use std::env::args;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

/// Read individual lines from a file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
    P: AsRef<Path>,
{
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

/// Log the url to view the trace in the browser.
fn log_web_url(jaeger_web_ui_url: &str, trace_id: &str) {
    println!(
        "Jaeger trace will be available on {}/trace/{}",
        jaeger_web_ui_url.to_string(),
        trace_id.to_string()
    )
}

/// Send trace JSON to Jaeger using ZipKin API.
fn send_json_to_zipkin(zipkin_api: &str, value: String) {
    let client = Client::new();

    let res = client
        .post(zipkin_api)
        .header("Content-Type", "application/json")
        .body(value)
        .send()
        .expect("Failed to send request");

    println!("body = {:?}", res.text());
}

fn main() {
    let service_name = "nextjs";
    let ipv4 = "127.0.0.1";
    let port = 9411;
    let zipkin_url = format!("http://{}:{}", ipv4, port);
    let jaeger_web_ui_url = format!("http://{}:16686", ipv4);
    let zipkin_api = format!("{}/api/v2/spans", zipkin_url);
    let mut logged_url = false;

    let mut local_endpoint = Map::new();
    local_endpoint.insert(
        "serviceName".to_string(),
        Value::String(service_name.to_string()),
    );
    local_endpoint.insert("ipv4".to_string(), Value::String(ipv4.to_string()));
    local_endpoint.insert("port".to_string(), Value::Number(Number::from(port)));

    let first_arg = args().nth(1).expect("Please provide a file name");

    if let Ok(lines) = read_lines(first_arg) {
        for line in lines {
            if let Ok(json_to_parse) = line {
                let v = match serde_json::from_str::<Vec<Value>>(&json_to_parse) {
                    Ok(v) => v
                        .into_iter()
                        .map(|mut data| {
                            if !logged_url {
                                log_web_url(&jaeger_web_ui_url, &data["traceId"].as_str().unwrap());
                                logged_url = true;
                            }
                            data["localEndpoint"] = Value::Object(local_endpoint.clone());
                            data
                        })
                        .collect::<Value>(),
                    Err(e) => {
                        println!("{}", e);
                        continue;
                    }
                };

                let json_map = serde_json::to_string(&v).expect("Failed to serialize");

                send_json_to_zipkin(&zipkin_api, json_map);
            }
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Add support for Dockerfiles filtering<commit_after><|endoftext|>


<commit_before>use beacon_chain::{
    fork_choice::BitwiseLMDGhost,
    slot_clock::SystemTimeSlotClock,
    store::{DiskStore, MemoryStore, Store},
    BeaconChain, BeaconChainTypes,
};
use fork_choice::ForkChoice;
use slog::{info, Logger};
use slot_clock::SlotClock;
use std::sync::Arc;
use tree_hash::TreeHash;
use types::{
    test_utils::TestingBeaconStateBuilder, BeaconBlock, EthSpec, Hash256, LighthouseTestnetEthSpec,
};

/// Provides a new, initialized `BeaconChain`
pub trait InitialiseBeaconChain<T: BeaconChainTypes> {
    fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T>;
}

/// A testnet-suitable BeaconChainType, using `MemoryStore`.
#[derive(Clone)]
pub struct TestnetMemoryBeaconChainTypes;

impl BeaconChainTypes for TestnetMemoryBeaconChainTypes {
    type Store = MemoryStore;
    type SlotClock = SystemTimeSlotClock;
    type ForkChoice = BitwiseLMDGhost<Self::Store, Self::EthSpec>;
    type EthSpec = LighthouseTestnetEthSpec;
}

impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetMemoryBeaconChainTypes {
    fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T> {
        maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store, log)
    }
}

/// A testnet-suitable BeaconChainType, using `DiskStore`.
#[derive(Clone)]
pub struct TestnetDiskBeaconChainTypes;

impl BeaconChainTypes for TestnetDiskBeaconChainTypes {
    type Store = DiskStore;
    type SlotClock = SystemTimeSlotClock;
    type ForkChoice = BitwiseLMDGhost<Self::Store, Self::EthSpec>;
    type EthSpec = LighthouseTestnetEthSpec;
}

impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetDiskBeaconChainTypes {
    fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T> {
        maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store, log)
    }
}

/// Loads a `BeaconChain` from `store`, if it exists. Otherwise, create a new chain from genesis.
fn maybe_load_from_store_for_testnet<T, U: Store, V: EthSpec>(
    store: Arc<U>,
    log: Logger,
) -> BeaconChain<T>
where
    T: BeaconChainTypes<Store = U>,
    T::ForkChoice: ForkChoice<U>,
{
    if let Ok(Some(beacon_chain)) = BeaconChain::from_store(store.clone()) {
        info!(
            log,
            "Loaded BeaconChain from store";
            "slot" => beacon_chain.state.read().slot,
            "best_slot" => beacon_chain.best_slot(),
        );

        beacon_chain
    } else {
        info!(log, "Initializing new BeaconChain from genesis");
        let spec = T::EthSpec::spec();

        let state_builder =
            TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(8, &spec);
        let (genesis_state, _keypairs) = state_builder.build();

        let mut genesis_block = BeaconBlock::empty(&spec);
        genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());

        // Slot clock
        let slot_clock = T::SlotClock::new(
            spec.genesis_slot,
            genesis_state.genesis_time,
            spec.seconds_per_slot,
        );
        // Choose the fork choice
        let fork_choice = T::ForkChoice::new(store.clone());

        // Genesis chain
        //TODO: Handle error correctly
        BeaconChain::from_genesis(
            store,
            slot_clock,
            genesis_state,
            genesis_block,
            spec.clone(),
            fork_choice,
        )
        .expect("Terminate if beacon chain generation fails")
    }
}
<commit_msg>Add constant for testnet validator count<commit_after>use beacon_chain::{
    fork_choice::BitwiseLMDGhost,
    slot_clock::SystemTimeSlotClock,
    store::{DiskStore, MemoryStore, Store},
    BeaconChain, BeaconChainTypes,
};
use fork_choice::ForkChoice;
use slog::{info, Logger};
use slot_clock::SlotClock;
use std::sync::Arc;
use tree_hash::TreeHash;
use types::{
    test_utils::TestingBeaconStateBuilder, BeaconBlock, EthSpec, Hash256, LighthouseTestnetEthSpec,
};

/// The number initial validators when starting the `LighthouseTestnet`.
const TESTNET_VALIDATOR_COUNT: usize = 16;

/// Provides a new, initialized `BeaconChain`
pub trait InitialiseBeaconChain<T: BeaconChainTypes> {
    fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T>;
}

/// A testnet-suitable BeaconChainType, using `MemoryStore`.
#[derive(Clone)]
pub struct TestnetMemoryBeaconChainTypes;

impl BeaconChainTypes for TestnetMemoryBeaconChainTypes {
    type Store = MemoryStore;
    type SlotClock = SystemTimeSlotClock;
    type ForkChoice = BitwiseLMDGhost<Self::Store, Self::EthSpec>;
    type EthSpec = LighthouseTestnetEthSpec;
}

impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetMemoryBeaconChainTypes {
    fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T> {
        maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store, log)
    }
}

/// A testnet-suitable BeaconChainType, using `DiskStore`.
#[derive(Clone)]
pub struct TestnetDiskBeaconChainTypes;

impl BeaconChainTypes for TestnetDiskBeaconChainTypes {
    type Store = DiskStore;
    type SlotClock = SystemTimeSlotClock;
    type ForkChoice = BitwiseLMDGhost<Self::Store, Self::EthSpec>;
    type EthSpec = LighthouseTestnetEthSpec;
}

impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetDiskBeaconChainTypes {
    fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T> {
        maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store, log)
    }
}

/// Loads a `BeaconChain` from `store`, if it exists. Otherwise, create a new chain from genesis.
fn maybe_load_from_store_for_testnet<T, U: Store, V: EthSpec>(
    store: Arc<U>,
    log: Logger,
) -> BeaconChain<T>
where
    T: BeaconChainTypes<Store = U>,
    T::ForkChoice: ForkChoice<U>,
{
    if let Ok(Some(beacon_chain)) = BeaconChain::from_store(store.clone()) {
        info!(
            log,
            "Loaded BeaconChain from store";
            "slot" => beacon_chain.state.read().slot,
            "best_slot" => beacon_chain.best_slot(),
        );

        beacon_chain
    } else {
        info!(log, "Initializing new BeaconChain from genesis");
        let spec = T::EthSpec::spec();

        let state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(
            TESTNET_VALIDATOR_COUNT,
            &spec,
        );
        let (genesis_state, _keypairs) = state_builder.build();

        let mut genesis_block = BeaconBlock::empty(&spec);
        genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());

        // Slot clock
        let slot_clock = T::SlotClock::new(
            spec.genesis_slot,
            genesis_state.genesis_time,
            spec.seconds_per_slot,
        );
        // Choose the fork choice
        let fork_choice = T::ForkChoice::new(store.clone());

        // Genesis chain
        //TODO: Handle error correctly
        BeaconChain::from_genesis(
            store,
            slot_clock,
            genesis_state,
            genesis_block,
            spec.clone(),
            fork_choice,
        )
        .expect("Terminate if beacon chain generation fails")
    }
}
<|endoftext|>


<commit_before>extern crate cursive;

use cursive::Cursive;
use cursive::traits::*;
use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView,
                     SelectView, TextView};

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(Dialog::new()
        .title("Please fill out this form")
        .button("Ok", |s| s.quit())
        .content(ListView::new()
            .child("Name", EditView::new().fixed_width(10))
            .child("Email",
                   LinearLayout::horizontal()
                       .child(EditView::new()
                           .disabled()
                           .with_id("email1")
                           .fixed_width(15))
                       .child(TextView::new("@"))
                       .child(EditView::new()
                           .disabled()
                           .with_id("email2")
                           .fixed_width(10)))
            .child("Receive spam?",
                   Checkbox::new()
                       .on_change(|s, checked| for name in &["email1",
                                                             "email2"] {
                           s.call_on_id(name, |view: &mut EditView| {
                               view.set_enabled(checked)
                           });
                       }))
            .delimiter()
            .child("Age",
                   SelectView::new()
                       .popup()
                       .item_str("0-18")
                       .item_str("19-30")
                       .item_str("31-40")
                       .item_str("41+"))
            .with(|list| for i in 0..50 {
                list.add_child(&format!("Item {}", i), EditView::new());
            })));

    siv.run();
}
<commit_msg>Add focus switch to list_view example<commit_after>extern crate cursive;

use cursive::Cursive;
use cursive::traits::*;
use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView,
                     SelectView, TextView};

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(Dialog::new()
        .title("Please fill out this form")
        .button("Ok", |s| s.quit())
        .content(ListView::new()
            .child("Name", EditView::new().fixed_width(10))
            .child("Receive spam?",
                   Checkbox::new()
                       .on_change(|s, checked| for name in &["email1",
                                                             "email2"] {
                           s.call_on_id(name, |view: &mut EditView| {
                               view.set_enabled(checked)
                           });
                           if checked {
                               s.focus_id("email1").unwrap();
                           }
                       }))
            .child("Email",
                   LinearLayout::horizontal()
                       .child(EditView::new()
                           .disabled()
                           .with_id("email1")
                           .fixed_width(15))
                       .child(TextView::new("@"))
                       .child(EditView::new()
                           .disabled()
                           .with_id("email2")
                           .fixed_width(10)))
            .delimiter()
            .child("Age",
                   SelectView::new()
                       .popup()
                       .item_str("0-18")
                       .item_str("19-30")
                       .item_str("31-40")
                       .item_str("41+"))
            .with(|list| for i in 0..50 {
                list.add_child(&format!("Item {}", i), EditView::new());
            })));

    siv.run();
}
<|endoftext|>


<commit_before>use hyper::Client;
use hyper::header::{Headers, Accept, qitem};
use hyper::client::Response as HyperResponse;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use std::io::Read;
use serde_json;

use Stream;
use types::Result;
use error::HesError;
use error::UserErrorKind;
use connection::ConnectionInfo;
use api::ESResolveLinkTos;

pub struct Reader<'a> {
    connection_info: &'a ConnectionInfo,
}

impl<'a> Reader<'a> {
    pub fn new(connection_info: &'a ConnectionInfo) -> Reader {
        Reader { connection_info: connection_info }
    }

    pub fn read_stream_events_forward(&self,
                                      stream_name: &str,
                                      start: u32,
                                      count: u32,
                                      resolve_link_tos: bool)
                                      -> Result<Stream> {
        let http_client = Client::default();

        let mut headers = Headers::new();
        headers.set(
            Accept(vec![
            qitem(Mime(TopLevel::Application,
                       SubLevel::Ext("vnd.eventstore.atom+json".to_owned()), vec![]))]));
        headers.set(ESResolveLinkTos(resolve_link_tos));

        let mut response = try!(http_client.get(&self.url(stream_name, start, count))
            .headers(headers)
            .send());
        match response.status {
            StatusCode::Ok => {
                let mut body = String::new();
                try!(response.read_to_string(&mut body));
                let stream: Stream = try!(serde_json::from_str(&body));
                Ok(stream)
            },
            StatusCode::NotFound => {
                Err(HesError::UserError(UserErrorKind::StreamNotFound))
            },
            StatusCode::Gone => {
                Err(HesError::UserError(UserErrorKind::StreamDeleted))
            },
            _ => {
                self.panic_showing(&response)
            }
        }
    }

    fn url(&self, stream_name: &str, start: u32, count: u32) -> String {
        format!("http://{}:{}/streams/{}/{}/forward/{}?embed=body",
                self.connection_info.host,
                self.connection_info.port,
                stream_name,
                start,
                count)
    }

    //TODO Duplication with StramAppender
    fn panic_showing(&self, response: &HyperResponse) -> ! {
        panic!("hyper::status::StatusCode {} Response: {:?}", response.status, response)
    }
}
<commit_msg>Return UnexpectedResponse instead of panicking<commit_after>use hyper::Client;
use hyper::header::{Headers, Accept, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use std::io::Read;
use serde_json;

use Stream;
use types::Result;
use error::HesError;
use error::UserErrorKind;
use connection::ConnectionInfo;
use api::ESResolveLinkTos;

pub struct Reader<'a> {
    connection_info: &'a ConnectionInfo,
}

impl<'a> Reader<'a> {
    pub fn new(connection_info: &'a ConnectionInfo) -> Reader {
        Reader { connection_info: connection_info }
    }

    pub fn read_stream_events_forward(&self,
                                      stream_name: &str,
                                      start: u32,
                                      count: u32,
                                      resolve_link_tos: bool)
                                      -> Result<Stream> {
        let http_client = Client::default();

        let mut headers = Headers::new();
        headers.set(
            Accept(vec![
            qitem(Mime(TopLevel::Application,
                       SubLevel::Ext("vnd.eventstore.atom+json".to_owned()), vec![]))]));
        headers.set(ESResolveLinkTos(resolve_link_tos));

        let mut response = try!(http_client.get(&self.url(stream_name, start, count))
            .headers(headers)
            .send());
        match response.status {
            StatusCode::Ok => {
                let mut body = String::new();
                try!(response.read_to_string(&mut body));
                let stream: Stream = try!(serde_json::from_str(&body));
                Ok(stream)
            },
            StatusCode::NotFound => {
                Err(HesError::UserError(UserErrorKind::StreamNotFound))
            },
            StatusCode::Gone => {
                Err(HesError::UserError(UserErrorKind::StreamDeleted))
            },
            _ => {
                Err(HesError::UserError(UserErrorKind::UnexpectedResponse(response)))
            }
        }
    }

    fn url(&self, stream_name: &str, start: u32, count: u32) -> String {
        format!("http://{}:{}/streams/{}/{}/forward/{}?embed=body",
                self.connection_info.host,
                self.connection_info.port,
                stream_name,
                start,
                count)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do
//! [Cap'n Proto code generation](https://capnproto.org/otherlang.html#how-to-write-compiler-plugins)
//! within a Cargo build. You still need the `capnp` binary (implemented in C++).
//! (If you use a package manager, try looking for a package called
//! `capnproto`.)
//!
//! In your Cargo.toml:
//!
//! ```ignore
//! [package]
//! build = "build.rs"
//!
//! [build-dependencies]
//! capnpc = "0.12"
//! ```
//!
//! In your build.rs:
//!
//! ```ignore
//! extern crate capnpc;
//!
//! fn main() {
//!     capnpc::CompilerCommand::new()
//!         .src_prefix("schema")
//!         .file("schema/foo.capnp")
//!         .file("schema/bar.capnp")
//!         .run().expect("schema compiler command");
//! }
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

extern crate capnp;

/// Code generated from
/// [schema.capnp](https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
pub mod schema_capnp;

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};

fn run_command(mut command: ::std::process::Command, path: &PathBuf) -> ::capnp::Result<()> {
    let mut p = command.spawn()?;
    crate::codegen::generate_code(p.stdout.take().unwrap(), path.as_path())?;
    let exit_status = p.wait()?;
    if !exit_status.success() {
        Err(::capnp::Error::failed(format!(
            "Non-success exit status: {}",
            exit_status
        )))
    } else {
        Ok(())
    }
}

/// A builder object for schema compiler commands.
pub struct CompilerCommand {
    files: Vec<PathBuf>,
    src_prefixes: Vec<PathBuf>,
    import_paths: Vec<PathBuf>,
    no_standard_import: bool,
    output_path: Option<PathBuf>,
}

impl CompilerCommand {
    /// Creates a new, empty command.
    pub fn new() -> CompilerCommand {
        CompilerCommand {
            files: Vec::new(),
            src_prefixes: Vec::new(),
            import_paths: Vec::new(),
            no_standard_import: false,
            output_path: None,
        }
    }

    /// Adds a file to be compiled.
    pub fn file<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.files.push(path.as_ref().to_path_buf());
        self
    }

    /// Adds a --src-prefix flag. For all files specified for compilation that start
    /// with `prefix`, removes the prefix when computing output filenames.
    pub fn src_prefix<'a, P>(&'a mut self, prefix: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.src_prefixes.push(prefix.as_ref().to_path_buf());
        self
    }

    /// Adds an --import_path flag. Adds `dir` to the list of directories searched
    /// for absolute imports.
    pub fn import_path<'a, P>(&'a mut self, dir: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.import_paths.push(dir.as_ref().to_path_buf());
        self
    }

    /// Adds the --no-standard-import flag, indicating that the default import paths of
    /// /usr/include and /usr/local/include should not bet included.
    pub fn no_standard_import<'a>(&'a mut self) -> &'a mut CompilerCommand {
        self.no_standard_import = true;
        self
    }

    ///Sets the output directory of generated code. Default is OUT_DIR
    pub fn output_path<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.output_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Runs the command, assuming a 'capnp' executable is accessible from current working directory (e.g. locally or in PATH environment variable).
    /// Returns an error if `OUT_DIR` or a custom output directory was not set or if `capnp compile` fails.
    pub fn run(&mut self) -> ::capnp::Result<()> {
        self.run_with("capnp")
    }

    /// Runs the command, using a custom capnp `executable` path (not a directory).
    /// Returns an error if `OUT_DIR` or a custom output directory was not set or if `<executable> compile` fails.
    pub fn run_with<P>(&mut self, executable: P) -> ::capnp::Result<()>
    where
        P: AsRef<Path>
    {
        let mut command = ::std::process::Command::new(executable.as_ref());
        command.arg("compile").arg("-o").arg("-");

        if self.no_standard_import {
            command.arg("--no-standard-import");
        }

        for import_path in &self.import_paths {
            command.arg(&format!("--import-path={}", import_path.display()));
        }

        for src_prefix in &self.src_prefixes {
            command.arg(&format!("--src-prefix={}", src_prefix.display()));
        }

        for file in &self.files {
            command.arg(&format!("{}", file.display()));
        }

        let output_path = if let Some(output_path) = &self.output_path {
            output_path.clone()
        } else {
            // Try `OUT_DIR` by default
            PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
                ::capnp::Error::failed(format!(
                    "Could not access `OUT_DIR` environment variable: {}. \
                     You might need to set it up or instead create you own output \
                     structure using `CompilerCommand::output_path`",
                    error
                ))
            })?)
        };

        command.stdout(::std::process::Stdio::piped());
        command.stderr(::std::process::Stdio::inherit());

        run_command(command, &output_path).map_err(|error| {
            ::capnp::Error::failed(format!(
                "Error while trying to execute `capnp compile`: {}.  \
                 Please verify that version 0.5.2 or higher of the capnp executable \
                 is installed on your system. See https://capnproto.org/install.html",
                error
            ))
        })
    }
}

#[test]
fn compiler_command_new_no_out_dir() {
    let error = CompilerCommand::new().run().unwrap_err().description;
    assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<commit_msg>fix warnings regarding lifetimes that can be auto-elided<commit_after>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do
//! [Cap'n Proto code generation](https://capnproto.org/otherlang.html#how-to-write-compiler-plugins)
//! within a Cargo build. You still need the `capnp` binary (implemented in C++).
//! (If you use a package manager, try looking for a package called
//! `capnproto`.)
//!
//! In your Cargo.toml:
//!
//! ```ignore
//! [package]
//! build = "build.rs"
//!
//! [build-dependencies]
//! capnpc = "0.12"
//! ```
//!
//! In your build.rs:
//!
//! ```ignore
//! extern crate capnpc;
//!
//! fn main() {
//!     capnpc::CompilerCommand::new()
//!         .src_prefix("schema")
//!         .file("schema/foo.capnp")
//!         .file("schema/bar.capnp")
//!         .run().expect("schema compiler command");
//! }
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

extern crate capnp;

/// Code generated from
/// [schema.capnp](https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
pub mod schema_capnp;

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};

fn run_command(mut command: ::std::process::Command, path: &PathBuf) -> ::capnp::Result<()> {
    let mut p = command.spawn()?;
    crate::codegen::generate_code(p.stdout.take().unwrap(), path.as_path())?;
    let exit_status = p.wait()?;
    if !exit_status.success() {
        Err(::capnp::Error::failed(format!(
            "Non-success exit status: {}",
            exit_status
        )))
    } else {
        Ok(())
    }
}

/// A builder object for schema compiler commands.
pub struct CompilerCommand {
    files: Vec<PathBuf>,
    src_prefixes: Vec<PathBuf>,
    import_paths: Vec<PathBuf>,
    no_standard_import: bool,
    output_path: Option<PathBuf>,
}

impl CompilerCommand {
    /// Creates a new, empty command.
    pub fn new() -> CompilerCommand {
        CompilerCommand {
            files: Vec::new(),
            src_prefixes: Vec::new(),
            import_paths: Vec::new(),
            no_standard_import: false,
            output_path: None,
        }
    }

    /// Adds a file to be compiled.
    pub fn file<P>(&mut self, path: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.files.push(path.as_ref().to_path_buf());
        self
    }

    /// Adds a --src-prefix flag. For all files specified for compilation that start
    /// with `prefix`, removes the prefix when computing output filenames.
    pub fn src_prefix<P>(&mut self, prefix: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.src_prefixes.push(prefix.as_ref().to_path_buf());
        self
    }

    /// Adds an --import_path flag. Adds `dir` to the list of directories searched
    /// for absolute imports.
    pub fn import_path<P>(&mut self, dir: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.import_paths.push(dir.as_ref().to_path_buf());
        self
    }

    /// Adds the --no-standard-import flag, indicating that the default import paths of
    /// /usr/include and /usr/local/include should not bet included.
    pub fn no_standard_import(&mut self) -> &mut CompilerCommand {
        self.no_standard_import = true;
        self
    }

    ///Sets the output directory of generated code. Default is OUT_DIR
    pub fn output_path<P>(&mut self, path: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.output_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Runs the command, assuming a 'capnp' executable is accessible from current working directory (e.g. locally or in PATH environment variable).
    /// Returns an error if `OUT_DIR` or a custom output directory was not set or if `capnp compile` fails.
    pub fn run(&mut self) -> ::capnp::Result<()> {
        self.run_with("capnp")
    }

    /// Runs the command, using a custom capnp `executable` path (not a directory).
    /// Returns an error if `OUT_DIR` or a custom output directory was not set or if `<executable> compile` fails.
    pub fn run_with<P>(&mut self, executable: P) -> ::capnp::Result<()>
    where
        P: AsRef<Path>
    {
        let mut command = ::std::process::Command::new(executable.as_ref());
        command.arg("compile").arg("-o").arg("-");

        if self.no_standard_import {
            command.arg("--no-standard-import");
        }

        for import_path in &self.import_paths {
            command.arg(&format!("--import-path={}", import_path.display()));
        }

        for src_prefix in &self.src_prefixes {
            command.arg(&format!("--src-prefix={}", src_prefix.display()));
        }

        for file in &self.files {
            command.arg(&format!("{}", file.display()));
        }

        let output_path = if let Some(output_path) = &self.output_path {
            output_path.clone()
        } else {
            // Try `OUT_DIR` by default
            PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
                ::capnp::Error::failed(format!(
                    "Could not access `OUT_DIR` environment variable: {}. \
                     You might need to set it up or instead create you own output \
                     structure using `CompilerCommand::output_path`",
                    error
                ))
            })?)
        };

        command.stdout(::std::process::Stdio::piped());
        command.stderr(::std::process::Stdio::inherit());

        run_command(command, &output_path).map_err(|error| {
            ::capnp::Error::failed(format!(
                "Error while trying to execute `capnp compile`: {}.  \
                 Please verify that version 0.5.2 or higher of the capnp executable \
                 is installed on your system. See https://capnproto.org/install.html",
                error
            ))
        })
    }
}

#[test]
fn compiler_command_new_no_out_dir() {
    let error = CompilerCommand::new().run().unwrap_err().description;
    assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<|endoftext|>


<commit_before>mod extractor;

extern crate num_cpus;

use clap;
use flate2::write::GzEncoder;
use rayon::prelude::*;
use std::fs;
use std::io::{BufRead, BufWriter, Write};
use std::path::{Path, PathBuf};

enum TrapCompression {
    None,
    Gzip,
}

impl TrapCompression {
    fn from_env() -> TrapCompression {
        match std::env::var("CODEQL_RUBY_TRAP_COMPRESSION") {
            Ok(method) => match TrapCompression::from_string(&method) {
                Some(c) => c,
                None => {
                    tracing::error!("Unknown compression method '{}'; using gzip.", &method);
                    TrapCompression::Gzip
                }
            },
            // Default compression method if the env var isn't set:
            Err(_) => TrapCompression::Gzip,
        }
    }

    fn from_string(s: &str) -> Option<TrapCompression> {
        match s.to_lowercase().as_ref() {
            "none" => Some(TrapCompression::None),
            "gzip" => Some(TrapCompression::Gzip),
            _ => None,
        }
    }

    fn extension(&self) -> &str {
        match self {
            TrapCompression::None => ".trap",
            TrapCompression::Gzip => ".trap.gz",
        }
    }
}

/**
 * Gets the number of threads the extractor should use, by reading the
 * CODEQL_THREADS environment variable and using it as described in the
 * extractor spec:
 *
 * "If the number is positive, it indicates the number of threads that should
 * be used. If the number is negative or zero, it should be added to the number
 * of cores available on the machine to determine how many threads to use
 * (minimum of 1). If unspecified, should be considered as set to 1."
 */
fn num_codeql_threads() -> usize {
    match std::env::var("CODEQL_THREADS") {
        // Use 1 thread if the environment variable isn't set.
        Err(_) => 1,

        Ok(num) => match num.parse::<i32>() {
            Ok(num) if num <= 0 => {
                let reduction = -num as usize;
                num_cpus::get() - reduction
            }
            Ok(num) => num as usize,

            Err(_) => {
                tracing::error!(
                    "Unable to parse CODEQL_THREADS value '{}'; defaulting to 1 thread.",
                    &num
                );
                1
            }
        },
    }
}

fn main() -> std::io::Result<()> {
    tracing_subscriber::fmt()
        .with_target(false)
        .without_time()
        .with_level(true)
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    let num_threads = num_codeql_threads();
    tracing::info!(
        "Using {} {}",
        num_threads,
        if num_threads == 1 {
            "thread"
        } else {
            "threads"
        }
    );
    rayon::ThreadPoolBuilder::new()
        .num_threads(num_threads)
        .build_global()
        .unwrap();

    let matches = clap::App::new("Ruby extractor")
        .version("1.0")
        .author("GitHub")
        .about("CodeQL Ruby extractor")
        .args_from_usage(
            "--source-archive-dir=<DIR> 'Sets a custom source archive folder'
                    --output-dir=<DIR>         'Sets a custom trap folder'
                    --file-list=<FILE_LIST>    'A text files containing the paths of the files to extract'",
        )
        .get_matches();
    let src_archive_dir = matches
        .value_of("source-archive-dir")
        .expect("missing --source-archive-dir");
    let src_archive_dir = PathBuf::from(src_archive_dir);

    let trap_dir = matches
        .value_of("output-dir")
        .expect("missing --output-dir");
    let trap_dir = PathBuf::from(trap_dir);
    let trap_compression = TrapCompression::from_env();

    let file_list = matches.value_of("file-list").expect("missing --file-list");
    let file_list = fs::File::open(file_list)?;

    let language = tree_sitter_ruby::language();
    let schema = node_types::read_node_types_str(tree_sitter_ruby::NODE_TYPES)?;
    let lines: std::io::Result<Vec<String>> = std::io::BufReader::new(file_list).lines().collect();
    let lines = lines?;
    lines.par_iter().try_for_each(|line| {
        let path = PathBuf::from(line).canonicalize()?;
        let trap_file = path_for(&trap_dir, &path, trap_compression.extension());
        let src_archive_file = path_for(&src_archive_dir, &path, "");
        let trap = extractor::extract(language, &schema, &path)?;
        std::fs::create_dir_all(&src_archive_file.parent().unwrap())?;
        std::fs::copy(&path, &src_archive_file)?;
        std::fs::create_dir_all(&trap_file.parent().unwrap())?;
        let trap_file = std::fs::File::create(&trap_file)?;
        let mut trap_file = BufWriter::new(trap_file);
        match trap_compression {
            TrapCompression::None => {
                write!(trap_file, "{}", trap)?;
            }
            TrapCompression::Gzip => {
                let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast());
                write!(compressed_writer, "{}", trap)?;
            }
        }
        std::io::Result::Ok(())
    })?;

    Ok(())
}

fn path_for(dir: &Path, path: &Path, ext: &str) -> PathBuf {
    let mut result = PathBuf::from(dir);
    for component in path.components() {
        match component {
            std::path::Component::Prefix(prefix) => match prefix.kind() {
                std::path::Prefix::Disk(letter) | std::path::Prefix::VerbatimDisk(letter) => {
                    result.push(format!("{}_", letter as char))
                }
                std::path::Prefix::Verbatim(x) | std::path::Prefix::DeviceNS(x) => {
                    result.push(x);
                }
                std::path::Prefix::UNC(server, share)
                | std::path::Prefix::VerbatimUNC(server, share) => {
                    result.push("unc");
                    result.push(server);
                    result.push(share);
                }
            },
            std::path::Component::RootDir => {
                // skip
            }
            std::path::Component::Normal(_) => {
                result.push(component);
            }
            std::path::Component::CurDir => {
                // skip
            }
            std::path::Component::ParentDir => {
                result.pop();
            }
        }
    }
    if let Some(x) = result.extension() {
        let mut new_ext = x.to_os_string();
        new_ext.push(ext);
        result.set_extension(new_ext);
    } else {
        result.set_extension(ext);
    }
    result
}
<commit_msg>Simplify propagation of errors<commit_after>mod extractor;

extern crate num_cpus;

use clap;
use flate2::write::GzEncoder;
use rayon::prelude::*;
use std::fs;
use std::io::{BufRead, BufWriter, Write};
use std::path::{Path, PathBuf};

enum TrapCompression {
    None,
    Gzip,
}

impl TrapCompression {
    fn from_env() -> TrapCompression {
        match std::env::var("CODEQL_RUBY_TRAP_COMPRESSION") {
            Ok(method) => match TrapCompression::from_string(&method) {
                Some(c) => c,
                None => {
                    tracing::error!("Unknown compression method '{}'; using gzip.", &method);
                    TrapCompression::Gzip
                }
            },
            // Default compression method if the env var isn't set:
            Err(_) => TrapCompression::Gzip,
        }
    }

    fn from_string(s: &str) -> Option<TrapCompression> {
        match s.to_lowercase().as_ref() {
            "none" => Some(TrapCompression::None),
            "gzip" => Some(TrapCompression::Gzip),
            _ => None,
        }
    }

    fn extension(&self) -> &str {
        match self {
            TrapCompression::None => ".trap",
            TrapCompression::Gzip => ".trap.gz",
        }
    }
}

/**
 * Gets the number of threads the extractor should use, by reading the
 * CODEQL_THREADS environment variable and using it as described in the
 * extractor spec:
 *
 * "If the number is positive, it indicates the number of threads that should
 * be used. If the number is negative or zero, it should be added to the number
 * of cores available on the machine to determine how many threads to use
 * (minimum of 1). If unspecified, should be considered as set to 1."
 */
fn num_codeql_threads() -> usize {
    match std::env::var("CODEQL_THREADS") {
        // Use 1 thread if the environment variable isn't set.
        Err(_) => 1,

        Ok(num) => match num.parse::<i32>() {
            Ok(num) if num <= 0 => {
                let reduction = -num as usize;
                num_cpus::get() - reduction
            }
            Ok(num) => num as usize,

            Err(_) => {
                tracing::error!(
                    "Unable to parse CODEQL_THREADS value '{}'; defaulting to 1 thread.",
                    &num
                );
                1
            }
        },
    }
}

fn main() -> std::io::Result<()> {
    tracing_subscriber::fmt()
        .with_target(false)
        .without_time()
        .with_level(true)
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    let num_threads = num_codeql_threads();
    tracing::info!(
        "Using {} {}",
        num_threads,
        if num_threads == 1 {
            "thread"
        } else {
            "threads"
        }
    );
    rayon::ThreadPoolBuilder::new()
        .num_threads(num_threads)
        .build_global()
        .unwrap();

    let matches = clap::App::new("Ruby extractor")
        .version("1.0")
        .author("GitHub")
        .about("CodeQL Ruby extractor")
        .args_from_usage(
            "--source-archive-dir=<DIR> 'Sets a custom source archive folder'
                    --output-dir=<DIR>         'Sets a custom trap folder'
                    --file-list=<FILE_LIST>    'A text files containing the paths of the files to extract'",
        )
        .get_matches();
    let src_archive_dir = matches
        .value_of("source-archive-dir")
        .expect("missing --source-archive-dir");
    let src_archive_dir = PathBuf::from(src_archive_dir);

    let trap_dir = matches
        .value_of("output-dir")
        .expect("missing --output-dir");
    let trap_dir = PathBuf::from(trap_dir);
    let trap_compression = TrapCompression::from_env();

    let file_list = matches.value_of("file-list").expect("missing --file-list");
    let file_list = fs::File::open(file_list)?;

    let language = tree_sitter_ruby::language();
    let schema = node_types::read_node_types_str(tree_sitter_ruby::NODE_TYPES)?;
    let lines: std::io::Result<Vec<String>> = std::io::BufReader::new(file_list).lines().collect();
    let lines = lines?;
    lines.par_iter().try_for_each(|line| {
        let path = PathBuf::from(line).canonicalize()?;
        let trap_file = path_for(&trap_dir, &path, trap_compression.extension());
        let src_archive_file = path_for(&src_archive_dir, &path, "");
        let trap = extractor::extract(language, &schema, &path)?;
        std::fs::create_dir_all(&src_archive_file.parent().unwrap())?;
        std::fs::copy(&path, &src_archive_file)?;
        std::fs::create_dir_all(&trap_file.parent().unwrap())?;
        let trap_file = std::fs::File::create(&trap_file)?;
        let mut trap_file = BufWriter::new(trap_file);
        match trap_compression {
            TrapCompression::None => {
                write!(trap_file, "{}", trap)
            }
            TrapCompression::Gzip => {
                let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast());
                write!(compressed_writer, "{}", trap)
            }
        }
    })
}

fn path_for(dir: &Path, path: &Path, ext: &str) -> PathBuf {
    let mut result = PathBuf::from(dir);
    for component in path.components() {
        match component {
            std::path::Component::Prefix(prefix) => match prefix.kind() {
                std::path::Prefix::Disk(letter) | std::path::Prefix::VerbatimDisk(letter) => {
                    result.push(format!("{}_", letter as char))
                }
                std::path::Prefix::Verbatim(x) | std::path::Prefix::DeviceNS(x) => {
                    result.push(x);
                }
                std::path::Prefix::UNC(server, share)
                | std::path::Prefix::VerbatimUNC(server, share) => {
                    result.push("unc");
                    result.push(server);
                    result.push(share);
                }
            },
            std::path::Component::RootDir => {
                // skip
            }
            std::path::Component::Normal(_) => {
                result.push(component);
            }
            std::path::Component::CurDir => {
                // skip
            }
            std::path::Component::ParentDir => {
                result.pop();
            }
        }
    }
    if let Some(x) = result.extension() {
        let mut new_ext = x.to_os_string();
        new_ext.push(ext);
        result.set_extension(new_ext);
    } else {
        result.set_extension(ext);
    }
    result
}
<|endoftext|>