<commit_before>use std::process::{Command, Stdio};

use error::{KawsError, KawsResult};

pub fn ensure_dependencies() -> KawsResult {
    ensure_openssl().and(ensure_kubectl()).and(ensure_terraform())
}

fn ensure_openssl() -> KawsResult {
    let installed = match Command::new("openssl")
        .arg("version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status() {
            Ok(status) => status.success(),
            Err(_) => false,
    };

    if installed {
        Ok(None)
    } else {
        Err(KawsError::new("openssl must be installed".to_string()))
    }
}

fn ensure_kubectl() -> KawsResult {
    let installed = match Command::new("kubectl")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status() {
            Ok(status) => status.success(),
            Err(_) => false,
    };

    if installed {
        Ok(None)
    } else {
        Err(KawsError::new("kubectl must be installed".to_string()))
    }
}

fn ensure_terraform() -> KawsResult {
    let installed = match Command::new("terraform")
        .arg("version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status() {
            Ok(status) => status.success(),
            Err(_) => false,
    };

    if installed {
        Ok(None)
    } else {
        Err(KawsError::new("terraform must be installed".to_string()))
    }
}
<commit_msg>Ensure cfssl is installed instead of openssl.<commit_after>use std::process::{Command, Stdio};

use error::{KawsError, KawsResult};

pub fn ensure_dependencies() -> KawsResult {
    ensure_cfssl().and(ensure_kubectl()).and(ensure_terraform())
}

fn ensure_cfssl() -> KawsResult {
    let installed = match Command::new("cfssl")
        .arg("version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status() {
            Ok(status) => status.success(),
            Err(_) => false,
    };

    if installed {
        Ok(None)
    } else {
        Err(KawsError::new("cfssl must be installed".to_string()))
    }
}

fn ensure_kubectl() -> KawsResult {
    let installed = match Command::new("kubectl")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status() {
            Ok(status) => status.success(),
            Err(_) => false,
    };

    if installed {
        Ok(None)
    } else {
        Err(KawsError::new("kubectl must be installed".to_string()))
    }
}

fn ensure_terraform() -> KawsResult {
    let installed = match Command::new("terraform")
        .arg("version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status() {
            Ok(status) => status.success(),
            Err(_) => false,
    };

    if installed {
        Ok(None)
    } else {
        Err(KawsError::new("terraform must be installed".to_string()))
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Serde
//!
//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data
//! structures efficiently and generically.
//!
//! The Serde ecosystem consists of data structures that know how to serialize
//! and deserialize themselves along with data formats that know how to
//! serialize and deserialize other things. Serde provides the layer by which
//! these two groups interact with each other, allowing any supported data
//! structure to be serialized and deserialized using any supported data format.
//!
//! See the Serde website [https://serde.rs/] for additional documentation and
//! usage examples.
//!
//! [https://serde.rs/]: https://serde.rs/
//!
//! ## Design
//!
//! Where many other languages rely on runtime reflection for serializing data,
//! Serde is instead built on Rust's powerful trait system. A data structure
//! that knows how to serialize and deserialize itself is one that implements
//! Serde's `Serialize` and `Deserialize` traits (or uses Serde's derive
//! attribute to automatically generate implementations at compile time). This
//! avoids any overhead of reflection or runtime type information. In fact in
//! many situations the interaction between data structure and data format can
//! be completely optimized away by the Rust compiler, leaving Serde
//! serialization to perform the same speed as a handwritten serializer for the
//! specific selection of data structure and data format.
//!
//! ## Data formats
//!
//! The following is a partial list of data formats that have been implemented
//! for Serde by the community.
//!
//! - [JSON], the ubiquitous JavaScript Object Notation used by many HTTP APIs.
//! - [Bincode], a compact binary format
//!   used for IPC within the Servo rendering engine.
//! - [CBOR], a Concise Binary Object Representation designed for small message
//!   size without the need for version negotiation.
//! - [YAML], a popular human-friendly configuration language that ain't markup
//!   language.
//! - [MessagePack], an efficient binary format that resembles a compact JSON.
//! - [TOML], a minimal configuration format used by [Cargo].
//! - [Pickle], a format common in the Python world.
//! - [Hjson], a variant of JSON designed to be readable and writable by humans.
//! - [BSON], the data storage and network transfer format used by MongoDB.
//! - [Avro], a binary format used within Apache Hadoop, with support for schema
//!   definition.
//! - [URL], the x-www-form-urlencoded format.
//! - [XML], the flexible machine-friendly W3C standard.
//!   *(deserialization only)*
//! - [Envy], a way to deserialize environment variables into Rust structs.
//!   *(deserialization only)*
//! - [Redis], deserialize values from Redis when using [redis-rs].
//!   *(deserialization only)*
//!
//! [JSON]: https://github.com/serde-rs/json
//! [Bincode]: https://github.com/TyOverby/bincode
//! [CBOR]: https://github.com/pyfisch/cbor
//! [YAML]: https://github.com/dtolnay/serde-yaml
//! [MessagePack]: https://github.com/3Hren/msgpack-rust
//! [TOML]: https://github.com/alexcrichton/toml-rs
//! [Pickle]: https://github.com/birkenfeld/serde-pickle
//! [Hjson]: https://github.com/laktak/hjson-rust
//! [BSON]: https://github.com/zonyitoo/bson-rs
//! [Avro]: https://github.com/flavray/avro-rs
//! [URL]: https://github.com/nox/serde_urlencoded
//! [XML]: https://github.com/RReverser/serde-xml-rs
//! [Envy]: https://github.com/softprops/envy
//! [Redis]: https://github.com/OneSignal/serde-redis
//! [Cargo]: http://doc.crates.io/manifest.html
//! [redis-rs]: https://crates.io/crates/redis

////////////////////////////////////////////////////////////////////////////////

// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.71")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Unstable functionality only if the user asks for it. For tracking and
// discussion of these features please refer to this issue:
//
//    https://github.com/serde-rs/serde/issues/812
#![cfg_attr(feature = "unstable", feature(specialization, never_type))]
#![cfg_attr(feature = "alloc", feature(alloc))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cast_lossless,
        const_static_lifetime,
        doc_markdown,
        linkedlist,
        needless_pass_by_value,
        redundant_field_names,
        type_complexity,
        unreadable_literal,
        zero_prefixed_literal
    )
)]
// Whitelisted clippy_pedantic lints
#![cfg_attr(feature = "cargo-clippy", allow(
// integer and float ser/de requires these sorts of casts
    cast_possible_truncation,
    cast_possible_wrap,
    cast_precision_loss,
    cast_sign_loss,
// simplifies some macros
    invalid_upcast_comparisons,
// things are often more readable this way
    decimal_literal_representation,
    option_unwrap_used,
    result_unwrap_used,
    shadow_reuse,
    single_match_else,
    stutter,
    use_self,
// not practical
    indexing_slicing,
    many_single_char_names,
    missing_docs_in_private_items,
    similar_names,
// alternative is not stable
    empty_enum,
    use_debug,
))]
// Blacklisted Rust lints.
//
// Compiler bug involving unused_imports:
// https://github.com/rust-lang/rust/issues/51661
#![deny(missing_docs, /*unused_imports*/)]

////////////////////////////////////////////////////////////////////////////////

#[cfg(feature = "alloc")]
extern crate alloc;

/// A facade around all the types we need from the `std`, `core`, and `alloc`
/// crates. This avoids elaborate import wrangling having to happen in every
/// module.
mod lib {
    mod core {
        #[cfg(not(feature = "std"))]
        pub use core::*;
        #[cfg(feature = "std")]
        pub use std::*;
    }

    pub use self::core::{cmp, iter, mem, num, slice, str};
    pub use self::core::{f32, f64};
    pub use self::core::{i16, i32, i64, i8, isize};
    pub use self::core::{u16, u32, u64, u8, usize};

