<commit_before>use crate::proto::resource::Resource;
use crate::proto::trace::{
    InstrumentationLibrarySpans, ResourceSpans, Span, Span_Event, Span_Link, Span_SpanKind, Status,
    Status_StatusCode,
};
use crate::transform::common::{to_nanos, Attributes};
use opentelemetry::exporter::trace::SpanData;
use opentelemetry::trace::{Link, SpanKind, StatusCode};
use protobuf::reflect::ProtobufValue;
use protobuf::{RepeatedField, SingularPtrField};

impl From<SpanKind> for Span_SpanKind {
    fn from(span_kind: SpanKind) -> Self {
        match span_kind {
            SpanKind::Client => Span_SpanKind::SPAN_KIND_CLIENT,
            SpanKind::Consumer => Span_SpanKind::SPAN_KIND_CONSUMER,
            SpanKind::Internal => Span_SpanKind::SPAN_KIND_INTERNAL,
            SpanKind::Producer => Span_SpanKind::SPAN_KIND_PRODUCER,
            SpanKind::Server => Span_SpanKind::SPAN_KIND_SERVER,
        }
    }
}

impl From<StatusCode> for Status_StatusCode {
    fn from(status_code: StatusCode) -> Self {
        match status_code {
            StatusCode::Ok => Status_StatusCode::STATUS_CODE_OK,
            StatusCode::Unset => Status_StatusCode::STATUS_CODE_UNSET,
            StatusCode::Error => Status_StatusCode::STATUS_CODE_ERROR,
        }
    }
}

impl From<Link> for Span_Link {
    fn from(link: Link) -> Self {
        Span_Link {
            trace_id: link
                .span_context()
                .trace_id()
                .to_u128()
                .to_be_bytes()
                .to_vec(),
            span_id: link
                .span_context()
                .span_id()
                .to_u64()
                .to_be_bytes()
                .to_vec(),
            trace_state: link.span_context().trace_state().header(),
            attributes: Attributes::from(link.attributes().clone()).0,
            dropped_attributes_count: 0,
            ..Default::default()
        }
    }
}

impl From<SpanData> for ResourceSpans {
    fn from(source_span: SpanData) -> Self {
        ResourceSpans {
            resource: SingularPtrField::from(Some(Resource {
                attributes: Default::default(),
                dropped_attributes_count: 0,
                ..Default::default()
            })),
            instrumentation_library_spans: RepeatedField::from_vec(vec![
                InstrumentationLibrarySpans {
                    instrumentation_library: Default::default(),
                    spans: RepeatedField::from_vec(vec![Span {
                        trace_id: source_span
                            .span_context
                            .trace_id()
                            .to_u128()
                            .to_be_bytes()
                            .to_vec(),
                        span_id: source_span
                            .span_context
                            .span_id()
                            .to_u64()
                            .to_be_bytes()
                            .to_vec(),
                        trace_state: source_span.span_context.trace_state().header(),
                        parent_span_id: {
                            if source_span.parent_span_id.to_u64().is_non_zero() {
                                source_span.parent_span_id.to_u64().to_be_bytes().to_vec()
                            } else {
                                vec![]
                            }
                        },
                        name: source_span.name,
                        kind: source_span.span_kind.into(),
                        start_time_unix_nano: to_nanos(source_span.start_time),
                        end_time_unix_nano: to_nanos(source_span.end_time),
                        dropped_attributes_count: source_span.attributes.dropped_count(),
                        attributes: Attributes::from(source_span.attributes).0,
                        dropped_events_count: source_span.message_events.dropped_count(),
                        events: RepeatedField::from_vec(
                            source_span
                                .message_events
                                .into_iter()
                                .map(|event| Span_Event {
                                    time_unix_nano: to_nanos(event.timestamp),
                                    name: event.name,
                                    attributes: Attributes::from(event.attributes).0,
                                    dropped_attributes_count: 0,
                                    ..Default::default()
                                })
                                .collect(),
                        ),
                        dropped_links_count: source_span.links.dropped_count(),
                        links: RepeatedField::from_vec(
                            source_span.links.into_iter().map(Into::into).collect(),
                        ),
                        status: SingularPtrField::some(Status {
                            code: Status_StatusCode::from(source_span.status_code),
                            message: source_span.status_message,
                            ..Default::default()
                        }),
                        ..Default::default()
                    }]),
                    ..Default::default()
                },
            ]),
            ..Default::default()
        }
    }
}
<commit_msg>fix(otlp): propagate Resource from Span to proto (#366)<commit_after>use crate::proto::resource::Resource;
use crate::proto::trace::{
    InstrumentationLibrarySpans, ResourceSpans, Span, Span_Event, Span_Link, Span_SpanKind, Status,
    Status_StatusCode,
};
use crate::transform::common::{to_nanos, Attributes};
use opentelemetry::exporter::trace::SpanData;
use opentelemetry::trace::{Link, SpanKind, StatusCode};
use protobuf::reflect::ProtobufValue;
use protobuf::{RepeatedField, SingularPtrField};

impl From<SpanKind> for Span_SpanKind {
    fn from(span_kind: SpanKind) -> Self {
        match span_kind {
            SpanKind::Client => Span_SpanKind::SPAN_KIND_CLIENT,
            SpanKind::Consumer => Span_SpanKind::SPAN_KIND_CONSUMER,
            SpanKind::Internal => Span_SpanKind::SPAN_KIND_INTERNAL,
            SpanKind::Producer => Span_SpanKind::SPAN_KIND_PRODUCER,
            SpanKind::Server => Span_SpanKind::SPAN_KIND_SERVER,
        }
    }
}

impl From<StatusCode> for Status_StatusCode {
    fn from(status_code: StatusCode) -> Self {
        match status_code {
            StatusCode::Ok => Status_StatusCode::STATUS_CODE_OK,
            StatusCode::Unset => Status_StatusCode::STATUS_CODE_UNSET,
            StatusCode::Error => Status_StatusCode::STATUS_CODE_ERROR,
        }
    }
}

impl From<Link> for Span_Link {
    fn from(link: Link) -> Self {
        Span_Link {
            trace_id: link
                .span_context()
                .trace_id()
                .to_u128()
                .to_be_bytes()
                .to_vec(),
            span_id: link
                .span_context()
                .span_id()
                .to_u64()
                .to_be_bytes()
                .to_vec(),
            trace_state: link.span_context().trace_state().header(),
            attributes: Attributes::from(link.attributes().clone()).0,
            dropped_attributes_count: 0,
            ..Default::default()
        }
    }
}

impl From<SpanData> for ResourceSpans {
    fn from(source_span: SpanData) -> Self {
        let resource_attributes: Attributes = source_span
            .resource
            .iter()
            .map(|(k, v)| opentelemetry::KeyValue::new(k.clone(), v.clone()))
            .collect::<Vec<_>>()
            .into();
        ResourceSpans {
            resource: SingularPtrField::from(Some(Resource {
                attributes: resource_attributes.0,
                dropped_attributes_count: 0,
                ..Default::default()
            })),
            instrumentation_library_spans: RepeatedField::from_vec(vec![
                InstrumentationLibrarySpans {
                    instrumentation_library: Default::default(),
                    spans: RepeatedField::from_vec(vec![Span {
                        trace_id: source_span
                            .span_context
                            .trace_id()
                            .to_u128()
                            .to_be_bytes()
                            .to_vec(),
                        span_id: source_span
                            .span_context
                            .span_id()
                            .to_u64()
                            .to_be_bytes()
                            .to_vec(),
                        trace_state: source_span.span_context.trace_state().header(),
                        parent_span_id: {
                            if source_span.parent_span_id.to_u64().is_non_zero() {
                                source_span.parent_span_id.to_u64().to_be_bytes().to_vec()
                            } else {
                                vec![]
                            }
                        },
                        name: source_span.name,
                        kind: source_span.span_kind.into(),
                        start_time_unix_nano: to_nanos(source_span.start_time),
                        end_time_unix_nano: to_nanos(source_span.end_time),
                        dropped_attributes_count: source_span.attributes.dropped_count(),
                        attributes: Attributes::from(source_span.attributes).0,
                        dropped_events_count: source_span.message_events.dropped_count(),
                        events: RepeatedField::from_vec(
                            source_span
                                .message_events
                                .into_iter()
                                .map(|event| Span_Event {
                                    time_unix_nano: to_nanos(event.timestamp),
                                    name: event.name,
                                    attributes: Attributes::from(event.attributes).0,
                                    dropped_attributes_count: 0,
                                    ..Default::default()
                                })
                                .collect(),
                        ),
                        dropped_links_count: source_span.links.dropped_count(),
                        links: RepeatedField::from_vec(
                            source_span.links.into_iter().map(Into::into).collect(),
                        ),
                        status: SingularPtrField::some(Status {
                            code: Status_StatusCode::from(source_span.status_code),
                            message: source_span.status_message,
                            ..Default::default()
                        }),
                        ..Default::default()
                    }]),
                    ..Default::default()
                },
            ]),
            ..Default::default()
        }
    }
}
<|endoftext|>


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

use std::env;
use std::path::Path;
use std::process::Command;
use std::str::from_utf8;

extern crate tempdir;
use self::tempdir::TempDir;

/// Use rustc to expand and pretty print the crate into a single file,
/// removing any macros in the process.
pub fn expand(manifest_path: &Path,
              crate_name: &str,
              version: &str) -> Result<String, String> {
    let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));

    // Create a temp directory to use as a target dir for cargo expand, for
    // hygenic purposes.
    let target_dir = TempDir::new("cbindgen-expand")
                             .map_err(|_| format!("couldn't create a temp target directory"))?;

    let mut cmd = Command::new(cargo);
    cmd.env("CARGO_TARGET_DIR", target_dir.path());
    cmd.arg("rustc");
    cmd.arg("--manifest-path");
    cmd.arg(manifest_path);
    cmd.arg("--all-features");
    cmd.arg("-p");
    cmd.arg(&format!("{}:{}", crate_name, version));
    cmd.arg("--");
    cmd.arg("-Z");
    cmd.arg("unstable-options");
    cmd.arg("--pretty=expanded");
    let output = cmd.output().unwrap();

    let src = from_utf8(&output.stdout).unwrap().to_owned();
    let error = from_utf8(&output.stderr).unwrap().to_owned();

    if src.len() == 0 {
        Err(error)
    } else {
        Ok(src)
    }
}
<commit_msg>Allow CARGO_TARGET_DIR override in cargo_expand<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::env;
use std::path::Path;
use std::process::Command;
use std::str::from_utf8;