    pub use self::core::cell::{Cell, RefCell};
    pub use self::core::clone::{self, Clone};
    pub use self::core::convert::{self, From, Into};
    pub use self::core::default::{self, Default};
    pub use self::core::fmt::{self, Debug, Display};
    pub use self::core::marker::{self, PhantomData};
    pub use self::core::ops::Range;
    pub use self::core::option::{self, Option};
    pub use self::core::result::{self, Result};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::borrow::{Cow, ToOwned};
    #[cfg(feature = "std")]
    pub use std::borrow::{Cow, ToOwned};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::string::{String, ToString};
    #[cfg(feature = "std")]
    pub use std::string::String;

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::vec::Vec;
    #[cfg(feature = "std")]
    pub use std::vec::Vec;

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::boxed::Box;
    #[cfg(feature = "std")]
    pub use std::boxed::Box;

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::rc::{Rc, Weak as RcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::rc::{Rc, Weak as RcWeak};

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::arc::{Arc, Weak as ArcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::sync::{Arc, Weak as ArcWeak};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
    #[cfg(feature = "std")]
    pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};

    #[cfg(feature = "std")]
    pub use std::{error, net};

    #[cfg(feature = "std")]
    pub use std::collections::{HashMap, HashSet};
    #[cfg(feature = "std")]
    pub use std::ffi::{CStr, CString, OsStr, OsString};
    #[cfg(feature = "std")]
    pub use std::hash::{BuildHasher, Hash};
    #[cfg(feature = "std")]
    pub use std::io::Write;
    #[cfg(feature = "std")]
    pub use std::num::Wrapping;
    #[cfg(feature = "std")]
    pub use std::path::{Path, PathBuf};
    #[cfg(feature = "std")]
    pub use std::sync::{Mutex, RwLock};
    #[cfg(feature = "std")]
    pub use std::time::{SystemTime, UNIX_EPOCH};

    #[cfg(any(core_duration, feature = "std"))]
    pub use self::core::time::Duration;

    #[cfg(range_inclusive)]
    pub use self::core::ops::RangeInclusive;
}

////////////////////////////////////////////////////////////////////////////////

#[macro_use]
mod macros;

#[macro_use]
mod integer128;

pub mod de;
pub mod ser;

#[doc(inline)]
pub use de::{Deserialize, Deserializer};
#[doc(inline)]
pub use ser::{Serialize, Serializer};

// Generated code uses these to support no_std. Not public API.
#[doc(hidden)]
pub mod export;

// Helpers used by generated code and doc tests. Not public API.
#[doc(hidden)]
pub mod private;

// Re-export #[derive(Serialize, Deserialize)].
//
// This is a workaround for https://github.com/rust-lang/cargo/issues/1286.
// Without this re-export, crates that put Serde derives behind a cfg_attr would
// need to use some silly feature name that depends on both serde and
// serde_derive.
//
//     [features]
//     serde-impls = ["serde", "serde_derive"]
//
//     [dependencies]
//     serde = { version = "1.0", optional = true }
//     serde_derive = { version = "1.0", optional = true }
//
//     # Used like this:
//     # #[cfg(feature = "serde-impls")]
//     # #[macro_use]
//     # extern crate serde_derive;
//     #
//     # #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
//     # struct S { /* ... */ }
//
// The re-exported derives allow crates to use "serde" as the name of their
// Serde feature which is more intuitive.
//
//     [dependencies]
//     serde = { version = "1.0", optional = true, features = ["derive"] }
//
//     # Used like this:
//     # #[cfg(feature = "serde")]
//     # #[macro_use]
//     # extern crate serde;
//     #
//     # #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
//     # struct S { /* ... */ }
//
// The reason re-exporting is not enabled by default is that disabling it would
// be annoying for crates that provide handwritten impls or data formats. They
// would need to disable default features and then explicitly re-enable std.
#[cfg(feature = "serde_derive")]
#[allow(unused_imports)]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde_derive")]
#[doc(hidden)]
pub use serde_derive::*;
<commit_msg>Fix compilation under `rc` feature<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Serde
//!
//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data
//! structures efficiently and generically.
//!
//! The Serde ecosystem consists of data structures that know how to serialize
//! and deserialize themselves along with data formats that know how to
//! serialize and deserialize other things. Serde provides the layer by which
//! these two groups interact with each other, allowing any supported data
//! structure to be serialized and deserialized using any supported data format.
//!
//! See the Serde website [https://serde.rs/] for additional documentation and
//! usage examples.
//!
//! [https://serde.rs/]: https://serde.rs/
//!
//! ## Design
//!
//! Where many other languages rely on runtime reflection for serializing data,
//! Serde is instead built on Rust's powerful trait system. A data structure
//! that knows how to serialize and deserialize itself is one that implements
//! Serde's `Serialize` and `Deserialize` traits (or uses Serde's derive
//! attribute to automatically generate implementations at compile time). This
//! avoids any overhead of reflection or runtime type information. In fact in
//! many situations the interaction between data structure and data format can
//! be completely optimized away by the Rust compiler, leaving Serde
//! serialization to perform the same speed as a handwritten serializer for the
//! specific selection of data structure and data format.
//!
//! ## Data formats
//!
//! The following is a partial list of data formats that have been implemented
//! for Serde by the community.
//!
//! - [JSON], the ubiquitous JavaScript Object Notation used by many HTTP APIs.
//! - [Bincode], a compact binary format
//!   used for IPC within the Servo rendering engine.
//! - [CBOR], a Concise Binary Object Representation designed for small message
//!   size without the need for version negotiation.
//! - [YAML], a popular human-friendly configuration language that ain't markup
//!   language.
//! - [MessagePack], an efficient binary format that resembles a compact JSON.
//! - [TOML], a minimal configuration format used by [Cargo].
//! - [Pickle], a format common in the Python world.
//! - [Hjson], a variant of JSON designed to be readable and writable by humans.
//! - [BSON], the data storage and network transfer format used by MongoDB.
//! - [Avro], a binary format used within Apache Hadoop, with support for schema
//!   definition.
//! - [URL], the x-www-form-urlencoded format.
//! - [XML], the flexible machine-friendly W3C standard.
//!   *(deserialization only)*
//! - [Envy], a way to deserialize environment variables into Rust structs.
//!   *(deserialization only)*
//! - [Redis], deserialize values from Redis when using [redis-rs].
//!   *(deserialization only)*
//!
//! [JSON]: https://github.com/serde-rs/json
//! [Bincode]: https://github.com/TyOverby/bincode
//! [CBOR]: https://github.com/pyfisch/cbor
//! [YAML]: https://github.com/dtolnay/serde-yaml
//! [MessagePack]: https://github.com/3Hren/msgpack-rust
//! [TOML]: https://github.com/alexcrichton/toml-rs
//! [Pickle]: https://github.com/birkenfeld/serde-pickle
//! [Hjson]: https://github.com/laktak/hjson-rust
//! [BSON]: https://github.com/zonyitoo/bson-rs
//! [Avro]: https://github.com/flavray/avro-rs
//! [URL]: https://github.com/nox/serde_urlencoded
//! [XML]: https://github.com/RReverser/serde-xml-rs
//! [Envy]: https://github.com/softprops/envy
//! [Redis]: https://github.com/OneSignal/serde-redis
//! [Cargo]: http://doc.crates.io/manifest.html
//! [redis-rs]: https://crates.io/crates/redis

////////////////////////////////////////////////////////////////////////////////

// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.71")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Unstable functionality only if the user asks for it. For tracking and
// discussion of these features please refer to this issue:
//
//    https://github.com/serde-rs/serde/issues/812
#![cfg_attr(feature = "unstable", feature(specialization, never_type))]
#![cfg_attr(feature = "alloc", feature(alloc))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cast_lossless,
        const_static_lifetime,
        doc_markdown,
        linkedlist,
        needless_pass_by_value,
        redundant_field_names,
        type_complexity,
        unreadable_literal,
        zero_prefixed_literal
    )
)]
// Whitelisted clippy_pedantic lints
#![cfg_attr(feature = "cargo-clippy", allow(
// integer and float ser/de requires these sorts of casts
    cast_possible_truncation,
    cast_possible_wrap,
    cast_precision_loss,
    cast_sign_loss,
// simplifies some macros
    invalid_upcast_comparisons,
// things are often more readable this way
    decimal_literal_representation,
    option_unwrap_used,
    result_unwrap_used,
    shadow_reuse,
    single_match_else,
    stutter,
    use_self,
// not practical
    indexing_slicing,
    many_single_char_names,
    missing_docs_in_private_items,
    similar_names,
// alternative is not stable
    empty_enum,
    use_debug,
))]
// Blacklisted Rust lints.
//
// Compiler bug involving unused_imports:
// https://github.com/rust-lang/rust/issues/51661
#![deny(missing_docs, /*unused_imports*/)]

////////////////////////////////////////////////////////////////////////////////

#[cfg(feature = "alloc")]
extern crate alloc;

/// A facade around all the types we need from the `std`, `core`, and `alloc`
/// crates. This avoids elaborate import wrangling having to happen in every
/// module.
mod lib {
    mod core {
        #[cfg(not(feature = "std"))]
        pub use core::*;
        #[cfg(feature = "std")]
        pub use std::*;
    }

    pub use self::core::{cmp, iter, mem, num, slice, str};
    pub use self::core::{f32, f64};
    pub use self::core::{i16, i32, i64, i8, isize};
    pub use self::core::{u16, u32, u64, u8, usize};

    pub use self::core::cell::{Cell, RefCell};
    pub use self::core::clone::{self, Clone};
    pub use self::core::convert::{self, From, Into};
    pub use self::core::default::{self, Default};
    pub use self::core::fmt::{self, Debug, Display};
    pub use self::core::marker::{self, PhantomData};
    pub use self::core::ops::Range;
    pub use self::core::option::{self, Option};
    pub use self::core::result::{self, Result};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::borrow::{Cow, ToOwned};
    #[cfg(feature = "std")]
    pub use std::borrow::{Cow, ToOwned};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::string::{String, ToString};
    #[cfg(feature = "std")]
    pub use std::string::String;

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::vec::Vec;
    #[cfg(feature = "std")]
    pub use std::vec::Vec;

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::boxed::Box;
    #[cfg(feature = "std")]
    pub use std::boxed::Box;

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::rc::{Rc, Weak as RcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::rc::{Rc, Weak as RcWeak};

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::sync::{Arc, Weak as ArcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::sync::{Arc, Weak as ArcWeak};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
    #[cfg(feature = "std")]
    pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};

    #[cfg(feature = "std")]
    pub use std::{error, net};

    #[cfg(feature = "std")]
    pub use std::collections::{HashMap, HashSet};
    #[cfg(feature = "std")]
    pub use std::ffi::{CStr, CString, OsStr, OsString};
    #[cfg(feature = "std")]
    pub use std::hash::{BuildHasher, Hash};
    #[cfg(feature = "std")]
    pub use std::io::Write;
    #[cfg(feature = "std")]
    pub use std::num::Wrapping;
    #[cfg(feature = "std")]
    pub use std::path::{Path, PathBuf};
    #[cfg(feature = "std")]
    pub use std::sync::{Mutex, RwLock};
    #[cfg(feature = "std")]
    pub use std::time::{SystemTime, UNIX_EPOCH};

    #[cfg(any(core_duration, feature = "std"))]
    pub use self::core::time::Duration;

    #[cfg(range_inclusive)]
    pub use self::core::ops::RangeInclusive;
}

////////////////////////////////////////////////////////////////////////////////

#[macro_use]
mod macros;

#[macro_use]
mod integer128;

pub mod de;
pub mod ser;

#[doc(inline)]
pub use de::{Deserialize, Deserializer};
#[doc(inline)]
pub use ser::{Serialize, Serializer};

// Generated code uses these to support no_std. Not public API.
#[doc(hidden)]
pub mod export;

// Helpers used by generated code and doc tests. Not public API.
#[doc(hidden)]
pub mod private;

// Re-export #[derive(Serialize, Deserialize)].
//
// This is a workaround for https://github.com/rust-lang/cargo/issues/1286.
// Without this re-export, crates that put Serde derives behind a cfg_attr would
// need to use some silly feature name that depends on both serde and
// serde_derive.
//
//     [features]
//     serde-impls = ["serde", "serde_derive"]
//
//     [dependencies]
//     serde = { version = "1.0", optional = true }
//     serde_derive = { version = "1.0", optional = true }
//
//     # Used like this:
//     # #[cfg(feature = "serde-impls")]
//     # #[macro_use]
//     # extern crate serde_derive;
//     #
//     # #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
//     # struct S { /* ... */ }
//
// The re-exported derives allow crates to use "serde" as the name of their
// Serde feature which is more intuitive.
//
//     [dependencies]
//     serde = { version = "1.0", optional = true, features = ["derive"] }
//
//     # Used like this:
//     # #[cfg(feature = "serde")]
//     # #[macro_use]
//     # extern crate serde;
//     #
//     # #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
//     # struct S { /* ... */ }
//
// The reason re-exporting is not enabled by default is that disabling it would
// be annoying for crates that provide handwritten impls or data formats. They
// would need to disable default features and then explicitly re-enable std.
#[cfg(feature = "serde_derive")]
#[allow(unused_imports)]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde_derive")]
#[doc(hidden)]
pub use serde_derive::*;
<|endoftext|>


<commit_before>extern crate docopt;
use docopt::Docopt;

use std::io::{BufRead, BufReader, BufWriter, Write};
use std::fs::File;
use std::slice;
use std::mem;

static USAGE: &'static str = "
Usage: digest <source> <target>
";

fn main() {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    println!("digest will overwrite <target>.targets and <target>.offsets, so careful");
    println!("at least, it will once you edit the code to uncomment the line.");
    let source = args.get_str("<source>");
    let _target = args.get_str("<target>");
    let graph = read_edges(&source);
    _digest_graph_vector(&_extract_fragment(graph.iter().map(|x| *x), 0, 1), _target); // will overwrite "prefix.offsets" and "prefix.targets"

}

// loads the read_edges file available at https://snap.stanford.edu/data/soc-LiveJournal1.html
fn read_edges(filename: &str) -> Vec<(u32, u32)> {
    let mut graph = Vec::new();
    let file = BufReader::new(File::open(filename).unwrap());
    for readline in file.lines() {
        let line = readline.ok().expect("read error");
        if !line.starts_with('#') {
            let elts: Vec<&str> = line[..].split("\t").collect();
            let src: u32 = elts[0].parse().ok().expect("malformed src");
            let dst: u32 = elts[1].parse().ok().expect("malformed dst");
            graph.push((src, dst))
        }
    }

    println!("graph data loaded; {:?} edges", graph.len());
    return graph;
}

fn _extract_fragment<I: Iterator<Item=(u32, u32)>>(graph: I, index: u64, parts: u64) -> (Vec<u32>, Vec<u32>) {
    let mut nodes = Vec::new();
    let mut edges = Vec::new();

    for (src, dst) in graph {
        if src as u64 % parts == index {
            while src + 1 >= nodes.len() as u32 { nodes.push(0); }
            while dst + 1 >= nodes.len() as u32 { nodes.push(0); } // allows unsafe access to nodes

            nodes[src as usize + 1] += 1;
            edges.push(dst);
        }
    }

    for index in (1..nodes.len()) {
        nodes[index] += nodes[index - 1];
    }

    return (nodes, edges);
}

fn _digest_graph_vector(graph: &(Vec<u32>, Vec<u32>), output_prefix: &str) {
    let mut edge_writer = BufWriter::new(File::create(format!("{}.targets", output_prefix)).unwrap());
    let mut node_writer = BufWriter::new(File::create(format!("{}.offsets", output_prefix)).unwrap());
    node_writer.write_all(unsafe { _typed_as_byte_slice(&graph.0[..]) }).unwrap();

    let mut slice = unsafe { _typed_as_byte_slice(&graph.1[..]) };
    while slice.len() > 0 {
        let to_write = if slice.len() < 1000000 { slice.len() } else { 1000000 };
        edge_writer.write_all(&slice[..to_write]).unwrap();
        println!("wrote some; remaining: {}", slice.len());
        slice = &slice[to_write..];
    }
}

unsafe fn _typed_as_byte_slice<T>(slice: &[T]) -> &[u8] {
    slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}
<commit_msg>should write u64s not u32s<commit_after>extern crate docopt;
use docopt::Docopt;

use std::io::{BufRead, BufReader, BufWriter, Write};
use std::fs::File;
use std::slice;
use std::mem;

static USAGE: &'static str = "
Usage: digest <source> <target>
";

fn main() {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());
    let source = args.get_str("<source>");
    let _target = args.get_str("<target>");
    let graph = read_edges(&source);
    _digest_graph_vector(&_extract_fragment(graph.iter().map(|x| *x)), _target);
}

// loads the read_edges file available at https://snap.stanford.edu/data/soc-LiveJournal1.html
fn read_edges(filename: &str) -> Vec<(u32, u32)> {
    let mut graph = Vec::new();
    let file = BufReader::new(File::open(filename).unwrap());
    for readline in file.lines() {
        let line = readline.ok().expect("read error");
        if !line.starts_with('#') {
            let elts: Vec<&str> = line[..].split(" ").collect();
            let src: u32 = elts[0].parse().ok().expect("malformed src");
            let dst: u32 = elts[1].parse().ok().expect("malformed dst");
            graph.push((src, dst))
        }
    }

    println!("graph data loaded; {:?} edges", graph.len());
    return graph;
}

fn _extract_fragment<I: Iterator<Item=(u32, u32)>>(graph: I) -> (Vec<u64>, Vec<u32>) {
    let mut nodes = Vec::new();
    let mut edges = Vec::new();

    for (src, dst) in graph {
        while src + 1 >= nodes.len() as u32 { nodes.push(0); }
        while dst + 1 >= nodes.len() as u32 { nodes.push(0); } // allows unsafe access to nodes

        nodes[src as usize + 1] += 1;
        edges.push(dst);
    }

    for index in (1..nodes.len()) {
        nodes[index] += nodes[index - 1];
    }

    return (nodes, edges);
}

fn _digest_graph_vector(graph: &(Vec<u64>, Vec<u32>), output_prefix: &str) {
    let mut edge_writer = BufWriter::new(File::create(format!("{}.targets", output_prefix)).unwrap());
    let mut node_writer = BufWriter::new(File::create(format!("{}.offsets", output_prefix)).unwrap());
    node_writer.write_all(unsafe { _typed_as_byte_slice(&graph.0[..]) }).unwrap();

    let mut slice = unsafe { _typed_as_byte_slice(&graph.1[..]) };
    while slice.len() > 0 {
        let to_write = if slice.len() < 1000000 { slice.len() } else { 1000000 };
        edge_writer.write_all(&slice[..to_write]).unwrap();
        println!("wrote some; remaining: {}", slice.len());
        slice = &slice[to_write..];
    }
}

unsafe fn _typed_as_byte_slice<T>(slice: &[T]) -> &[u8] {
    slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}
<|endoftext|>


<commit_before>//! Callback methods for rustwlc
use rustwlc::handle::{WlcOutput, WlcView};
use rustwlc::types::*;
use rustwlc::input::{pointer, keyboard};

use compositor;
use super::keys;
use super::lua;
use super::keys::KeyPress;
use super::layout::tree;

/// If the event is handled by way-cooler
const EVENT_HANDLED: bool = true;

/// If the event should be passed through to clients
const EVENT_PASS_THROUGH: bool = false;

// wlc callbacks

pub extern fn output_created(output: WlcOutput) -> bool {
    trace!("output_created: {:?}: {}", output, output.get_name());
    {
        if let Ok(mut tree) = tree::try_lock_tree() {
            tree.add_output(output.clone());
            tree.switch_to_workspace(&"1");
            true
        } else {
            false
        }
    }
}

pub extern fn output_destroyed(output: WlcOutput) {
    trace!("output_destroyed: {:?}", output);
}

pub extern fn output_focus(output: WlcOutput, focused: bool) {
    trace!("output_focus: {:?} focus={}", output, focused);
}

pub extern fn output_resolution(output: WlcOutput,
                            old_size_ptr: &Size, new_size_ptr: &Size) {
    trace!("output_resolution: {:?} from  {:?} to {:?}",
             output, *old_size_ptr, *new_size_ptr);
}
/*
pub extern fn output_render_pre(output: WlcOutput) {
    //println!("output_render_pre");
}

pub extern fn output_render_post(output: WlcOutput) {
    //println!("output_render_post");
}
*/
pub extern fn view_created(view: WlcView) -> bool {
    trace!("view_created: {:?}: \"{}\"", view, view.get_title());
    let output = view.get_output();
    if let Ok(mut tree) = tree::try_lock_tree() {
        tree.add_view(view.clone());
        drop(tree);
        view.set_mask(output.get_mask());
        view.bring_to_front();
        view.focus();
        true
    } else {
        false
    }
}

pub extern fn view_destroyed(view: WlcView) {
    trace!("view_destroyed: {:?}", view);
    if let Ok(mut tree) = tree::try_lock_tree() {
        tree.remove_view(&view);
    } else {
        warn!("Could not delete view {:?}", view);
    }
}

pub extern fn view_focus(current: WlcView, focused: bool) {
    trace!("view_focus: {:?} {}", current, focused);
    current.set_state(VIEW_ACTIVATED, focused);
    // set the focus view in the tree
    {
        // If tree is already grabbed,
        // it should have the active container all set
        if let Ok(mut tree) = tree::try_lock_tree() {
            tree.set_active_container(current.clone());
        }
    }
}

pub extern fn view_move_to_output(current: WlcView,
                                  o1: WlcOutput, o2: WlcOutput) {
    trace!("view_move_to_output: {:?}, {:?}, {:?}", current, o1, o2);
}

pub extern fn view_request_geometry(view: WlcView, geometry: &Geometry) {
    trace!("view_request_geometry: {:?} wants {:?}", view, geometry);
    view.set_geometry(EDGE_NONE, geometry);
}

pub extern fn view_request_state(view: WlcView, state: ViewState, handled: bool) {
    trace!("view_request_state: {}, {:?}, handled: {}",
    view.get_title(), state, handled);
    view.set_state(state, handled);
}

pub extern fn view_request_move(view: WlcView, dest: &Point) {
    // Called by views when they have a dang resize mouse thing, we should only
    // let it happen in view floating mode
    compositor::start_interactive_move(&view, dest);
    trace!("view_request_move: to {}", *dest);
}

pub extern fn view_request_resize(view: WlcView,
                              edge: ResizeEdge, location: &Point) {
    compositor::start_interactive_resize(&view, edge, location);
    trace!("view_request_resize: edge {:?}, to {}",
             edge, location);
}

pub extern fn keyboard_key(_view: WlcView, _time: u32, mods: &KeyboardModifiers,
                       key: u32, state: KeyState) -> bool {
    if state == KeyState::Pressed {
        // TODO this function will throw an error in Rustwlc right now
        // let mut keys = keyboard::get_current_keys().into_iter()
        //      .map(|&k| Keysym::from(k)).collect();
        let sym = keyboard::get_keysym_for_key(key, &KeyMod::empty());
        let keys = vec![sym];

        let press = KeyPress::new(mods.mods, keys);
        if let Some(action) = keys::get(&press) {
            debug!("[key] Found an action for {:?}", press);
            action();
            return EVENT_HANDLED;
        }
    }

    return EVENT_PASS_THROUGH;
}

pub extern fn pointer_button(view: WlcView, _time: u32,
                         mods: &KeyboardModifiers, button: u32,
                             state: ButtonState, point: &Point) -> bool {
    compositor::on_pointer_button(view, _time, mods, button, state, point)
}

pub extern fn pointer_scroll(_view: WlcView, _time: u32,
                         _mods_ptr: &KeyboardModifiers, axis: ScrollAxis,
                         heights: [f64; 2]) -> bool {
    trace!("pointer_scroll: {:?} {:?}", axis,
           heights.iter().map(|f| f.clone().round()).collect::<Vec<f64>>());
    false
}

pub extern fn pointer_motion(_view: WlcView, _time: u32, point: &Point) -> bool {
    pointer::set_position(point);
    compositor::on_pointer_motion(_view, _time, point)
}

/*
pub extern fn touch(view: WlcView, time: u32, mods_ptr: &KeyboardModifiers,
               touch: TouchType, key: i32, point_ptr: &Point) -> bool {
    false
}
*/