extern crate tempdir;
use self::tempdir::TempDir;

/// Use rustc to expand and pretty print the crate into a single file,
/// removing any macros in the process.
pub fn expand(manifest_path: &Path,
              crate_name: &str,
              version: &str) -> Result<String, String> {
    let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
    let run = |target_dir: &Path| {
        let mut cmd = Command::new(cargo);
        cmd.env("CARGO_TARGET_DIR", target_dir);
        cmd.arg("rustc");
        cmd.arg("--manifest-path");
        cmd.arg(manifest_path);
        cmd.arg("--all-features");
        cmd.arg("-p");
        cmd.arg(&format!("{}:{}", crate_name, version));
        cmd.arg("--");
        cmd.arg("-Z");
        cmd.arg("unstable-options");
        cmd.arg("--pretty=expanded");
        let output = cmd.output().unwrap();

        let src = from_utf8(&output.stdout).unwrap().to_owned();
        let error = from_utf8(&output.stderr).unwrap().to_owned();

        if src.len() == 0 {
            Err(error)
        } else {
            Ok(src)
        }
    };

    if let Ok(ref path) = env::var("CARGO_EXPAND_TARGET_DIR") {
        run(&Path::new(path))
    } else {
        // Create a temp directory to use as a target dir for cargo expand, for
        // hygenic purposes.
        let target_dir = TempDir::new("cbindgen-expand")
            .map_err(|_| format!("couldn't create a temp target directory"))?;

        run(target_dir.path())
    }
}
<|endoftext|>