pub extern fn compositor_ready() {
    info!("Preparing compositor!");
    info!("Initializing Lua...");
    lua::init();
}

pub extern fn compositor_terminating() {
    info!("Compositor terminating!");
    lua::send(lua::LuaQuery::Terminate).ok();
}

pub fn init() {
    use rustwlc::callback;

    callback::output_created(output_created);
    callback::output_destroyed(output_destroyed);
    callback::output_focus(output_focus);
    callback::output_resolution(output_resolution);
    callback::view_created(view_created);
    callback::view_destroyed(view_destroyed);
    callback::view_focus(view_focus);
    callback::view_move_to_output(view_move_to_output);
    callback::view_request_geometry(view_request_geometry);
    callback::view_request_state(view_request_state);
    callback::view_request_move(view_request_move);
    callback::view_request_resize(view_request_resize);
    callback::keyboard_key(keyboard_key);
    callback::pointer_button(pointer_button);
    callback::pointer_scroll(pointer_scroll);
    callback::pointer_motion(pointer_motion);
    callback::compositor_ready(compositor_ready);
    callback::compositor_terminate(compositor_terminating);
    trace!("Registered wlc callbacks");
}
<commit_msg>View requests are now auto denied.<commit_after>//! Callback methods for rustwlc
use rustwlc::handle::{WlcOutput, WlcView};
use rustwlc::types::*;
use rustwlc::input::{pointer, keyboard};

use compositor;
use super::keys;
use super::lua;
use super::keys::KeyPress;
use super::layout::tree;

/// If the event is handled by way-cooler
const EVENT_HANDLED: bool = true;

/// If the event should be passed through to clients
const EVENT_PASS_THROUGH: bool = false;

// wlc callbacks

pub extern fn output_created(output: WlcOutput) -> bool {
    trace!("output_created: {:?}: {}", output, output.get_name());
    {
        if let Ok(mut tree) = tree::try_lock_tree() {
            tree.add_output(output.clone());
            tree.switch_to_workspace(&"1");
            true
        } else {
            false
        }
    }
}

pub extern fn output_destroyed(output: WlcOutput) {
    trace!("output_destroyed: {:?}", output);
}

pub extern fn output_focus(output: WlcOutput, focused: bool) {
    trace!("output_focus: {:?} focus={}", output, focused);
}

pub extern fn output_resolution(output: WlcOutput,
                            old_size_ptr: &Size, new_size_ptr: &Size) {
    trace!("output_resolution: {:?} from  {:?} to {:?}",
             output, *old_size_ptr, *new_size_ptr);
}
/*
pub extern fn output_render_pre(output: WlcOutput) {
    //println!("output_render_pre");
}

pub extern fn output_render_post(output: WlcOutput) {
    //println!("output_render_post");
}
*/
pub extern fn view_created(view: WlcView) -> bool {
    trace!("view_created: {:?}: \"{}\"", view, view.get_title());
    let output = view.get_output();
    if let Ok(mut tree) = tree::try_lock_tree() {
        tree.add_view(view.clone());
        drop(tree);
        view.set_mask(output.get_mask());
        view.bring_to_front();
        view.focus();
        true
    } else {
        false
    }
}

pub extern fn view_destroyed(view: WlcView) {
    trace!("view_destroyed: {:?}", view);
    if let Ok(mut tree) = tree::try_lock_tree() {
        tree.remove_view(&view);
    } else {
        warn!("Could not delete view {:?}", view);
    }
}

pub extern fn view_focus(current: WlcView, focused: bool) {
    trace!("view_focus: {:?} {}", current, focused);
    current.set_state(VIEW_ACTIVATED, focused);
    // set the focus view in the tree
    {
        // If tree is already grabbed,
        // it should have the active container all set
        if let Ok(mut tree) = tree::try_lock_tree() {
            tree.set_active_container(current.clone());
        }
    }
}

pub extern fn view_move_to_output(current: WlcView,
                                  o1: WlcOutput, o2: WlcOutput) {
    trace!("view_move_to_output: {:?}, {:?}, {:?}", current, o1, o2);
}

pub extern fn view_request_geometry(view: WlcView, geometry: &Geometry) {
    trace!("view_request_geometry: {:?} wants {:?}", view, geometry);
    warn!("Denying request");
}

pub extern fn view_request_state(view: WlcView, state: ViewState, handled: bool) {
    trace!("view_request_state: {}, {:?}, handled: {}",
    view.get_title(), state, handled);
    view.set_state(state, handled);
}

pub extern fn view_request_move(view: WlcView, dest: &Point) {
    // Called by views when they have a dang resize mouse thing, we should only
    // let it happen in view floating mode
    compositor::start_interactive_move(&view, dest);
    trace!("view_request_move: to {}", *dest);
}

pub extern fn view_request_resize(view: WlcView,
                              edge: ResizeEdge, location: &Point) {
    compositor::start_interactive_resize(&view, edge, location);
    trace!("view_request_resize: edge {:?}, to {}",
             edge, location);
}

pub extern fn keyboard_key(_view: WlcView, _time: u32, mods: &KeyboardModifiers,
                       key: u32, state: KeyState) -> bool {
    if state == KeyState::Pressed {
        // TODO this function will throw an error in Rustwlc right now
        // let mut keys = keyboard::get_current_keys().into_iter()
        //      .map(|&k| Keysym::from(k)).collect();
        let sym = keyboard::get_keysym_for_key(key, &KeyMod::empty());
        let keys = vec![sym];

        let press = KeyPress::new(mods.mods, keys);
        if let Some(action) = keys::get(&press) {
            debug!("[key] Found an action for {:?}", press);
            action();
            return EVENT_HANDLED;
        }
    }

    return EVENT_PASS_THROUGH;
}

pub extern fn pointer_button(view: WlcView, _time: u32,
                         mods: &KeyboardModifiers, button: u32,
                             state: ButtonState, point: &Point) -> bool {
    compositor::on_pointer_button(view, _time, mods, button, state, point)
}

pub extern fn pointer_scroll(_view: WlcView, _time: u32,
                         _mods_ptr: &KeyboardModifiers, axis: ScrollAxis,
                         heights: [f64; 2]) -> bool {
    trace!("pointer_scroll: {:?} {:?}", axis,
           heights.iter().map(|f| f.clone().round()).collect::<Vec<f64>>());
    false
}

pub extern fn pointer_motion(_view: WlcView, _time: u32, point: &Point) -> bool {
    pointer::set_position(point);
    compositor::on_pointer_motion(_view, _time, point)
}

/*
pub extern fn touch(view: WlcView, time: u32, mods_ptr: &KeyboardModifiers,
               touch: TouchType, key: i32, point_ptr: &Point) -> bool {
    false
}
*/

pub extern fn compositor_ready() {
    info!("Preparing compositor!");
    info!("Initializing Lua...");
    lua::init();
}

pub extern fn compositor_terminating() {
    info!("Compositor terminating!");
    lua::send(lua::LuaQuery::Terminate).ok();
}

pub fn init() {
    use rustwlc::callback;

    callback::output_created(output_created);
    callback::output_destroyed(output_destroyed);
    callback::output_focus(output_focus);
    callback::output_resolution(output_resolution);
    callback::view_created(view_created);
    callback::view_destroyed(view_destroyed);
    callback::view_focus(view_focus);
    callback::view_move_to_output(view_move_to_output);
    callback::view_request_geometry(view_request_geometry);
    callback::view_request_state(view_request_state);
    callback::view_request_move(view_request_move);
    callback::view_request_resize(view_request_resize);
    callback::keyboard_key(keyboard_key);
    callback::pointer_button(pointer_button);
    callback::pointer_scroll(pointer_scroll);
    callback::pointer_motion(pointer_motion);
    callback::compositor_ready(compositor_ready);
    callback::compositor_terminate(compositor_terminating);
    trace!("Registered wlc callbacks");
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! X.509 parsing.

use crate::cert;
use crate::cert::der;
use crate::cert::der::Tag;
use crate::cert::Algo;
use crate::cert::Cert;
use crate::cert::Error;
use crate::cert::Name;
use crate::cert::PublicKeyParams;

#[cfg(test)]
#[path = "x509_test.rs"]
mod test;

/// OIDs used by the parser.
#[allow(unused)]
mod oid {
    use crate::cert::der::Oid;

    pub const RSA_ENCRYPTION: Oid = oid!(1, 2, 840, 113549, 1, 1, 1);
    pub const RSA_PKCS1_SHA256: Oid = oid!(1, 2, 840, 113549, 1, 1, 11);

    pub const KEY_USAGE: Oid = oid!(2, 5, 29, 15);
    pub const BASIC_CONSTRAINTS: Oid = oid!(2, 5, 29, 19);
    pub const TCG_DICE_FWID: Oid = oid!(2, 23, 133, 5, 4, 1);
}

/// Parses an RFC3279 algorithm identifier.
fn parse_algo(buf: &mut untrusted::Reader) -> Result<Algo, Error> {
    match der::oid(buf)? {
        oid::RSA_PKCS1_SHA256 => {
            der::null(buf)?;
            Ok(Algo::RsaPkcs1Sha256)
        }
        _ => Err(Error::UnknownAlgorithm),
    }
}

/// Parses an X.509 certificate.
///
/// This function performs several aggressive checks to reject any and all
/// certificates that do not fit our profile. These include:
/// - Version must be v3.
/// - Extensions must be present.
/// - `keyUsage`, `authorityKeyIdentifier`, `subjectKeyIdentifier` must all
///   be present.
///   - A `keyUsage` with `keyCertSign` and any other usage is rejected.
///
/// All of the above are treated as encoding errors.
pub fn parse<'cert>(
    cert: &'cert [u8],
    format: cert::CertFormat,
    key: Option<&PublicKeyParams<'_>>,
    ciphers: &mut impl cert::Ciphers,
) -> Result<Cert<'cert>, Error> {
    let buf = untrusted::Input::from(cert);
    let (cert, tbs, sig_algo, sig) =
        buf.read_all(Error::BadEncoding, |buf| {
            der::tagged(Tag::SEQUENCE, buf, |buf| {
                let mark = buf.mark();
                let tbs = der::parse(Tag::SEQUENCE, buf)?;
                let tbs_bytes =
                    buf.get_input_between_marks(mark, buf.mark())?;
                let sig_algo_bytes = der::parse(Tag::SEQUENCE, buf)?;
                let sig_algo =
                    sig_algo_bytes.read_all(Error::BadEncoding, parse_algo)?;

                let sig = der::bits_total(buf)?;

                let cert = tbs.read_all(Error::BadEncoding, |buf| {
                    parse_tbs(format, sig_algo_bytes, buf)
                })?;

                Ok((
                    cert,
                    tbs_bytes.as_slice_less_safe(),
                    sig_algo,
                    sig.as_slice_less_safe(),
                ))
            })
        })?;

    let key = key.unwrap_or_else(|| cert.subject_key());
    if !key.is_params_for(sig_algo) {
        return Err(Error::WrongAlgorithm);
    }

    let verifier = ciphers
        .verifier(sig_algo, key)
        .ok_or(Error::UnknownAlgorithm)?;
    verifier
        .verify(&[tbs], sig)
        .map_err(|_| Error::BadSignature)?;

    Ok(cert)
}