<commit_before>/**
 * Copyright 2021 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate wasm_bindgen_rayon;

use rayon::prelude::*;
use wasm_bindgen::prelude::*;

// While this is a primitive test, it already requires Rayon to be spawned
// correctly, send tasks to different threads and collect the results back -
// which is all that we need to verify.
#[wasm_bindgen]
pub fn sum(numbers: &[i32]) -> i32 {
    numbers.par_iter().sum()
}
<commit_msg>Make example closer to docs<commit_after>/**
 * Copyright 2021 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pub use wasm_bindgen_rayon::init_thread_pool;

use rayon::prelude::*;
use wasm_bindgen::prelude::*;

// While this is a primitive test, it already requires Rayon to be spawned
// correctly, send tasks to different threads and collect the results back -
// which is all that we need to verify.
#[wasm_bindgen]
pub fn sum(numbers: &[i32]) -> i32 {
    numbers.par_iter().sum()
}
<|endoftext|>


<commit_before>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/// This module is responsible for handling the .delivery/config.json file

use std::collections::HashMap;
use std::default::Default;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use rustc_serialize::json;
use errors::{DeliveryError, Kind};
use types::DeliveryResult;
use git;
use utils::{walk_tree_for_path, read_file, copy_recursive, file_needs_updated};
use utils::path_join_many::PathJoinMany;

pub mod project;

#[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct DeliveryConfig {
    pub version: String,
    pub build_cookbook: HashMap<String, String>,
    pub skip_phases: Option<Vec<String>>,
    pub build_nodes: Option<HashMap<String, Vec<String>>>,
    pub job_dispatch: Option<JobDispatch>,
    pub dependencies: Option<Vec<String>>,
}

// JobDispatch Struct
//
// This structure has two main fields;
//   * version - The config version
//   * filters - Specific search nodes filters for each phase
//
// Example:
//  "job_dispatch": {
//    "version": "v2",
//    "filters": {
//      "unit": [
//        {
//          "platform_family": ["debian"],
//          "platform_version": ["14.04"]
//        },
//        {
//          "platform_family": ["rhel"]
//        }
//      ],
//      "syntax": [
//        {
//          "platform_family": ["debian"],
//          "platform_version": ["14.04"]
//        },
//        {
//          "platform_family": ["rhel"]
//        }
//      ]
//    }
//  }
#[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct JobDispatch {
    pub version: String,
    pub filters: Option<HashMap<String, Vec<HashMap<String, Vec<String>>>>>,
}

impl Default for DeliveryConfig {
    fn default() -> Self {
        let mut build_cookbook = HashMap::new();
        build_cookbook.insert("name".to_string(),
                              "build_cookbook".to_string());
        build_cookbook.insert("path".to_string(),
                              ".delivery/build_cookbook".to_string());

        let job_dispatch = JobDispatch {
            version: "v2".to_string(),
            filters: None,
        };

        DeliveryConfig {
            version: "2".to_string(),
            build_cookbook: build_cookbook,
            skip_phases: Some(Vec::new()),
            build_nodes: None,
            job_dispatch: Some(job_dispatch),
            dependencies: Some(Vec::new()),
        }
    }
}

// BuildCookbookLocation Enumarator
//
// The build_cokbook could be sourced from the following locations:
//   * Local       - On the project repo
//   * Git         - From a git server. (url)
//   * Supermarket - From a Supermarket Server
//   * Workflow    - From the Workflow Server
//   * ChefServer  - From the Chef Server
//
// Examples: https://docs.chef.io/config_json_delivery.html#examples
pub enum BuildCookbookLocation {
    Local,
    Git,
    Supermarket,
    Workflow,
    ChefServer,
}

impl DeliveryConfig {
    // Return the build_cookbook location
    //
    // Searches for the right field inside the build_cookbook HashMap
    // and translates it to a BuildCookbookLocation Enum, if none of
    // the possible entries exist, throws a `Err()`
    pub fn build_cookbook_location(&self) -> DeliveryResult<BuildCookbookLocation> {
        if self.build_cookbook.contains_key("path") {
            return Ok(BuildCookbookLocation::Local)
        }
        if self.build_cookbook.contains_key("git") {
            return Ok(BuildCookbookLocation::Git)
        }
        if self.build_cookbook.contains_key("supermarket") {
            return Ok(BuildCookbookLocation::Supermarket)
        }
        if self.build_cookbook.contains_key("enterprise") {
            return Ok(BuildCookbookLocation::Workflow)
        }
        if self.build_cookbook.contains_key("server") {
            return Ok(BuildCookbookLocation::ChefServer)
        }
        Err(DeliveryError{ kind: Kind::NoValidBuildCookbook, detail: None })
    }

    // Get the content of a specific build_cookbook field
    //
    // The build_cookbook is difined as a HashMap that we can easily extract
    // the content of a particular `key`, this will reduce complexity and code
    //
    // Example:
    // ```
    // config    = DeliveryConfig.default();
    // c_version = try!(config.build_cookbook_get("version"));
    // assert_eq!("2".to_string(), c_version);
    // ```
    pub fn build_cookbook_get(&self, key: &str) -> DeliveryResult<String> {
        self.build_cookbook.get(key)
            .ok_or(DeliveryError{
                kind: Kind::MissingBuildCookbookField,
                detail: Some(format!("Unable to find '{}' field.", key).to_string())
            }).map(|s| { s.to_owned() })
    }

    // Return the build_cookbook name
    //
    // A valid Delivery V2 config should always have a `name` entry
    // inside the build_cookbook HashMap.
    pub fn build_cookbook_name(&self) -> DeliveryResult<String> {
        self.build_cookbook_get("name")
    }

    /// Copy a provided `config.json` file to `.delivery/` of
    /// the project root path. Also verify that the config is
    /// valid and finally add/commit the changes.
    /// If the config already exists, skip this process.
    pub fn copy_config_file(config_f: &PathBuf,
                            proj_path: &PathBuf) -> DeliveryResult<Option<String>> {
        let write_path = DeliveryConfig::config_file_path(proj_path);

        // If a config.json already exists, check to see if it is exactly
        // the same as what we want to copy to it.
        if !try!(file_needs_updated(config_f, &write_path)) {
            return Ok(None)
        }

        try!(copy_recursive(config_f, &write_path));
        try!(DeliveryConfig::validate_config_file(proj_path));
        let content = try!(read_file(&write_path));
        Ok(Some(content))
    }

    pub fn git_add_commit_config(proj_path: &PathBuf) -> DeliveryResult<bool> {
        let config_path = DeliveryConfig::config_file_path(proj_path);
        let config_path_str = &config_path.to_str().unwrap();
        try!(git::git_command(&["add", &config_path_str], proj_path));

        // Commit the changes made in .delivery but detect if nothing has changed,
        // if that is the case, we are Ok() to continue
        match git::git_commit("Adds custom Delivery config") {
          Ok(_) => Ok(true),
          Err(DeliveryError{ kind: Kind::EmptyGitCommit, .. }) => Ok(false),
          Err(e) => Err(e)
        }
    }

    pub fn config_file_path(proj_path: &PathBuf) -> PathBuf {
        proj_path.join_many(&[".delivery", "config.json"])
    }

    fn find_config_file(proj_path: &PathBuf) -> DeliveryResult<PathBuf> {
        match walk_tree_for_path(proj_path, ".delivery/config.json") {
            Some(p) => {
                debug!("found config: {:?}", p);
                Ok(p)
            },
            None => Err(DeliveryError{kind: Kind::MissingProjectConfig,
                                      detail: None})
        }
    }

    // Validate the config.json file
    pub fn validate_config_file(proj_path: &PathBuf) -> DeliveryResult<bool> {
        DeliveryConfig::load_config(proj_path).and(Ok(true))
    }

    // Load the .delivery/config.json into a DeliveryConfig object
    //
    // This fn is capable of loading the `config.json` from a provided
    // path, it will try to decode the config V2 (latest at the moment)
    // and if it is unable to do so, it will try to decode in V1
    pub fn load_config(p_path: &PathBuf) -> DeliveryResult<Self> {
        debug!("Loading config.json into memory");
        let config_path = try!(DeliveryConfig::find_config_file(p_path));
        let mut config_file = try!(File::open(&config_path));
        let mut config_json = String::new();
        try!(config_file.read_to_string(&mut config_json));

        // Try to decode the config, but if you are unable to, try V1;
        // If you are still unable; just fail
        let json: DeliveryConfig = try!(json::decode(&config_json).or_else( |e| {
            debug!("Unable to parse DeliveryConfig: {}", e);
            debug!("Attepting to load version: 1");
            let v1_config = try!(DeliveryConfigV1::load_config(p_path));
            v1_config.convert_to_v2()
        }));
        Ok(json)
    }

}

// v1 config, deprecated, but still supported
#[derive(RustcDecodable)]
pub struct DeliveryConfigV1 {
    pub version: String,
    pub build_cookbook: String,
    pub skip_phases: Option<Vec<String>>,
    pub build_nodes: Option<HashMap<String, Vec<String>>>
}

impl Default for DeliveryConfigV1 {
    fn default() -> Self {
        DeliveryConfigV1 {
            version: "1".to_string(),
            build_cookbook: "./.delivery/build_cookbook".to_string(),
            skip_phases: Some(Vec::new()),
            build_nodes: Some(HashMap::new()),
        }
    }
}

impl DeliveryConfigV1 {
    // Load the .delivery/config.json into a DeliveryConfigV1 object
    pub fn load_config(p_path: &PathBuf) -> DeliveryResult<Self> {
        let config_path = try!(DeliveryConfig::find_config_file(p_path));
        let mut config_file = try!(File::open(&config_path));
        let mut config_json = String::new();
        try!(config_file.read_to_string(&mut config_json));
        let json: DeliveryConfigV1 = try!(json::decode(&config_json));
        Ok(json)
    }

    // Convert DeliveryConfigV1 to V2
    //
    // The big difference between V1 and V2 is that the build_cookbook field was
    // at first a simple String that pointed to either a build_cookbook path stored
    // locally or a simple name of the build_cookbook that would mean we would pull
    // it from the Chef Sever. In V2 instead we allows multiple locations including
    // `path` and `server` among others.
    //
    // This function will decode a V1 config and convert it into V2 properly
    fn convert_to_v2(&self) -> DeliveryResult<DeliveryConfig> {
        let mut build_cookbook = HashMap::new();

        // Detect if the build_cookbook is stored locally or remotely
        if self.build_cookbook.contains("/") {
            // A local path, lets add the `path` field
            let cookbook_path = PathBuf::from(&self.build_cookbook);
            let cookbook_name = try!(cookbook_path.file_name().ok_or(DeliveryError{
                kind: Kind::NoValidBuildCookbook,
                detail: Some("V1: Expected a valid path to a build_cookbook".to_string())
            }));

            build_cookbook.insert(String::from("name"),
                                  cookbook_name
                                    .to_string_lossy()
                                    .into_owned());
            build_cookbook.insert(String::from("path"),
                                  cookbook_path
                                    .to_string_lossy()
                                    .into_owned());
        } else {
            // A build_cookbook name, load it from the `server`
            build_cookbook.insert(String::from("name"),
                                  self.build_cookbook.clone());
            build_cookbook.insert(String::from("server"), String::from("true"));
        }

        Ok(
            // Instantiate a DeliveryConfig consuming `self` properties
            DeliveryConfig {
                // This is a config coming from V1, lets persist this
                version: "1".to_string(),
                build_cookbook: build_cookbook,
                skip_phases: self.skip_phases.clone(),
                build_nodes: self.build_nodes.clone(),
                job_dispatch: None,
                dependencies: None,
            }
        )
    }
}
<commit_msg>Implemente Default for JobDispatch struct<commit_after>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/// This module is responsible for handling the .delivery/config.json file

use std::collections::HashMap;
use std::default::Default;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use rustc_serialize::json;
use errors::{DeliveryError, Kind};
use types::DeliveryResult;
use git;
use utils::{walk_tree_for_path, read_file, copy_recursive, file_needs_updated};
use utils::path_join_many::PathJoinMany;

pub mod project;

#[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct DeliveryConfig {
    pub version: String,
    pub build_cookbook: HashMap<String, String>,
    pub skip_phases: Option<Vec<String>>,
    pub build_nodes: Option<HashMap<String, Vec<String>>>,
    pub job_dispatch: Option<JobDispatch>,
    pub dependencies: Option<Vec<String>>,
}

// JobDispatch Struct
//
// This structure has two main fields;
//   * version - The config version
//   * filters - Specific search nodes filters for each phase
//
// Example:
//  "job_dispatch": {
//    "version": "v2",
//    "filters": {
//      "unit": [
//        {
//          "platform_family": ["debian"],
//          "platform_version": ["14.04"]
//        },
//        {
//          "platform_family": ["rhel"]
//        }
//      ],
//      "syntax": [
//        {
//          "platform_family": ["debian"],
//          "platform_version": ["14.04"]
//        },
//        {
//          "platform_family": ["rhel"]
//        }
//      ]
//    }
//  }
#[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct JobDispatch {
    pub version: String,
    pub filters: Option<HashMap<String, Vec<HashMap<String, Vec<String>>>>>,
}

impl Default for JobDispatch {
    fn default() -> Self {
        JobDispatch {
            version: "v2".to_string(),
            filters: None,
        }
    }
}

impl Default for DeliveryConfig {
    fn default() -> Self {
        let mut build_cookbook = HashMap::new();
        build_cookbook.insert("name".to_string(),
                              "build_cookbook".to_string());
        build_cookbook.insert("path".to_string(),
                              ".delivery/build_cookbook".to_string());

        DeliveryConfig {
            version: "2".to_string(),
            build_cookbook: build_cookbook,
            skip_phases: Some(Vec::new()),
            build_nodes: None,
            job_dispatch: Some(JobDispatch::default()),
            dependencies: Some(Vec::new()),
        }
    }
}

// BuildCookbookLocation Enumarator
//
// The build_cokbook could be sourced from the following locations:
//   * Local       - On the project repo
//   * Git         - From a git server. (url)
//   * Supermarket - From a Supermarket Server
//   * Workflow    - From the Workflow Server
//   * ChefServer  - From the Chef Server
//
// Examples: https://docs.chef.io/config_json_delivery.html#examples
pub enum BuildCookbookLocation {
    Local,
    Git,
    Supermarket,
    Workflow,
    ChefServer,
}

impl DeliveryConfig {
    // Return the build_cookbook location
    //
    // Searches for the right field inside the build_cookbook HashMap
    // and translates it to a BuildCookbookLocation Enum, if none of
    // the possible entries exist, throws a `Err()`
    pub fn build_cookbook_location(&self) -> DeliveryResult<BuildCookbookLocation> {
        if self.build_cookbook.contains_key("path") {
            return Ok(BuildCookbookLocation::Local)
        }
        if self.build_cookbook.contains_key("git") {
            return Ok(BuildCookbookLocation::Git)
        }
        if self.build_cookbook.contains_key("supermarket") {
            return Ok(BuildCookbookLocation::Supermarket)
        }
        if self.build_cookbook.contains_key("enterprise") {
            return Ok(BuildCookbookLocation::Workflow)
        }
        if self.build_cookbook.contains_key("server") {
            return Ok(BuildCookbookLocation::ChefServer)
        }
        Err(DeliveryError{ kind: Kind::NoValidBuildCookbook, detail: None })
    }

    // Get the content of a specific build_cookbook field
    //
    // The build_cookbook is difined as a HashMap that we can easily extract
    // the content of a particular `key`, this will reduce complexity and code
    //
    // Example:
    // ```
    // config    = DeliveryConfig.default();
    // c_version = try!(config.build_cookbook_get("version"));
    // assert_eq!("2".to_string(), c_version);
    // ```
    pub fn build_cookbook_get(&self, key: &str) -> DeliveryResult<String> {
        self.build_cookbook.get(key)
            .ok_or(DeliveryError{
                kind: Kind::MissingBuildCookbookField,
                detail: Some(format!("Unable to find '{}' field.", key).to_string())
            }).map(|s| { s.to_owned() })
    }

    // Return the build_cookbook name
    //
    // A valid Delivery V2 config should always have a `name` entry
    // inside the build_cookbook HashMap.
    pub fn build_cookbook_name(&self) -> DeliveryResult<String> {
        self.build_cookbook_get("name")
    }

    /// Copy a provided `config.json` file to `.delivery/` of
    /// the project root path. Also verify that the config is
    /// valid and finally add/commit the changes.
    /// If the config already exists, skip this process.
    pub fn copy_config_file(config_f: &PathBuf,
                            proj_path: &PathBuf) -> DeliveryResult<Option<String>> {
        let write_path = DeliveryConfig::config_file_path(proj_path);

        // If a config.json already exists, check to see if it is exactly
        // the same as what we want to copy to it.
        if !try!(file_needs_updated(config_f, &write_path)) {
            return Ok(None)
        }

        try!(copy_recursive(config_f, &write_path));
        try!(DeliveryConfig::validate_config_file(proj_path));
        let content = try!(read_file(&write_path));
        Ok(Some(content))
    }

    pub fn git_add_commit_config(proj_path: &PathBuf) -> DeliveryResult<bool> {
        let config_path = DeliveryConfig::config_file_path(proj_path);
        let config_path_str = &config_path.to_str().unwrap();
        try!(git::git_command(&["add", &config_path_str], proj_path));

        // Commit the changes made in .delivery but detect if nothing has changed,
        // if that is the case, we are Ok() to continue
        match git::git_commit("Adds custom Delivery config") {
          Ok(_) => Ok(true),
          Err(DeliveryError{ kind: Kind::EmptyGitCommit, .. }) => Ok(false),
          Err(e) => Err(e)
        }
    }

    pub fn config_file_path(proj_path: &PathBuf) -> PathBuf {
        proj_path.join_many(&[".delivery", "config.json"])
    }

    fn find_config_file(proj_path: &PathBuf) -> DeliveryResult<PathBuf> {
        match walk_tree_for_path(proj_path, ".delivery/config.json") {
            Some(p) => {
                debug!("found config: {:?}", p);
                Ok(p)
            },
            None => Err(DeliveryError{kind: Kind::MissingProjectConfig,
                                      detail: None})
        }
    }

    // Validate the config.json file
    pub fn validate_config_file(proj_path: &PathBuf) -> DeliveryResult<bool> {
        DeliveryConfig::load_config(proj_path).and(Ok(true))
    }

    // Load the .delivery/config.json into a DeliveryConfig object
    //
    // This fn is capable of loading the `config.json` from a provided
    // path, it will try to decode the config V2 (latest at the moment)
    // and if it is unable to do so, it will try to decode in V1
    pub fn load_config(p_path: &PathBuf) -> DeliveryResult<Self> {
        debug!("Loading config.json into memory");
        let config_path = try!(DeliveryConfig::find_config_file(p_path));
        let mut config_file = try!(File::open(&config_path));
        let mut config_json = String::new();
        try!(config_file.read_to_string(&mut config_json));

        // Try to decode the config, but if you are unable to, try V1;
        // If you are still unable; just fail
        let json: DeliveryConfig = try!(json::decode(&config_json).or_else( |e| {
            debug!("Unable to parse DeliveryConfig: {}", e);
            debug!("Attepting to load version: 1");
            let v1_config = try!(DeliveryConfigV1::load_config(p_path));
            v1_config.convert_to_v2()
        }));
        Ok(json)
    }

}

// v1 config, deprecated, but still supported
#[derive(RustcDecodable)]
pub struct DeliveryConfigV1 {
    pub version: String,
    pub build_cookbook: String,
    pub skip_phases: Option<Vec<String>>,
    pub build_nodes: Option<HashMap<String, Vec<String>>>
}

impl Default for DeliveryConfigV1 {
    fn default() -> Self {
        DeliveryConfigV1 {
            version: "1".to_string(),
            build_cookbook: "./.delivery/build_cookbook".to_string(),
            skip_phases: Some(Vec::new()),
            build_nodes: Some(HashMap::new()),
        }
    }
}

impl DeliveryConfigV1 {
    // Load the .delivery/config.json into a DeliveryConfigV1 object
    pub fn load_config(p_path: &PathBuf) -> DeliveryResult<Self> {
        let config_path = try!(DeliveryConfig::find_config_file(p_path));
        let mut config_file = try!(File::open(&config_path));
        let mut config_json = String::new();
        try!(config_file.read_to_string(&mut config_json));
        let json: DeliveryConfigV1 = try!(json::decode(&config_json));
        Ok(json)
    }

    // Convert DeliveryConfigV1 to V2
    //
    // The big difference between V1 and V2 is that the build_cookbook field was
    // at first a simple String that pointed to either a build_cookbook path stored
    // locally or a simple name of the build_cookbook that would mean we would pull
    // it from the Chef Sever. In V2 instead we allows multiple locations including
    // `path` and `server` among others.
    //
    // This function will decode a V1 config and convert it into V2 properly
    fn convert_to_v2(&self) -> DeliveryResult<DeliveryConfig> {
        let mut build_cookbook = HashMap::new();

        // Detect if the build_cookbook is stored locally or remotely
        if self.build_cookbook.contains("/") {
            // A local path, lets add the `path` field
            let cookbook_path = PathBuf::from(&self.build_cookbook);
            let cookbook_name = try!(cookbook_path.file_name().ok_or(DeliveryError{
                kind: Kind::NoValidBuildCookbook,
                detail: Some("V1: Expected a valid path to a build_cookbook".to_string())
            }));

            build_cookbook.insert(String::from("name"),
                                  cookbook_name
                                    .to_string_lossy()
                                    .into_owned());
            build_cookbook.insert(String::from("path"),
                                  cookbook_path
                                    .to_string_lossy()
                                    .into_owned());
        } else {
            // A build_cookbook name, load it from the `server`
            build_cookbook.insert(String::from("name"),
                                  self.build_cookbook.clone());
            build_cookbook.insert(String::from("server"), String::from("true"));
        }

        Ok(
            // Instantiate a DeliveryConfig consuming `self` properties
            DeliveryConfig {
                // This is a config coming from V1, lets persist this
                version: "1".to_string(),
                build_cookbook: build_cookbook,
                skip_phases: self.skip_phases.clone(),
                build_nodes: self.build_nodes.clone(),
                job_dispatch: None,
                dependencies: None,
            }
        )
    }
}
<|endoftext|>


<commit_before>use libimagstore::store::EntryHeader;
use libimagstore::store::Result as StoreResult;

use error::{LinkError, LinkErrorKind};
use link::{Link, Links};
use result::Result;

use toml::Value;

pub fn get_links(header: &EntryHeader) -> Result<Links> {
    process_rw_result(header.read("imag.links"))
}

/// Set the links in a header and return the old links, if any.
pub fn set_links(header: &mut EntryHeader, links: Links) -> Result<Links> {
    let links : Vec<Link> = links.into();
    let links : Vec<Value> = links.into_iter().map(|link| Value::String(link.into())).collect();
    process_rw_result(header.set("imag.links", Value::Array(links)))
}

pub fn add_link(header: &mut EntryHeader, link: Link) -> Result<()> {
    get_links(header).and_then(|mut links| {
        links.add(link);
        set_links(header, links).map(|_| ())
    })
}

pub fn remove_link(header: &mut EntryHeader, link: Link) -> Result<()> {
    get_links(header).and_then(|mut links| {
        links.remove(link);
        set_links(header, links).map(|_| ())
    })
}

fn process_rw_result(links: StoreResult<Option<Value>>) -> Result<Links> {
    if links.is_err() {
        let lerr  = LinkError::new(LinkErrorKind::EntryHeaderReadError,
                                   Some(Box::new(links.err().unwrap())));
        return Err(lerr);
    }
    let links = links.unwrap();

    if links.iter().any(|l| match l { &Value::String(_) => true, _ => false }) {
        return Err(LinkError::new(LinkErrorKind::ExistingLinkTypeWrong, None));
    }

    let links : Vec<Link> = links.into_iter()
        .map(|link| {
            match link {
                Value::String(s) => Link::new(s),
                _ => unreachable!(),
            }
        })
        .collect();

    Ok(Links::new(links))
}

<commit_msg>Refactor internal link helpers into trait<commit_after>use libimagstore::store::EntryHeader;
use libimagstore::store::Result as StoreResult;

use error::{LinkError, LinkErrorKind};
use link::{Link, Links};
use result::Result;

use toml::Value;

pub trait InternalLinker {

    /// Get the internal links from the implementor object
    fn get_internal_links(&self) -> Result<Links>;

    /// Set the internal links for the implementor object
    fn set_internal_links(&mut self, links: Links) -> Result<Links>;

    /// Add an internal link to the implementor object
    fn add_internal_link(&mut self, link: Link) -> Result<()>;

    /// Remove an internal link from the implementor object
    fn remove_internal_link(&mut self, link: Link) -> Result<()>;

}

impl InternalLinker for EntryHeader {

    fn get_internal_links(self: &EntryHeader) -> Result<Links> {
        process_rw_result(self.read("imag.links"))
    }

    /// Set the links in a header and return the old links, if any.
    fn set_internal_links(&mut self, links: Links) -> Result<Links> {
        let links : Vec<Link> = links.into();
        let links : Vec<Value> = links.into_iter().map(|link| Value::String(link.into())).collect();
        process_rw_result(self.set("imag.links", Value::Array(links)))
    }

    fn add_internal_link(&mut self, link: Link) -> Result<()> {
        self.get_internal_links()
            .and_then(|mut links| {
                links.add(link);
                self.set_internal_links(links).map(|_| ())
            })
    }

    fn remove_internal_link(&mut self, link: Link) -> Result<()> {
        self.get_internal_links()
            .and_then(|mut links| {
                links.remove(link);
                self.set_internal_links(links).map(|_| ())
            })
    }

}

fn process_rw_result(links: StoreResult<Option<Value>>) -> Result<Links> {
    if links.is_err() {
        let lerr  = LinkError::new(LinkErrorKind::EntryHeaderReadError,
                                   Some(Box::new(links.err().unwrap())));
        return Err(lerr);
    }
    let links = links.unwrap();

    if links.iter().any(|l| match l { &Value::String(_) => true, _ => false }) {
        return Err(LinkError::new(LinkErrorKind::ExistingLinkTypeWrong, None));
    }

    let links : Vec<Link> = links.into_iter()
        .map(|link| {
            match link {
                Value::String(s) => Link::new(s),
                _ => unreachable!(),
            }
        })
        .collect();

    Ok(Links::new(links))
}

<|endoftext|>


<commit_before>extern crate tokio_io;
extern crate bytes;

use bytes::{BytesMut, BufMut};
use tokio_io::codec::{BytesCodec, LinesCodec, Decoder, Encoder};

#[test]
fn bytes_decoder() {
    let mut codec = BytesCodec::new();
    let buf = &mut BytesMut::new();
    buf.put_slice(b"abc");
    assert_eq!("abc", codec.decode(buf).unwrap().unwrap());
    assert_eq!(None, codec.decode(buf).unwrap());
    assert_eq!(None, codec.decode(buf).unwrap());
    buf.put_slice(b"a");
    assert_eq!("a", codec.decode(buf).unwrap().unwrap());
}

#[test]
fn lines_decoder() {
    let mut codec = LinesCodec::new();
    let buf = &mut BytesMut::new();
    buf.put("line 1\nline 2\r\nline 3\n\r\n\r");
    assert_eq!("line 1", codec.decode(buf).unwrap().unwrap());
    assert_eq!("line 2", codec.decode(buf).unwrap().unwrap());
    assert_eq!("line 3", codec.decode(buf).unwrap().unwrap());
    assert_eq!("", codec.decode(buf).unwrap().unwrap());
    assert_eq!(None, codec.decode(buf).unwrap());
    assert_eq!(None, codec.decode_eof(buf).unwrap());
    buf.put("k");
    assert_eq!("\rk", codec.decode_eof(buf).unwrap().unwrap());
}
<commit_msg>Add a reserve to a test to prevent an assertion<commit_after>extern crate tokio_io;
extern crate bytes;

use bytes::{BytesMut, BufMut};
use tokio_io::codec::{BytesCodec, LinesCodec, Decoder, Encoder};

#[test]
fn bytes_decoder() {
    let mut codec = BytesCodec::new();
    let buf = &mut BytesMut::new();
    buf.put_slice(b"abc");
    assert_eq!("abc", codec.decode(buf).unwrap().unwrap());
    assert_eq!(None, codec.decode(buf).unwrap());
    assert_eq!(None, codec.decode(buf).unwrap());
    buf.put_slice(b"a");
    assert_eq!("a", codec.decode(buf).unwrap().unwrap());
}

#[test]
fn lines_decoder() {
    let mut codec = LinesCodec::new();
    let buf = &mut BytesMut::new();
    buf.reserve(200);
    buf.put("line 1\nline 2\r\nline 3\n\r\n\r");
    assert_eq!("line 1", codec.decode(buf).unwrap().unwrap());
    assert_eq!("line 2", codec.decode(buf).unwrap().unwrap());
    assert_eq!("line 3", codec.decode(buf).unwrap().unwrap());
    assert_eq!("", codec.decode(buf).unwrap().unwrap());
    assert_eq!(None, codec.decode(buf).unwrap());
    assert_eq!(None, codec.decode_eof(buf).unwrap());
    buf.put("k");
    assert_eq!("\rk", codec.decode_eof(buf).unwrap().unwrap());
}
<|endoftext|>


<commit_before>use super::types::{ShardAndCommittee, ValidatorRecord, ChainConfig};
use super::TransitionError;
use super::shuffle;
use std::cmp::min;

type DelegatedCycle = Vec<Vec<ShardAndCommittee>>;

/// Iterator for the honey_badger_split function
struct Split<'a, T: 'a> {
    n: usize,
    current_pos: usize,
    list: &'a [T],
    list_length: usize
}

impl<'a,T> Iterator for Split<'a, T> {
    type Item = &'a [T];

    fn next(&mut self) -> Option<Self::Item> {
        self.current_pos +=1;
        if self.current_pos <= self.n {
            match self.list.get(self.list_length*(self.current_pos-1)/self.n..self.list_length*self.current_pos/self.n) {
                Some(v) => Some(v),
                None => unreachable!()
            }
        }
        else {
            None
        }
    }
}

/// splits a slice into chunks of size n. All postive n values are applicable,
/// hence the honey_badger prefix.
/// Returns an iterator over the original list.
trait SplitExt<T> {
    fn honey_badger_split(&self, n: usize) -> Split<T>;
}

impl<T> SplitExt<T> for [T] {

    fn honey_badger_split(&self, n: usize) -> Split<T> {
        Split {
            n,
            current_pos: 0,
            list: &self,
            list_length: self.len(),
        }
    }
}

/* Produce a vector of validators indicies where those validators start and end
 * dynasties are within the supplied `dynasty`.
*/
fn active_validator_indicies(
    dynasty: u64,
    validators: &[ValidatorRecord])
    -> Vec<usize>
{
    validators.iter()
        .enumerate()
        .filter_map(|(i, validator)| {
            if (validator.start_dynasty >= dynasty) &
                (validator.end_dynasty < dynasty)
            {
                Some(i)
            } else {
                None
            }
        })
        .collect()
}

/*
 * Delegates active validators into slots for a given cycle, given a random seed.
 * Returns a vector or ShardAndComitte vectors representing the shards and committiees for
 * each slot.
 * References get_new_shuffling (ethereum 2.1 specification)
 */
pub fn delegate_validators(
    seed: &[u8],
    validators: &[ValidatorRecord],
    dynasty: u64,
    crosslinking_shard_start: u16,
    config: &ChainConfig)
    -> Result<DelegatedCycle, TransitionError>
{
    let shuffled_validator_indices = {
        let mut validator_indices = active_validator_indicies(dynasty, validators);
        match shuffle(seed, validator_indices) {
            Ok(shuffled) => shuffled,
            _ => return Err(TransitionError::InvalidInput(
                    String::from("Shuffle list length exceed.")))
        }
    };
    let shard_indices: Vec<usize> = (0_usize..config.shard_count as usize).into_iter().collect();
    let crosslinking_shard_start = crosslinking_shard_start as usize;
    let cycle_length = config.cycle_length as usize;
    let min_committee_size = config.min_committee_size as usize;
    generate_cycle(
        &shuffled_validator_indices,
        &shard_indices,
        crosslinking_shard_start,
        cycle_length,
        min_committee_size)
}

/*
 * Given the validator list, delegates the validators into slots and comittees for a given cycle.
 */