fn parse_tbs<'cert>(
    format: cert::CertFormat,
    sig_algo_bytes: untrusted::Input,
    buf: &mut untrusted::Reader<'cert>,
) -> Result<Cert<'cert>, Error> {
    // Although the version field is optional, we reject all non-v3
    // certificates, which require this field.
    //
    // We treat this as a syntactic, rather than semantic, error.
    der::tagged(Tag::context_specific(0), buf, |mut buf| {
        // `v3` certificates are encoded as an `INTEGER { 2 }`.
        if der::u32(&mut buf)? != 2 {
            return Err(Error::BadEncoding);
        }
        Ok(())
    })?;

    // The certificate serial number must be a positive `INTEGER` consisting
    // of at most 20 octets.
    //
    // Like with the version, this is a syntactic error. The value itself
    // is discarded.
    let serial = der::uint(buf)?.as_slice_less_safe();
    if serial[0] == 0 || serial.len() > 20 {
        return Err(Error::BadEncoding);
    }

    // A mismatch between the inner and outer signature algorithm
    // identifiers (byte-for-byte) is a syntax error.
    let sig_algo2 = der::parse(Tag::SEQUENCE, buf)?;
    if sig_algo2 != sig_algo_bytes {
        return Err(Error::BadEncoding);
    }

    // The issuer is an opaque name.
    let issuer = Name(der::parse(Tag::SEQUENCE, buf)?.as_slice_less_safe());

    // TODO: Provide some mechanism for the user to pass in a clock, if
    // available. X.509 time parsing incurs significant complexity, so
    // it probably shouldn't be implemented until it's absolutely necessary.
    let _validity = der::parse(Tag::SEQUENCE, buf)?;

    // The subject is also opaque
    let subject = Name(der::parse(Tag::SEQUENCE, buf)?.as_slice_less_safe());

    let subject_key = der::tagged(Tag::SEQUENCE, buf, |buf| {
        let (algo, aparams) = der::tagged(Tag::SEQUENCE, buf, |buf| {
            let algo = der::oid(buf)?;
            let aparams = buf.read_bytes_to_end();
            Ok((algo, aparams))
        })?;

        der::bits_total(buf)?.read_all(Error::BadEncoding, |buf| match algo {
            oid::RSA_ENCRYPTION => {
                aparams.read_all(Error::BadEncoding, der::null)?;
                der::tagged(Tag::SEQUENCE, buf, |buf| {
                    let mut modulus = der::uint(buf)?.as_slice_less_safe();
                    // DER inserts a leading zero sometimes (to disambiguate
                    // negative integers) so we need to remove it.
                    if modulus[0] == 0 {
                        modulus = &modulus[1..];
                    }
                    let mut exponent = der::uint(buf)?.as_slice_less_safe();
                    if exponent[0] == 0 {
                        exponent = &exponent[1..];
                    }
                    Ok(PublicKeyParams::Rsa { modulus, exponent })
                })
            }
            _ => Err(Error::UnknownAlgorithm),
        })
    })?;

    // We don't care about the UIDs at all.
    let _issuer_uid = der::opt(Tag::context_specific(1), buf)?;
    let _subject_uid = der::opt(Tag::context_specific(2), buf)?;

    // Extensions are mandatory.
    let mut extns = Extensions::default();
    der::tagged(Tag::context_specific(3), buf, |buf| {
        der::tagged(Tag::SEQUENCE, buf, |buf| {
            while !buf.at_end() {
                parse_extn(buf, &mut extns)?;
            }
            Ok(())
        })
    })?;

    let is_cert_sign = match extns.is_cert_sign {
        Some(b) => b,
        _ => return Err(Error::BadEncoding),
    };

    let is_ca = match &extns.basic_constraints {
        Some(bc) => bc.is_ca,
        _ => false,
    };

    // CA certificates must always specify keyCertSign as a valid usage. To
    // fail to do so is a syntax error.
    if is_ca != is_cert_sign {
        return Err(Error::BadEncoding);
    }

    Ok(Cert {
        format,
        issuer,
        subject,
        subject_key,
        basic_constraints: extns.basic_constraints,
        is_cert_sign,
    })
}

#[derive(Default)]
struct Extensions {
    basic_constraints: Option<cert::BasicConstraints>,
    is_cert_sign: Option<bool>,
}

fn parse_extn(
    buf: &mut untrusted::Reader,
    extns: &mut Extensions,
) -> Result<(), Error> {
    der::tagged(Tag::SEQUENCE, buf, |buf| {
        let oid = der::oid(buf)?;
        let is_critical = der::opt_bool(buf)?.unwrap_or(false);
        der::tagged(Tag::OCTET_STRING, buf, |buf| match oid {
            oid::KEY_USAGE => {
                if extns.is_cert_sign.is_some() {
                    return Err(Error::BadEncoding);
                }

                // Constants from RFC5280 S4.2.1.3. I.e.,
                // ```asn1
                // KeyUsage ::= BIT STRING {
                //   digitalSignature        (0),
                //   nonRepudiation          (1),
                //   keyEncipherment         (2),
                //   dataEncipherment        (3),
                //   keyAgreement            (4),
                //   keyCertSign             (5),
                //   cRLSign                 (6),
                //   encipherOnly            (7),
                //   decipherOnly            (8),
                // }
                // ```
                //
                // Note that the bits in a `BIT STRING` are big endian
                // within bytes, so we need to "shift from the left"
                // instead.
                const CERT_SIGN_MASK: u16 = 1 << (15 - 5);

                der::bits_partial(buf)?.read_all(Error::BadEncoding, |buf| {
                    let first_byte = buf.read_byte().unwrap_or(0);
                    let second_byte = buf.read_byte().unwrap_or(0);
                    // NOTE: This technically drops any "domain-specific"
                    // bits on the ground, but we have zero interest in retaining
                    // those.
                    let bits = u16::from_be_bytes([first_byte, second_byte]);

                    extns.is_cert_sign = Some(bits & CERT_SIGN_MASK != 0);
                    if extns.is_cert_sign == Some(true)
                        && bits != CERT_SIGN_MASK
                    {
                        // For domain separation reasons, we reject all
                        // certificates that mix certificate signing with any
                        // other usage.
                        return Err(Error::BadEncoding);
                    }

                    Ok(())
                })
            }
            oid::BASIC_CONSTRAINTS => {
                if extns.basic_constraints.is_some() {
                    return Err(Error::BadEncoding);
                }
                der::tagged(Tag::SEQUENCE, buf, |buf| {
                    let is_ca = der::opt_bool(buf)?.unwrap_or(false);
                    let path_len_constraint = der::opt_u32(buf)?;
                    extns.basic_constraints = Some(cert::BasicConstraints {
                        is_ca,
                        path_len_constraint,
                    });
                    Ok(())
                })
            }
            _ if is_critical => Err(Error::BadEncoding),
            _ => {
                // Drop the rest of the bytes on the floor. `untrusted` requires
                // us to explicitly use all bytes, but we just want to ignore
                // them in this case.
                let _ = buf.read_bytes_to_end();
                Ok(())
            }
        })
    })
}
<commit_msg>[cert] Extract out KeyUsage logic for reuse on the CWT side<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! X.509 parsing.

use crate::cert;
use crate::cert::der;
use crate::cert::der::Tag;
use crate::cert::Algo;
use crate::cert::Cert;
use crate::cert::Error;
use crate::cert::Name;
use crate::cert::PublicKeyParams;

#[cfg(test)]
#[path = "x509_test.rs"]
mod test;

/// OIDs used by the parser.
#[allow(unused)]
mod oid {
    use crate::cert::der::Oid;

    pub const RSA_ENCRYPTION: Oid = oid!(1, 2, 840, 113549, 1, 1, 1);
    pub const RSA_PKCS1_SHA256: Oid = oid!(1, 2, 840, 113549, 1, 1, 11);

    pub const KEY_USAGE: Oid = oid!(2, 5, 29, 15);
    pub const BASIC_CONSTRAINTS: Oid = oid!(2, 5, 29, 19);
    pub const TCG_DICE_FWID: Oid = oid!(2, 23, 133, 5, 4, 1);
}

/// Parses an RFC3279 algorithm identifier.
fn parse_algo(buf: &mut untrusted::Reader) -> Result<Algo, Error> {
    match der::oid(buf)? {
        oid::RSA_PKCS1_SHA256 => {
            der::null(buf)?;
            Ok(Algo::RsaPkcs1Sha256)
        }
        _ => Err(Error::UnknownAlgorithm),
    }
}

/// Parses an X.509 certificate.
///
/// This function performs several aggressive checks to reject any and all
/// certificates that do not fit our profile. These include:
/// - Version must be v3.
/// - Extensions must be present.
/// - `keyUsage`, `authorityKeyIdentifier`, `subjectKeyIdentifier` must all
///   be present.
///   - A `keyUsage` with `keyCertSign` and any other usage is rejected.
///
/// All of the above are treated as encoding errors.
pub fn parse<'cert>(
    cert: &'cert [u8],
    format: cert::CertFormat,
    key: Option<&PublicKeyParams<'_>>,
    ciphers: &mut impl cert::Ciphers,
) -> Result<Cert<'cert>, Error> {
    let buf = untrusted::Input::from(cert);
    let (cert, tbs, sig_algo, sig) =
        buf.read_all(Error::BadEncoding, |buf| {
            der::tagged(Tag::SEQUENCE, buf, |buf| {
                let mark = buf.mark();
                let tbs = der::parse(Tag::SEQUENCE, buf)?;
                let tbs_bytes =
                    buf.get_input_between_marks(mark, buf.mark())?;
                let sig_algo_bytes = der::parse(Tag::SEQUENCE, buf)?;
                let sig_algo =
                    sig_algo_bytes.read_all(Error::BadEncoding, parse_algo)?;

                let sig = der::bits_total(buf)?;

                let cert = tbs.read_all(Error::BadEncoding, |buf| {
                    parse_tbs(format, sig_algo_bytes, buf)
                })?;

                Ok((
                    cert,
                    tbs_bytes.as_slice_less_safe(),
                    sig_algo,
                    sig.as_slice_less_safe(),
                ))
            })
        })?;

    let key = key.unwrap_or_else(|| cert.subject_key());
    if !key.is_params_for(sig_algo) {
        return Err(Error::WrongAlgorithm);
    }

    let verifier = ciphers
        .verifier(sig_algo, key)
        .ok_or(Error::UnknownAlgorithm)?;
    verifier
        .verify(&[tbs], sig)
        .map_err(|_| Error::BadSignature)?;

    Ok(cert)
}

fn parse_tbs<'cert>(
    format: cert::CertFormat,
    sig_algo_bytes: untrusted::Input,
    buf: &mut untrusted::Reader<'cert>,
) -> Result<Cert<'cert>, Error> {
    // Although the version field is optional, we reject all non-v3
    // certificates, which require this field.
    //
    // We treat this as a syntactic, rather than semantic, error.
    der::tagged(Tag::context_specific(0), buf, |mut buf| {
        // `v3` certificates are encoded as an `INTEGER { 2 }`.
        if der::u32(&mut buf)? != 2 {
            return Err(Error::BadEncoding);
        }
        Ok(())
    })?;

    // The certificate serial number must be a positive `INTEGER` consisting
    // of at most 20 octets.
    //
    // Like with the version, this is a syntactic error. The value itself
    // is discarded.
    let serial = der::uint(buf)?.as_slice_less_safe();
    if serial[0] == 0 || serial.len() > 20 {
        return Err(Error::BadEncoding);
    }

    // A mismatch between the inner and outer signature algorithm
    // identifiers (byte-for-byte) is a syntax error.
    let sig_algo2 = der::parse(Tag::SEQUENCE, buf)?;
    if sig_algo2 != sig_algo_bytes {
        return Err(Error::BadEncoding);
    }

    // The issuer is an opaque name.
    let issuer = Name(der::parse(Tag::SEQUENCE, buf)?.as_slice_less_safe());

    // TODO: Provide some mechanism for the user to pass in a clock, if
    // available. X.509 time parsing incurs significant complexity, so
    // it probably shouldn't be implemented until it's absolutely necessary.
    let _validity = der::parse(Tag::SEQUENCE, buf)?;

    // The subject is also opaque
    let subject = Name(der::parse(Tag::SEQUENCE, buf)?.as_slice_less_safe());

    let subject_key = der::tagged(Tag::SEQUENCE, buf, |buf| {
        let (algo, aparams) = der::tagged(Tag::SEQUENCE, buf, |buf| {
            let algo = der::oid(buf)?;
            let aparams = buf.read_bytes_to_end();
            Ok((algo, aparams))
        })?;

        der::bits_total(buf)?.read_all(Error::BadEncoding, |buf| match algo {
            oid::RSA_ENCRYPTION => {
                aparams.read_all(Error::BadEncoding, der::null)?;
                der::tagged(Tag::SEQUENCE, buf, |buf| {
                    let mut modulus = der::uint(buf)?.as_slice_less_safe();
                    // DER inserts a leading zero sometimes (to disambiguate
                    // negative integers) so we need to remove it.
                    if modulus[0] == 0 {
                        modulus = &modulus[1..];
                    }
                    let mut exponent = der::uint(buf)?.as_slice_less_safe();
                    if exponent[0] == 0 {
                        exponent = &exponent[1..];
                    }
                    Ok(PublicKeyParams::Rsa { modulus, exponent })
                })
            }
            _ => Err(Error::UnknownAlgorithm),
        })
    })?;

    // We don't care about the UIDs at all.
    let _issuer_uid = der::opt(Tag::context_specific(1), buf)?;
    let _subject_uid = der::opt(Tag::context_specific(2), buf)?;

    // Extensions are mandatory.
    let mut extns = Extensions::default();
    der::tagged(Tag::context_specific(3), buf, |buf| {
        der::tagged(Tag::SEQUENCE, buf, |buf| {
            while !buf.at_end() {
                parse_extn(buf, &mut extns)?;
            }
            Ok(())
        })
    })?;

    let is_cert_sign = match extns.key_usage {
        Some(b) => b.is_cert_sign(),
        _ => return Err(Error::BadEncoding),
    };

    let is_ca = match &extns.basic_constraints {
        Some(bc) => bc.is_ca,
        _ => false,
    };

    // CA certificates must always specify keyCertSign as a valid usage. To
    // fail to do so is a syntax error.
    if is_ca != is_cert_sign {
        return Err(Error::BadEncoding);
    }

    Ok(Cert {
        format,
        issuer,
        subject,
        subject_key,
        basic_constraints: extns.basic_constraints,
        is_cert_sign,
    })
}

/// An X.509 `KeyUsage` value, representing the valid usages of a subject
/// public key.
///
/// The memory representation is little-endian, meaning that for X.509, we
/// need to reverse the bytes and the bits within, but not for CBOR.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct KeyUsage(u16);

impl KeyUsage {
    pub fn from_be(bytes: &[u8]) -> Result<Self, Error> {
        Self::from_bits(bytes, true)
    }

    pub fn from_le(bytes: &[u8]) -> Result<Self, Error> {
        Self::from_bits(bytes, false)
    }

    fn from_bits(bytes: &[u8], is_be: bool) -> Result<Self, Error> {
        let b1 = *bytes.get(0).unwrap_or(&0);
        let b2 = *bytes.get(1).unwrap_or(&0);
        let val = if is_be {
            // Note that ASN.1 BIT STRINGs are actually little-endian bytes;
            // it is merely the bits within the bytes that are big endian!
            u16::from_le_bytes([b1.reverse_bits(), b2.reverse_bits()])
        } else {
            u16::from_le_bytes([b1, b2])
        };

        // NOTE: This technically drops any "domain-specific"
        // bits on the ground, but we have zero interest in retaining
        // those.
        let bits = Self(val);

        // For domain separation reasons, we reject all
        // certificates that mix certificate signing with any
        // other usage.
        if bits.is_cert_sign() && bits.0 != Self::CERT_SIGN_MASK {
            return Err(Error::BadEncoding);
        }

        Ok(bits)
    }

    // Constants from RFC5280 S4.2.1.3. I.e.,
    // ```asn1
    // KeyUsage ::= BIT STRING {
    //   digitalSignature        (0),
    //   nonRepudiation          (1),
    //   keyEncipherment         (2),
    //   dataEncipherment        (3),
    //   keyAgreement            (4),
    //   keyCertSign             (5),
    //   cRLSign                 (6),
    //   encipherOnly            (7),
    //   decipherOnly            (8),
    // }
    // ```
    // Add constants as needed.
    const CERT_SIGN_MASK: u16 = 1 << 5;

    pub fn is_cert_sign(self) -> bool {
        self.0 & Self::CERT_SIGN_MASK != 0
    }
}

#[derive(Default)]
struct Extensions {
    basic_constraints: Option<cert::BasicConstraints>,
    key_usage: Option<KeyUsage>,
}