fn generate_cycle(
    validator_indices: &[usize],
    shard_indices: &[usize],
    crosslinking_shard_start: usize,
    cycle_length: usize,
    min_committee_size: usize)
    -> Result<DelegatedCycle, TransitionError>
{

    let validator_count = validator_indices.len();
    let shard_count = shard_indices.len();

    if shard_count / cycle_length == 0 {
	    return Err(TransitionError::InvalidInput(String::from("Number of
					    shards needs to be greater than
					    cycle length")));

    }

    let (committees_per_slot, slots_per_committee) = {
        if validator_count >= cycle_length * min_committee_size {
            let committees_per_slot = min(validator_count / cycle_length /
			    (min_committee_size * 2) + 1, shard_count /
			    cycle_length);
            let slots_per_committee = 1;
            (committees_per_slot, slots_per_committee)
        } else {
            let committees_per_slot = 1;
            let mut slots_per_committee = 1;
            while (validator_count * slots_per_committee < cycle_length * min_committee_size) &
                (slots_per_committee < cycle_length) {
                slots_per_committee *= 2;
            }
            (committees_per_slot, slots_per_committee)
        }
    };

    let cycle = validator_indices.honey_badger_split(cycle_length)
        .enumerate()
        .map(|(i, slot_indices)| {
            let shard_id_start = crosslinking_shard_start + i * committees_per_slot / slots_per_committee;
            slot_indices.honey_badger_split(committees_per_slot)
                .enumerate()
                .map(|(j, shard_indices)| {
                    ShardAndCommittee{
                        shard_id: ((shard_id_start + j) % shard_count) as u16,
                        committee: shard_indices.to_vec(),
                    }
                })
                .collect()
        })
        .collect();
    Ok(cycle)
}

#[cfg(test)]
mod tests {
    use super::*;

    fn generate_cycle_helper(
        validator_count: &usize,
        shard_count: &usize,
        crosslinking_shard_start: &usize,
        cycle_length: &usize,
        min_committee_size: &usize)
        -> (Vec<usize>, Vec<usize>, Result<DelegatedCycle, TransitionError>)
    {
        let validator_indices = (0_usize..*validator_count).into_iter().collect();
        let shard_indices = (0_usize..*shard_count).into_iter().collect();
        let result = generate_cycle(
            &validator_indices,
            &shard_indices,
            &crosslinking_shard_start,
            &cycle_length,
            &min_committee_size);
        (validator_indices, shard_indices, result)
    }

    #[allow(dead_code)]
    fn print_cycle(cycle: &DelegatedCycle) {
        cycle.iter()
            .enumerate()
            .for_each(|(i, slot)| {
                println!("slot {:?}", &i);
                slot.iter()
                    .enumerate()
                    .for_each(|(i, sac)| {
                        println!("#{:?}\tshard_id={}\tcommittee.len()={}",
                            &i, &sac.shard_id, &sac.committee.len())
                    })
            });
    }

    fn flatten_validators(cycle: &DelegatedCycle)
        -> Vec<usize>
    {
        let mut flattened = vec![];
        for slot in cycle.iter() {
            for sac in slot.iter() {
                for validator in sac.committee.iter() {
                    flattened.push(*validator);
                }
            }
        }
        flattened
    }

    fn flatten_and_dedup_shards(cycle: &DelegatedCycle)
        -> Vec<usize>
    {
        let mut flattened = vec![];
        for slot in cycle.iter() {
            for sac in slot.iter() {
                flattened.push(sac.shard_id as usize);
            }
        }
        flattened.dedup();
        flattened
    }

    fn flatten_shards_in_slots(cycle: &DelegatedCycle)
        -> Vec<Vec<usize>>
    {
        let mut shards_in_slots: Vec<Vec<usize>> = vec![];
        for slot in cycle.iter() {
            let mut shards: Vec<usize> = vec![];
            for sac in slot.iter() {
                shards.push(sac.shard_id as usize);
            }
            shards_in_slots.push(shards);
        }
        shards_in_slots
    }

    // TODO: Improve these tests to check committee lengths
    #[test]
    fn test_generate_cycle() {
        let validator_count: usize = 100;
        let shard_count: usize = 20;
        let crosslinking_shard_start: usize = 0;
        let cycle_length: usize = 20;
        let min_committee_size: usize = 10;
        let (validators, shards, result) = generate_cycle_helper(
            &validator_count,
            &shard_count,
            &crosslinking_shard_start,
            &cycle_length,
            &min_committee_size);
        let cycle = result.unwrap();

        let assigned_validators = flatten_validators(&cycle);
        let assigned_shards = flatten_and_dedup_shards(&cycle);
        let shards_in_slots = flatten_shards_in_slots(&cycle);
        let expected_shards = shards.get(0..10).unwrap();
        assert_eq!(assigned_validators, validators, "Validator assignment incorrect");
        assert_eq!(assigned_shards, expected_shards, "Shard assignment incorrect");

        let expected_shards_in_slots: Vec<Vec<usize>> = vec![
            vec![0], vec![0],   // Each line is 2 slots..
            vec![1], vec![1],
            vec![2], vec![2],
            vec![3], vec![3],
            vec![4], vec![4],
            vec![5], vec![5],
            vec![6], vec![6],
            vec![7], vec![7],
            vec![8], vec![8],
            vec![9], vec![9],
        ];
        // assert!(compare_shards_in_slots(&cycle, &expected_shards_in_slots));
        assert_eq!(expected_shards_in_slots, shards_in_slots, "Shard assignment incorrect.")
    }

    #[test]
    // Check that the committees per slot is upper bounded by shard count
    fn test_generate_cycle_committees_bounded() {
        let validator_count: usize = 523;
        let shard_count: usize = 31;
        let crosslinking_shard_start: usize = 0;
        let cycle_length: usize = 11;
        let min_committee_size: usize = 5;
        let (validators, shards, result) = generate_cycle_helper(
            &validator_count,
            &shard_count,
            &crosslinking_shard_start,
            &cycle_length,
            &min_committee_size);
        let cycle = result.unwrap();
        let assigned_validators = flatten_validators(&cycle);
        let assigned_shards = flatten_and_dedup_shards(&cycle);
        let shards_in_slots = flatten_shards_in_slots(&cycle);
        let expected_shards = shards.get(0..22).unwrap();
        let expected_shards_in_slots: Vec<Vec<usize>> =
            (0_usize..11_usize) .map(|x| vec![2*x,2*x+1]).collect();
        assert_eq!(assigned_validators, validators, "Validator assignment incorrect");
        assert_eq!(assigned_shards, expected_shards, "Shard assignment incorrect");
        // assert!(compare_shards_in_slots(&cycle, &expected_shards_in_slots));
        assert_eq!(expected_shards_in_slots, shards_in_slots, "Shard assignment incorrect.")
    }
}
<commit_msg>Updates validation tests for clippy modifications<commit_after>use super::types::{ShardAndCommittee, ValidatorRecord, ChainConfig};
use super::TransitionError;
use super::shuffle;
use std::cmp::min;

type DelegatedCycle = Vec<Vec<ShardAndCommittee>>;

/// Iterator for the honey_badger_split function
struct Split<'a, T: 'a> {
    n: usize,
    current_pos: usize,
    list: &'a [T],
    list_length: usize
}

impl<'a,T> Iterator for Split<'a, T> {
    type Item = &'a [T];

    fn next(&mut self) -> Option<Self::Item> {
        self.current_pos +=1;
        if self.current_pos <= self.n {
            match self.list.get(self.list_length*(self.current_pos-1)/self.n..self.list_length*self.current_pos/self.n) {
                Some(v) => Some(v),
                None => unreachable!()
            }
        }
        else {
            None
        }
    }
}

/// splits a slice into chunks of size n. All postive n values are applicable,
/// hence the honey_badger prefix.
/// Returns an iterator over the original list.
trait SplitExt<T> {
    fn honey_badger_split(&self, n: usize) -> Split<T>;
}

impl<T> SplitExt<T> for [T] {

    fn honey_badger_split(&self, n: usize) -> Split<T> {
        Split {
            n,
            current_pos: 0,
            list: &self,
            list_length: self.len(),
        }
    }
}

/* Produce a vector of validators indicies where those validators start and end
 * dynasties are within the supplied `dynasty`.
*/
fn active_validator_indicies(
    dynasty: u64,
    validators: &[ValidatorRecord])
    -> Vec<usize>
{
    validators.iter()
        .enumerate()
        .filter_map(|(i, validator)| {
            if (validator.start_dynasty >= dynasty) &
                (validator.end_dynasty < dynasty)
            {
                Some(i)
            } else {
                None
            }
        })
        .collect()
}

/*
 * Delegates active validators into slots for a given cycle, given a random seed.
 * Returns a vector or ShardAndComitte vectors representing the shards and committiees for
 * each slot.
 * References get_new_shuffling (ethereum 2.1 specification)
 */
pub fn delegate_validators(
    seed: &[u8],
    validators: &[ValidatorRecord],
    dynasty: u64,
    crosslinking_shard_start: u16,
    config: &ChainConfig)
    -> Result<DelegatedCycle, TransitionError>
{
    let shuffled_validator_indices = {
        let mut validator_indices = active_validator_indicies(dynasty, validators);
        match shuffle(seed, validator_indices) {
            Ok(shuffled) => shuffled,
            _ => return Err(TransitionError::InvalidInput(
                    String::from("Shuffle list length exceed.")))
        }
    };
    let shard_indices: Vec<usize> = (0_usize..config.shard_count as usize).into_iter().collect();
    let crosslinking_shard_start = crosslinking_shard_start as usize;
    let cycle_length = config.cycle_length as usize;
    let min_committee_size = config.min_committee_size as usize;
    generate_cycle(
        &shuffled_validator_indices,
        &shard_indices,
        crosslinking_shard_start,
        cycle_length,
        min_committee_size)
}

/*
 * Given the validator list, delegates the validators into slots and comittees for a given cycle.
 */
fn generate_cycle(
    validator_indices: &[usize],
    shard_indices: &[usize],
    crosslinking_shard_start: usize,
    cycle_length: usize,
    min_committee_size: usize)
    -> Result<DelegatedCycle, TransitionError>
{

    let validator_count = validator_indices.len();
    let shard_count = shard_indices.len();

    if shard_count / cycle_length == 0 {
	    return Err(TransitionError::InvalidInput(String::from("Number of
					    shards needs to be greater than
					    cycle length")));

    }

    let (committees_per_slot, slots_per_committee) = {
        if validator_count >= cycle_length * min_committee_size {
            let committees_per_slot = min(validator_count / cycle_length /
			    (min_committee_size * 2) + 1, shard_count /
			    cycle_length);
            let slots_per_committee = 1;
            (committees_per_slot, slots_per_committee)
        } else {
            let committees_per_slot = 1;
            let mut slots_per_committee = 1;
            while (validator_count * slots_per_committee < cycle_length * min_committee_size) &
                (slots_per_committee < cycle_length) {
                slots_per_committee *= 2;
            }
            (committees_per_slot, slots_per_committee)
        }
    };

    let cycle = validator_indices.honey_badger_split(cycle_length)
        .enumerate()
        .map(|(i, slot_indices)| {
            let shard_id_start = crosslinking_shard_start + i * committees_per_slot / slots_per_committee;
            slot_indices.honey_badger_split(committees_per_slot)
                .enumerate()
                .map(|(j, shard_indices)| {
                    ShardAndCommittee{
                        shard_id: ((shard_id_start + j) % shard_count) as u16,
                        committee: shard_indices.to_vec(),
                    }
                })
                .collect()
        })
        .collect();
    Ok(cycle)
}

#[cfg(test)]
mod tests {
    use super::*;

    fn generate_cycle_helper(
        validator_count: &usize,
        shard_count: &usize,
        crosslinking_shard_start: usize,
        cycle_length: usize,
        min_committee_size: usize)
        -> (Vec<usize>, Vec<usize>, Result<DelegatedCycle, TransitionError>)
    {
        let validator_indices: Vec<usize> = (0_usize..*validator_count).into_iter().collect();
        let shard_indices: Vec<usize> = (0_usize..*shard_count).into_iter().collect();
        let result = generate_cycle(
            &validator_indices,
            &shard_indices,
            crosslinking_shard_start,
            cycle_length,
            min_committee_size);
        (validator_indices, shard_indices, result)
    }

    #[allow(dead_code)]
    fn print_cycle(cycle: &DelegatedCycle) {
        cycle.iter()
            .enumerate()
            .for_each(|(i, slot)| {
                println!("slot {:?}", &i);
                slot.iter()
                    .enumerate()
                    .for_each(|(i, sac)| {
                        println!("#{:?}\tshard_id={}\tcommittee.len()={}",
                            &i, &sac.shard_id, &sac.committee.len())
                    })
            });
    }

    fn flatten_validators(cycle: &DelegatedCycle)
        -> Vec<usize>
    {
        let mut flattened = vec![];
        for slot in cycle.iter() {
            for sac in slot.iter() {
                for validator in sac.committee.iter() {
                    flattened.push(*validator);
                }
            }
        }
        flattened
    }

    fn flatten_and_dedup_shards(cycle: &DelegatedCycle)
        -> Vec<usize>
    {
        let mut flattened = vec![];
        for slot in cycle.iter() {
            for sac in slot.iter() {
                flattened.push(sac.shard_id as usize);
            }
        }
        flattened.dedup();
        flattened
    }

    fn flatten_shards_in_slots(cycle: &DelegatedCycle)
        -> Vec<Vec<usize>>
    {
        let mut shards_in_slots: Vec<Vec<usize>> = vec![];
        for slot in cycle.iter() {
            let mut shards: Vec<usize> = vec![];
            for sac in slot.iter() {
                shards.push(sac.shard_id as usize);
            }
            shards_in_slots.push(shards);
        }
        shards_in_slots
    }

    // TODO: Improve these tests to check committee lengths
    #[test]
    fn test_generate_cycle() {
        let validator_count: usize = 100;
        let shard_count: usize = 20;
        let crosslinking_shard_start: usize = 0;
        let cycle_length: usize = 20;
        let min_committee_size: usize = 10;
        let (validators, shards, result) = generate_cycle_helper(
            &validator_count,
            &shard_count,
            crosslinking_shard_start,
            cycle_length,
            min_committee_size);
        let cycle = result.unwrap();

        let assigned_validators = flatten_validators(&cycle);
        let assigned_shards = flatten_and_dedup_shards(&cycle);
        let shards_in_slots = flatten_shards_in_slots(&cycle);
        let expected_shards = shards.get(0..10).unwrap();
        assert_eq!(assigned_validators, validators, "Validator assignment incorrect");
        assert_eq!(assigned_shards, expected_shards, "Shard assignment incorrect");

        let expected_shards_in_slots: Vec<Vec<usize>> = vec![
            vec![0], vec![0],   // Each line is 2 slots..
            vec![1], vec![1],
            vec![2], vec![2],
            vec![3], vec![3],
            vec![4], vec![4],
            vec![5], vec![5],
            vec![6], vec![6],
            vec![7], vec![7],
            vec![8], vec![8],
            vec![9], vec![9],
        ];
        // assert!(compare_shards_in_slots(&cycle, &expected_shards_in_slots));
        assert_eq!(expected_shards_in_slots, shards_in_slots, "Shard assignment incorrect.")
    }

    #[test]
    // Check that the committees per slot is upper bounded by shard count
    fn test_generate_cycle_committees_bounded() {
        let validator_count: usize = 523;
        let shard_count: usize = 31;
        let crosslinking_shard_start: usize = 0;
        let cycle_length: usize = 11;
        let min_committee_size: usize = 5;
        let (validators, shards, result) = generate_cycle_helper(
            &validator_count,
            &shard_count,
            crosslinking_shard_start,
            cycle_length,
            min_committee_size);
        let cycle = result.unwrap();
        let assigned_validators = flatten_validators(&cycle);
        let assigned_shards = flatten_and_dedup_shards(&cycle);
        let shards_in_slots = flatten_shards_in_slots(&cycle);
        let expected_shards = shards.get(0..22).unwrap();
        let expected_shards_in_slots: Vec<Vec<usize>> =
            (0_usize..11_usize) .map(|x| vec![2*x,2*x+1]).collect();
        assert_eq!(assigned_validators, validators, "Validator assignment incorrect");
        assert_eq!(assigned_shards, expected_shards, "Shard assignment incorrect");
        // assert!(compare_shards_in_slots(&cycle, &expected_shards_in_slots));
        assert_eq!(expected_shards_in_slots, shards_in_slots, "Shard assignment incorrect.")
    }
}
<|endoftext|>