fn parse_extn(
    buf: &mut untrusted::Reader,
    extns: &mut Extensions,
) -> Result<(), Error> {
    der::tagged(Tag::SEQUENCE, buf, |buf| {
        let oid = der::oid(buf)?;
        let is_critical = der::opt_bool(buf)?.unwrap_or(false);
        der::tagged(Tag::OCTET_STRING, buf, |buf| match oid {
            oid::KEY_USAGE => {
                if extns.key_usage.is_some() {
                    return Err(Error::BadEncoding);
                }

                der::bits_partial(buf)?.read_all(Error::BadEncoding, |buf| {
                    let bytes = buf.read_bytes_to_end().as_slice_less_safe();
                    extns.key_usage = Some(KeyUsage::from_be(bytes)?);
                    Ok(())
                })
            }
            oid::BASIC_CONSTRAINTS => {
                if extns.basic_constraints.is_some() {
                    return Err(Error::BadEncoding);
                }
                der::tagged(Tag::SEQUENCE, buf, |buf| {
                    let is_ca = der::opt_bool(buf)?.unwrap_or(false);
                    let path_len_constraint = der::opt_u32(buf)?;
                    extns.basic_constraints = Some(cert::BasicConstraints {
                        is_ca,
                        path_len_constraint,
                    });
                    Ok(())
                })
            }
            _ if is_critical => Err(Error::BadEncoding),
            _ => {
                // Drop the rest of the bytes on the floor. `untrusted` requires
                // us to explicitly use all bytes, but we just want to ignore
                // them in this case.
                let _ = buf.read_bytes_to_end();
                Ok(())
            }
        })
    })
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::sync::mpsc::channel;
use std::sync::{Mutex, Arc};
use std::time::{Instant, Duration};
use std::thread;
use std::net;
use std::hash::Hash;
use std::time::{SystemTime, UNIX_EPOCH};
use std::path::{Path, PathBuf};

use notify::{
    RecommendedWatcher,
    Watcher,
    RecursiveMode,
    DebouncedEvent
};

use handlebars::Handlebars;
use percent_encoding::percent_decode;

use hyper::StatusCode;

use cargo_shim::{
    Profile,
    TargetKind,
    CargoTarget
};

use build::{BuildArgs, Project, PathKind, ShouldTriggerRebuild};
use http_utils::{
    SimpleServer,
    response_from_data,
    response_from_status,
    response_from_file
};

use deployment::{Deployment, ArtifactKind};
use error::Error;

fn auto_reload_code( hash: u32 ) -> String {
    // TODO: We probably should do this with with Websockets.
    const TEMPLATE: &'static str = r##"
        window.addEventListener( "load", function() {
            var current_build_hash = {{{current_build_hash}}};
            function try_reload() {
                var req = new XMLHttpRequest();
                req.addEventListener( "load" , function() {
                    if( req.responseText != current_build_hash ) {
                        window.location.reload( true );
                    }
                });
                req.addEventListener( "loadend", function() {
                    setTimeout( try_reload, 500 );
                });
                req.open( "GET", "/__cargo-web__/build_hash" );
                req.send();
            }
            try_reload();
        });
    "##;

    let handlebars = Handlebars::new();
    let mut template_data = BTreeMap::new();
    template_data.insert( "current_build_hash", hash );
    handlebars.render_template( TEMPLATE, &template_data ).unwrap()
}

fn hash< T: Hash >( value: T ) -> u64 {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::Hasher;

    let mut hasher = DefaultHasher::new();
    value.hash( &mut hasher );
    hasher.finish()
}

#[derive(Clone)]
struct Counter {
    seed: u64,
    value: u64
}

impl Counter {
    fn new() -> Self {
        let timestamp = SystemTime::now().duration_since( UNIX_EPOCH ).unwrap();
        let seed = hash( timestamp.as_secs() ) ^ hash( timestamp.subsec_nanos() );

        Counter {
            seed,
            value: 0
        }
    }

    fn next( &self ) -> Self {
        Counter {
            seed: self.seed,
            value: self.value + 1
        }
    }

    fn get_hash( &self ) -> u32 {
        hash( self.seed + self.value ) as u32
    }
}

impl From< PathKind > for RecursiveMode {
    fn from( mode: PathKind ) -> Self {
        match mode {
            PathKind::File => RecursiveMode::NonRecursive,
            PathKind::Directory => RecursiveMode::Recursive
        }
    }
}

struct LastBuild {
    counter: Counter,
    deployment: Deployment,
    project: Project,
    target: CargoTarget
}

fn select_target( project: &Project ) -> Result< CargoTarget, Error > {
    let target = {
        let targets = project.target_or_select( |target| {
            target.kind == TargetKind::Bin ||
            (target.kind == TargetKind::CDyLib && project.backend().is_native_wasm())
        })?;

        if targets.is_empty() {
            return Err(
                Error::ConfigurationError( format!( "cannot start a webserver for a crate which is a library!" ) )
            );
        }

        targets[ 0 ].clone()
    };

    Ok( target )
}

impl LastBuild {
    fn new( project: Project, target: CargoTarget, counter: Counter ) -> Result< Self, Error > {
        let config = project.aggregate_configuration( Profile::Main )?;
        let result = project.build( &config, &target )?;
        let deployment = Deployment::new( project.package(), &target, &result )?;

        Ok( LastBuild {
            counter,
            deployment,
            project,
            target
        })
    }

    fn get_build_hash( &self ) -> u32 {
        self.counter.get_hash()
    }
}

fn should_rebuild( paths_to_watch: &[(PathBuf, PathKind, ShouldTriggerRebuild)], path: &Path ) -> bool {
    paths_to_watch.iter()
        .filter( |&&(_, _, should_rebuild)| should_rebuild == ShouldTriggerRebuild::Yes )
        .any( |&(ref root, _, _)| path.starts_with( root ) )
}

fn watch_paths( watcher: &mut RecommendedWatcher, paths_to_watch: &[(PathBuf, PathKind, ShouldTriggerRebuild)] ) {
    for &(ref path, ref mode, _) in paths_to_watch {
        // TODO: Handle paths that currently don't exist which *will* be created.
        match watcher.watch( &path, (*mode).into() ) {
            Ok( _ ) => trace!( "Watching {:?} ({:?})", path, mode ),
            Err( error ) => trace!( "Failed to watch {:?} ({:?}): {:?}", path, mode, error )
        }
    }
}

fn monitor_for_changes_and_rebuild(
    last_build: Arc< Mutex< LastBuild > >
) -> Arc< Mutex< RecommendedWatcher > > {
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new( tx, Duration::from_millis( 500 ) ).unwrap();

    let last_paths_to_watch = {
        let last_build = last_build.lock().unwrap();
        let paths_to_watch = last_build.project.paths_to_watch( &last_build.target );
        debug!( "Found paths to watch: {:#?}", paths_to_watch );

        watch_paths( &mut watcher, &paths_to_watch );
        paths_to_watch.clone()
    };

    let watcher = Arc::new( Mutex::new( watcher ) );
    let weak_watcher = Arc::downgrade( &watcher );

    thread::spawn( move || {
        let rx = rx;
        let mut last_paths_to_watch = last_paths_to_watch;

        while let Ok( event ) = rx.recv() {
            trace!( "Watch event: {:?}", event );
            let should_rebuild = match event {
                DebouncedEvent::Create( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                DebouncedEvent::Remove( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                DebouncedEvent::Rename( ref old_path, ref new_path ) => {
                    should_rebuild( &last_paths_to_watch, old_path ) ||
                    should_rebuild( &last_paths_to_watch, new_path )
                },
                DebouncedEvent::Write( ref path ) => should_rebuild( &last_paths_to_watch, path ),
                _ => continue
            };

            if !should_rebuild {
                trace!( "Nothing of consequence changed; bumping build counter without rebuilding" );
                let mut last_build = last_build.lock().unwrap();
                let counter = last_build.counter.next();
                last_build.counter = counter;
                continue;
            }

            eprintln!( "==== Triggering `cargo build` ====" );
            let (counter, build_args) = {
                let last_build = last_build.lock().unwrap();
                let counter = last_build.counter.next();
                let build_args = last_build.project.build_args().clone();
                (counter, build_args)
            };

            if let Ok( project ) = build_args.load_project() {
                if let Ok( target ) = select_target( &project ) {
                    let mut new_paths_to_watch = project.paths_to_watch( &target );

                    if new_paths_to_watch != last_paths_to_watch {
                        debug!( "Paths to watch have changed; new paths to watch: {:#?}", new_paths_to_watch );
                        if let Some( watcher ) = weak_watcher.upgrade() {
                            let mut watcher = watcher.lock().expect( "watcher was poisoned" );
                            for (path, _, _) in last_paths_to_watch {
                                let _ = watcher.unwatch( path );
                            }

                            watch_paths( &mut watcher, &new_paths_to_watch );
                        }
                        last_paths_to_watch = new_paths_to_watch;
                    }

                    if let Ok( new_build ) = LastBuild::new( project, target, counter ) {
                        *last_build.lock().unwrap() = new_build;
                    }
                }
            }
        }
    });

    watcher
}

pub fn command_start(
    build_args: BuildArgs,
    host: net::IpAddr,
    port: u16,
    open: bool,
    auto_reload: bool
) -> Result<(), Error> {
    let project = build_args.load_project()?;

    let last_build = {
        let target = select_target( &project )?;
        LastBuild::new( project, target, Counter::new() )?
    };

    let last_build = Arc::new( Mutex::new( last_build ) );
    let (target, js_url) = {
        let last_build = last_build.lock().unwrap();
        let target = last_build.target.clone();
        let js_url = last_build.deployment.js_url().to_owned();
        (target, js_url)
    };

    let _watcher = monitor_for_changes_and_rebuild( last_build.clone() );

    let target_name = target.name.clone();
    let address = net::SocketAddr::new(host, port);
    let server = SimpleServer::new(&address, move |request| {
        let path = request.uri().path();
        let path = percent_decode( path.as_bytes() ).decode_utf8().unwrap();
        let last_build = last_build.lock().unwrap();

        if path == "/__cargo-web__/build_hash" {
            let data = format!( "{}", last_build.get_build_hash() );
            return response_from_data("application/text", data.into_bytes());
        }

        if path == "/js/app.js" {
            eprintln!( "!!!!!!!!!!!!!!!!!!!!!" );
            eprintln!( "WARNING: `/js/app.js` is deprecated; you should update your HTML file to use `/{}.js` instead!", target_name );
            eprintln!( "!!!!!!!!!!!!!!!!!!!!!" );
        }

        debug!( "Received a request for {:?}", path );
        if let Some( mut artifact ) = last_build.deployment.get_by_url(&path) {
            if auto_reload && (path == "/" || path == "/index.html") {
                let result = artifact.map_text( |text| {
                    let injected_code = auto_reload_code( last_build.get_build_hash() );
                    text.replace( "<head>", &format!( "<head><script>{}</script>", injected_code ) )
                });
                artifact = match result {
                    Ok( artifact ) => artifact,
                    Err( error ) => {
                        warn!( "Cannot read {:?}: {:?}", path, error );
                        return response_from_status(StatusCode::INTERNAL_SERVER_ERROR);
                    }
                }
            }

            match artifact.kind {
                ArtifactKind::Data( data ) => {
                    return response_from_data(artifact.mime_type, data);
                },

                ArtifactKind::File( fp ) => {
                    return response_from_file(artifact.mime_type, fp);
                }
            }
        } else {
            response_from_status(StatusCode::NOT_FOUND)
        }
    });

    eprintln!( "" );
    eprintln!( "If you need to serve any extra files put them in the 'static' directory" );
    eprintln!( "in the root of your crate; they will be served alongside your application." );
    match target.kind {
        TargetKind::Example => eprintln!( "You can also put a '{}-static' directory in your 'examples' directory.", target.name ),
        TargetKind::Bin | TargetKind::CDyLib => eprintln!( "You can also put a 'static' directory in your 'src' directory." ),
        _ => unreachable!()
    };
    eprintln!( "" );
    eprintln!( "Your application is being served at '/{}'. It will be automatically", js_url );
    eprintln!( "rebuilt if you make any changes in your code." );
    eprintln!( "" );
    eprintln!( "You can access the web server at `http://{}`.", &address );

    if open {
        thread::spawn( move || {
            // Wait for server to start
            let start = Instant::now();
            let check_url = format!( "http://{}/__cargo-web__/build_hash", &address );
            let mut response = None;
            while start.elapsed() < Duration::from_secs( 10 ) && response.is_none() {
                thread::sleep( Duration::from_millis( 100 ) );
                response = ::reqwest::get( &check_url ).ok();
            }

            ::open::that( &format!( "http://{}", &address ) ).expect( "Failed to open browser" );
        });
    }

    server.run();

    Ok(())
}
<commit_msg>Only trigger build once if multiple FS events arrive at the same time<commit_after>use std::collections::BTreeMap;
use std::sync::mpsc::{channel, RecvTimeoutError};
use std::sync::{Mutex, Arc};
use std::time::{Instant, Duration};
use std::thread;
use std::net;
use std::hash::Hash;
use std::time::{SystemTime, UNIX_EPOCH};
use std::path::{Path, PathBuf};

use notify::{
    RecommendedWatcher,
    Watcher,
    RecursiveMode,
    DebouncedEvent
};

use handlebars::Handlebars;
use percent_encoding::percent_decode;

use hyper::StatusCode;

use cargo_shim::{
    Profile,
    TargetKind,
    CargoTarget
};

use build::{BuildArgs, Project, PathKind, ShouldTriggerRebuild};
use http_utils::{
    SimpleServer,
    response_from_data,
    response_from_status,
    response_from_file
};

use deployment::{Deployment, ArtifactKind};
use error::Error;

fn auto_reload_code( hash: u32 ) -> String {
    // TODO: We probably should do this with with Websockets.
    const TEMPLATE: &'static str = r##"
        window.addEventListener( "load", function() {
            var current_build_hash = {{{current_build_hash}}};
            function try_reload() {
                var req = new XMLHttpRequest();
                req.addEventListener( "load" , function() {
                    if( req.responseText != current_build_hash ) {
                        window.location.reload( true );
                    }
                });
                req.addEventListener( "loadend", function() {
                    setTimeout( try_reload, 500 );
                });
                req.open( "GET", "/__cargo-web__/build_hash" );
                req.send();
            }
            try_reload();
        });
    "##;

    let handlebars = Handlebars::new();
    let mut template_data = BTreeMap::new();
    template_data.insert( "current_build_hash", hash );
    handlebars.render_template( TEMPLATE, &template_data ).unwrap()
}

fn hash< T: Hash >( value: T ) -> u64 {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::Hasher;

    let mut hasher = DefaultHasher::new();
    value.hash( &mut hasher );
    hasher.finish()
}

#[derive(Clone)]
struct Counter {
    seed: u64,
    value: u64
}

impl Counter {
    fn new() -> Self {
        let timestamp = SystemTime::now().duration_since( UNIX_EPOCH ).unwrap();
        let seed = hash( timestamp.as_secs() ) ^ hash( timestamp.subsec_nanos() );

        Counter {
            seed,
            value: 0
        }
    }

    fn next( &self ) -> Self {
        Counter {
            seed: self.seed,
            value: self.value + 1
        }
    }

    fn get_hash( &self ) -> u32 {
        hash( self.seed + self.value ) as u32
    }
}

impl From< PathKind > for RecursiveMode {
    fn from( mode: PathKind ) -> Self {
        match mode {
            PathKind::File => RecursiveMode::NonRecursive,
            PathKind::Directory => RecursiveMode::Recursive
        }
    }
}

struct LastBuild {
    counter: Counter,
    deployment: Deployment,
    project: Project,
    target: CargoTarget
}

fn select_target( project: &Project ) -> Result< CargoTarget, Error > {
    let target = {
        let targets = project.target_or_select( |target| {
            target.kind == TargetKind::Bin ||
            (target.kind == TargetKind::CDyLib && project.backend().is_native_wasm())
        })?;

        if targets.is_empty() {
            return Err(
                Error::ConfigurationError( format!( "cannot start a webserver for a crate which is a library!" ) )
            );
        }

        targets[ 0 ].clone()
    };

    Ok( target )
}

impl LastBuild {
    fn new( project: Project, target: CargoTarget, counter: Counter ) -> Result< Self, Error > {
        let config = project.aggregate_configuration( Profile::Main )?;
        let result = project.build( &config, &target )?;
        let deployment = Deployment::new( project.package(), &target, &result )?;

        Ok( LastBuild {
            counter,
            deployment,
            project,
            target
        })
    }

    fn get_build_hash( &self ) -> u32 {
        self.counter.get_hash()
    }
}

fn should_rebuild( paths_to_watch: &[(PathBuf, PathKind, ShouldTriggerRebuild)], path: &Path ) -> bool {
    paths_to_watch.iter()
        .filter( |&&(_, _, should_rebuild)| should_rebuild == ShouldTriggerRebuild::Yes )
        .any( |&(ref root, _, _)| path.starts_with( root ) )
}

fn watch_paths( watcher: &mut RecommendedWatcher, paths_to_watch: &[(PathBuf, PathKind, ShouldTriggerRebuild)] ) {
    for &(ref path, ref mode, _) in paths_to_watch {
        // TODO: Handle paths that currently don't exist which *will* be created.
        match watcher.watch( &path, (*mode).into() ) {
            Ok( _ ) => trace!( "Watching {:?} ({:?})", path, mode ),
            Err( error ) => trace!( "Failed to watch {:?} ({:?}): {:?}", path, mode, error )
        }
    }
}

fn monitor_for_changes_and_rebuild(
    last_build: Arc< Mutex< LastBuild > >
) -> Arc< Mutex< RecommendedWatcher > > {
    let event_timeout = Duration::from_millis( 500 );
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new( tx, event_timeout ).unwrap();

    let last_paths_to_watch = {
        let last_build = last_build.lock().unwrap();
        let paths_to_watch = last_build.project.paths_to_watch( &last_build.target );
        debug!( "Found paths to watch: {:#?}", paths_to_watch );

        watch_paths( &mut watcher, &paths_to_watch );
        paths_to_watch.clone()
    };

    let watcher = Arc::new( Mutex::new( watcher ) );
    let weak_watcher = Arc::downgrade( &watcher );

    thread::spawn( move || {
        let rx = rx;
        let mut last_paths_to_watch = last_paths_to_watch;

        fn event_triggers_rebuild( event: DebouncedEvent, paths_to_watch: &Vec< ( PathBuf, PathKind, ShouldTriggerRebuild ) > ) -> bool {
            match event {
                DebouncedEvent::Create( ref path ) => should_rebuild( paths_to_watch, path ),
                DebouncedEvent::Remove( ref path ) => should_rebuild( paths_to_watch, path ),
                DebouncedEvent::Rename( ref old_path, ref new_path ) => {
                    should_rebuild( paths_to_watch, old_path ) ||
                    should_rebuild( paths_to_watch, new_path )
                },
                DebouncedEvent::Write( ref path ) => should_rebuild( paths_to_watch, path ),
                _ => false
            }
        }

        fn record_inconsequential_change(last_build: &Arc< Mutex< LastBuild > >) {
            trace!( "Nothing of consequence changed; bumping build counter without rebuilding" );
            let mut last_build = last_build.lock().unwrap();
            let counter = last_build.counter.next();
            last_build.counter = counter;
        }

        'outer: while let Ok( event ) = rx.recv() {
            trace!( "Watch event: {:?}", event );
            if !event_triggers_rebuild( event, &last_paths_to_watch ) {
                record_inconsequential_change( &last_build );
                continue;
            }

            trace!( "Starting build in {}ms if no more changes detected", event_timeout.as_millis() );
            let mut deadline = Instant::now() + event_timeout;
            while Instant::now() < deadline {
                match rx.recv_timeout( deadline - Instant::now() ) {
                    Ok( event ) => {
                        trace!( "Watch event: {:?}", event );
                        if !event_triggers_rebuild( event, &last_paths_to_watch ) {
                            record_inconsequential_change( &last_build );
                            continue;
                        }

                        trace!( "Noticed follow-up change; waiting additional {}ms for more", event_timeout.as_millis() );
                        deadline = Instant::now() + event_timeout;
                    }
                    Err( RecvTimeoutError::Timeout ) => {
                        trace!( "Timed out waiting for follow-up changes; proceeding to build" );
                        break;
                    }
                    Err( RecvTimeoutError::Disconnected ) => {
                        break 'outer;
                    }
                }
            }

            eprintln!( "==== Triggering `cargo build` ====" );
            let (counter, build_args) = {
                let last_build = last_build.lock().unwrap();
                let counter = last_build.counter.next();
                let build_args = last_build.project.build_args().clone();
                (counter, build_args)
            };

            if let Ok( project ) = build_args.load_project() {
                if let Ok( target ) = select_target( &project ) {
                    let new_paths_to_watch = project.paths_to_watch( &target );

                    if new_paths_to_watch != last_paths_to_watch {
                        debug!( "Paths to watch have changed; new paths to watch: {:#?}", new_paths_to_watch );
                        if let Some( watcher ) = weak_watcher.upgrade() {
                            let mut watcher = watcher.lock().expect( "watcher was poisoned" );
                            for (path, _, _) in last_paths_to_watch {
                                let _ = watcher.unwatch( path );
                            }

                            watch_paths( &mut watcher, &new_paths_to_watch );
                        }
                        last_paths_to_watch = new_paths_to_watch;
                    }

                    if let Ok( new_build ) = LastBuild::new( project, target, counter ) {
                        *last_build.lock().unwrap() = new_build;
                    }
                }
            }
        }
    });

    watcher
}

pub fn command_start(
    build_args: BuildArgs,
    host: net::IpAddr,
    port: u16,
    open: bool,
    auto_reload: bool
) -> Result<(), Error> {
    let project = build_args.load_project()?;

    let last_build = {
        let target = select_target( &project )?;
        LastBuild::new( project, target, Counter::new() )?
    };

    let last_build = Arc::new( Mutex::new( last_build ) );
    let (target, js_url) = {
        let last_build = last_build.lock().unwrap();
        let target = last_build.target.clone();
        let js_url = last_build.deployment.js_url().to_owned();
        (target, js_url)
    };

    let _watcher = monitor_for_changes_and_rebuild( last_build.clone() );

    let target_name = target.name.clone();
    let address = net::SocketAddr::new(host, port);
    let server = SimpleServer::new(&address, move |request| {
        let path = request.uri().path();
        let path = percent_decode( path.as_bytes() ).decode_utf8().unwrap();
        let last_build = last_build.lock().unwrap();

        if path == "/__cargo-web__/build_hash" {
            let data = format!( "{}", last_build.get_build_hash() );
            return response_from_data("application/text", data.into_bytes());
        }

        if path == "/js/app.js" {
            eprintln!( "!!!!!!!!!!!!!!!!!!!!!" );
            eprintln!( "WARNING: `/js/app.js` is deprecated; you should update your HTML file to use `/{}.js` instead!", target_name );
            eprintln!( "!!!!!!!!!!!!!!!!!!!!!" );
        }

        debug!( "Received a request for {:?}", path );
        if let Some( mut artifact ) = last_build.deployment.get_by_url(&path) {
            if auto_reload && (path == "/" || path == "/index.html") {
                let result = artifact.map_text( |text| {
                    let injected_code = auto_reload_code( last_build.get_build_hash() );
                    text.replace( "<head>", &format!( "<head><script>{}</script>", injected_code ) )
                });
                artifact = match result {
                    Ok( artifact ) => artifact,
                    Err( error ) => {
                        warn!( "Cannot read {:?}: {:?}", path, error );
                        return response_from_status(StatusCode::INTERNAL_SERVER_ERROR);
                    }
                }
            }

            match artifact.kind {
                ArtifactKind::Data( data ) => {
                    return response_from_data(artifact.mime_type, data);
                },

                ArtifactKind::File( fp ) => {
                    return response_from_file(artifact.mime_type, fp);
                }
            }
        } else {
            response_from_status(StatusCode::NOT_FOUND)
        }
    });

    eprintln!( "" );
    eprintln!( "If you need to serve any extra files put them in the 'static' directory" );
    eprintln!( "in the root of your crate; they will be served alongside your application." );
    match target.kind {
        TargetKind::Example => eprintln!( "You can also put a '{}-static' directory in your 'examples' directory.", target.name ),
        TargetKind::Bin | TargetKind::CDyLib => eprintln!( "You can also put a 'static' directory in your 'src' directory." ),
        _ => unreachable!()
    };
    eprintln!( "" );
    eprintln!( "Your application is being served at '/{}'. It will be automatically", js_url );
    eprintln!( "rebuilt if you make any changes in your code." );
    eprintln!( "" );
    eprintln!( "You can access the web server at `http://{}`.", &address );

    if open {
        thread::spawn( move || {
            // Wait for server to start
            let start = Instant::now();
            let check_url = format!( "http://{}/__cargo-web__/build_hash", &address );
            let mut response = None;
            while start.elapsed() < Duration::from_secs( 10 ) && response.is_none() {
                thread::sleep( Duration::from_millis( 100 ) );
                response = ::reqwest::get( &check_url ).ok();
            }

            ::open::that( &format!( "http://{}", &address ) ).expect( "Failed to open browser" );
        });
    }

    server.run();

    Ok(())
}
<|endoftext|>