<commit_before>//! Helper functions for dealing with shaders.

// External crates.
use gl;
use gl::types::{
    GLboolean,
    GLchar,
    GLenum,
    GLint,
    GLsizeiptr,
    GLuint,
};
use std::ffi::CString;
use std::{ ptr, mem };

/// Describes a shader attribute.
pub struct DynamicAttribute {
    /// The vertex buffer object.
    vbo: GLuint,
    /// The number of components.
    size: i32,
    /// The location of the attribute in shader.
    location: GLuint,
    /// Whether to normalize when sending to GPU.
    normalize: GLboolean,
    /// The type, for example gl::FLOAT.
    ty: GLenum,
}

impl Drop for DynamicAttribute {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteBuffers(1, &self.vbo);
        }
    }
}

impl DynamicAttribute {
    /// Binds to a vertex array object.
    ///
    /// The vertex array object remembers the format for later.
    fn bind_vao(&self, vao: GLuint) {
        let stride = 0;
        unsafe {
            gl::BindVertexArray(vao);
            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
            gl::VertexAttribPointer(
                self.location,
                self.size,
                self.ty,
                self.normalize,
                stride,
                ptr::null()
            );
        }
    }

    fn new(
        program: GLuint,
        name: &str,
        size: i32,
        normalize: GLboolean,
        ty: GLenum,
        vao: GLuint
    ) -> Result<Self, String> {
        let location = try!(attribute_location(program, name));
        let mut vbo = 0;
        unsafe {
            gl::GenBuffers(1, &mut vbo);
        }
        let res = DynamicAttribute {
                vbo: vbo,
                size: size,
                location: location,
                normalize: normalize,
                ty: ty,
            };
        res.bind_vao(vao);
        Ok(res)
    }

    /// Create XYZ vertex attribute.
    pub fn xyz(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao)
    }

    /// Create XY vertex attribute.
    pub fn xy(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao)
    }

    /// Create RGB color attribute.
    pub fn rgb(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao)
    }

    /// Create RGBA color attribute.
    pub fn rgba(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 4, gl::FALSE, gl::FLOAT, vao)
    }

    /// Create texture coordinate attribute.
    pub fn uv(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao)
    }

    /// Sets attribute data.
    pub unsafe fn set<T>(&self, data: &[T]) {
        gl::EnableVertexAttribArray(self.location);
        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        gl::BufferData(
            gl::ARRAY_BUFFER,
            data.len() as GLsizeiptr * mem::size_of::<T>() as GLsizeiptr,
            mem::transmute(data.as_ptr()),
            gl::DYNAMIC_DRAW
        );
    }
}

/// Compiles a shader.
///
/// Returns a shader or a message with the error.
pub fn compile_shader(
    shader_type: GLenum,
    source: &str
) -> Result<GLuint, String> {
    unsafe {
        let shader = gl::CreateShader(shader_type);
        gl::ShaderSource(shader, 1,
            &match CString::new(source) {
                Ok(x) => x.as_ptr(),
                Err(err) => return Err(format!("compile_shader: {}", err))
            }, ptr::null());
        gl::CompileShader(shader);
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
        if status == (gl::TRUE as GLint) {
            Ok(shader)
        } else {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);

            if len == 0 {
                Err("Compilation failed with no log. \
                     The OpenGL context might have been created on another thread, \
                     or not have been created.".to_string())
            } else {
                // Subtract 1 to skip the trailing null character.
                let mut buf = vec![0; len as usize - 1];
                gl::GetShaderInfoLog(
                    shader,
                    len,
                    ptr::null_mut(),
                    buf.as_mut_ptr() as *mut GLchar
                );

                gl::DeleteShader(shader);

                Err(String::from_utf8(buf).ok().expect(
                    "ShaderInfoLog not valid utf8"
                ))
            }
        }
    }
}

/// Creates a vertex buffer for an attribute from a program.
///
/// Returns `None` if there is no attribute with such name.
pub fn attribute_location(program: GLuint, name: &str) -> Result<GLuint, String> {
    unsafe {
        let id = gl::GetAttribLocation(program,
            match CString::new(name) {
                Ok(x) => x,
                Err(err) => return Err(format!("attribute_location: {}", err))
            }.as_ptr());
        if id < 0 {
            Err(format!("Attribute '{}' does not exists in shader", name))
        } else {
            Ok(id as GLuint)
        }
    }
}
<commit_msg>Fix possible dangling pointer to shader source<commit_after>//! Helper functions for dealing with shaders.

// External crates.
use gl;
use gl::types::{
    GLboolean,
    GLchar,
    GLenum,
    GLint,
    GLsizeiptr,
    GLuint,
};
use std::ffi::CString;
use std::{ ptr, mem };

/// Describes a shader attribute.
pub struct DynamicAttribute {
    /// The vertex buffer object.
    vbo: GLuint,
    /// The number of components.
    size: i32,
    /// The location of the attribute in shader.
    location: GLuint,
    /// Whether to normalize when sending to GPU.
    normalize: GLboolean,
    /// The type, for example gl::FLOAT.
    ty: GLenum,
}

impl Drop for DynamicAttribute {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteBuffers(1, &self.vbo);
        }
    }
}

impl DynamicAttribute {
    /// Binds to a vertex array object.
    ///
    /// The vertex array object remembers the format for later.
    fn bind_vao(&self, vao: GLuint) {
        let stride = 0;
        unsafe {
            gl::BindVertexArray(vao);
            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
            gl::VertexAttribPointer(
                self.location,
                self.size,
                self.ty,
                self.normalize,
                stride,
                ptr::null()
            );
        }
    }

    fn new(
        program: GLuint,
        name: &str,
        size: i32,
        normalize: GLboolean,
        ty: GLenum,
        vao: GLuint
    ) -> Result<Self, String> {
        let location = try!(attribute_location(program, name));
        let mut vbo = 0;
        unsafe {
            gl::GenBuffers(1, &mut vbo);
        }
        let res = DynamicAttribute {
                vbo: vbo,
                size: size,
                location: location,
                normalize: normalize,
                ty: ty,
            };
        res.bind_vao(vao);
        Ok(res)
    }

    /// Create XYZ vertex attribute.
    pub fn xyz(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao)
    }

    /// Create XY vertex attribute.
    pub fn xy(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao)
    }

    /// Create RGB color attribute.
    pub fn rgb(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao)
    }

    /// Create RGBA color attribute.
    pub fn rgba(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 4, gl::FALSE, gl::FLOAT, vao)
    }

    /// Create texture coordinate attribute.
    pub fn uv(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao)
    }

    /// Sets attribute data.
    pub unsafe fn set<T>(&self, data: &[T]) {
        gl::EnableVertexAttribArray(self.location);
        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        gl::BufferData(
            gl::ARRAY_BUFFER,
            data.len() as GLsizeiptr * mem::size_of::<T>() as GLsizeiptr,
            mem::transmute(data.as_ptr()),
            gl::DYNAMIC_DRAW
        );
    }
}

/// Compiles a shader.
///
/// Returns a shader or a message with the error.
pub fn compile_shader(
    shader_type: GLenum,
    source: &str
) -> Result<GLuint, String> {
    unsafe {
        let shader = gl::CreateShader(shader_type);
        {
            let source = &match CString::new(source) {
                Ok(x) => x,
                Err(err) => return Err(format!("compile_shader: {}", err))
            };
            gl::ShaderSource(shader, 1, &source.as_ptr(), ptr::null());
        }
        gl::CompileShader(shader);
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
        if status == (gl::TRUE as GLint) {
            Ok(shader)
        } else {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);

            if len == 0 {
                Err("Compilation failed with no log. \
                     The OpenGL context might have been created on another thread, \
                     or not have been created.".to_string())
            } else {
                // Subtract 1 to skip the trailing null character.
                let mut buf = vec![0; len as usize - 1];
                gl::GetShaderInfoLog(
                    shader,
                    len,
                    ptr::null_mut(),
                    buf.as_mut_ptr() as *mut GLchar
                );

                gl::DeleteShader(shader);

                Err(String::from_utf8(buf).ok().expect(
                    "ShaderInfoLog not valid utf8"
                ))
            }
        }
    }
}

/// Creates a vertex buffer for an attribute from a program.
///
/// Returns `None` if there is no attribute with such name.
pub fn attribute_location(program: GLuint, name: &str) -> Result<GLuint, String> {
    unsafe {
        let id = gl::GetAttribLocation(program,
            match CString::new(name) {
                Ok(x) => x,
                Err(err) => return Err(format!("attribute_location: {}", err))
            }.as_ptr());
        if id < 0 {
            Err(format!("Attribute '{}' does not exists in shader", name))
        } else {
            Ok(id as GLuint)
        }
    }
}
<|endoftext|>


<commit_before>use chrono::UTC;
use chrono::datetime::DateTime;

use params::*;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::units::JsonVal;
use rs_es::operations::search::{Sort, SortField, Order};

use terms::VectorOfTerms;

pub struct Talent;

#[derive(Debug, RustcDecodable)]
struct TalentsSearchResult {
  id: u32
}

impl Talent {
  pub fn visibility_filters(presented_talents: Vec<i32>) -> Vec<Filter> {
    let now = DateTime::timestamp(&UTC::now());

    let visibility_rules = Filter::build_bool()
                                  .with_must(
                                    vec![
                                      Filter::build_term("accepted", true)
                                             .build(),
                                      Filter::build_range("batch_starts_at")
                                             .with_lt(JsonVal::from(now))
                                             .with_format("epoch_second")
                                             .build(),
                                      Filter::build_range("batch_ends_at")
                                             .with_gte(JsonVal::from(now))
                                             .with_format("epoch_second")
                                             .build()
                                    ])
                                  .build();

    if presented_talents.is_empty() {
      let presented_talents_filters = Filter::build_bool()
                                             .with_must(
                                               vec![
                                                 <Filter as VectorOfTerms<i32>>::build_terms(
                                                   "ids", &presented_talents)
                                               ].into_iter()
                                                .flat_map(|x| x)
                                                .collect::<Vec<Filter>>())
                                             .build();
      vec![
        Filter::build_bool()
               .with_should(vec![visibility_rules, presented_talents_filters])
               .build()
      ]
    }
    else {
      vec![visibility_rules]
    }
  }

  pub fn search_filters(params: &Map) -> Query {
    let company_id = i32_vec_from_params!(params, "company_id");

    Query::build_filtered(Filter::build_bool()
                                 .with_must(
                                   vec![
                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_roles", &vec_from_params!(params, "work_roles")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_languages", &vec_from_params!(params, "work_languages")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_experience", &vec_from_params!(params, "work_experience")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_locations", &vec_from_params!(params, "work_locations")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                      "work_authorization", &vec_from_params!(params, "work_authorization")),

                                     Talent::visibility_filters(
                                       i32_vec_from_params!(params, "presented_talents"))
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .with_must_not(
                                   vec![
                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "company_ids", &company_id),

                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "blocked_companies", &company_id)
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .build())
          .build()
  }

  pub fn sorting_criteria() -> Sort {
    Sort::new(
      vec![
        SortField::new("updated_at", Some(Order::Desc)).build()
      ])
  }

  pub fn search(mut es: Client, params: &Map, indexes: &[&str]) -> Vec<u32> {
    let result = es.search_query()
                   .with_indexes(indexes)
                   .with_query(&Talent::search_filters(params))
                   .with_sort(&Talent::sorting_criteria())
                   .send()
                   .ok()
                   .unwrap();

    result.hits.hits.into_iter()
                    .map(|hit| {
                      let talent: TalentsSearchResult = hit.source().unwrap();
                      talent.id
                    })
                    .collect::<Vec<u32>>()
  }
}

#[cfg(test)]
mod tests {
  #[test]
  fn test_visibility_filters() {
    // TODO
  }

  #[test]
  fn test_search_filters() {
    // TODO
  }

  #[test]
  fn test_search() {
    // TODO
  }
}
<commit_msg>resources/user: add documentation<commit_after>use chrono::UTC;
use chrono::datetime::DateTime;

use params::*;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::units::JsonVal;
use rs_es::operations::search::{Sort, SortField, Order};

use terms::VectorOfTerms;

#[derive(Debug, RustcDecodable)]
struct TalentsSearchResult {
  id: u32
}

pub struct Talent;

impl Talent {
  /// Return a `Vec<Filter>` with visibility criteria for the talents.
  /// If `presented_talents` is provided, talents who match the IDs
  /// contained there skip the standard visibility criteria.
  ///
  /// Basically, the talents must be accepted into the platform and must be
  /// inside a living batch to match the visibility criteria.
  fn visibility_filters(presented_talents: Vec<i32>) -> Vec<Filter> {
    let now = DateTime::timestamp(&UTC::now());

    let visibility_rules = Filter::build_bool()
                                  .with_must(
                                    vec![
                                      Filter::build_term("accepted", true)
                                             .build(),
                                      Filter::build_range("batch_starts_at")
                                             .with_lt(JsonVal::from(now))
                                             .with_format("epoch_second")
                                             .build(),
                                      Filter::build_range("batch_ends_at")
                                             .with_gte(JsonVal::from(now))
                                             .with_format("epoch_second")
                                             .build()
                                    ])
                                  .build();

    if presented_talents.is_empty() {
      let presented_talents_filters = Filter::build_bool()
                                             .with_must(
                                               vec![
                                                 <Filter as VectorOfTerms<i32>>::build_terms(
                                                   "ids", &presented_talents)
                                               ].into_iter()
                                                .flat_map(|x| x)
                                                .collect::<Vec<Filter>>())
                                             .build();
      vec![
        Filter::build_bool()
               .with_should(vec![visibility_rules, presented_talents_filters])
               .build()
      ]
    }
    else {
      vec![visibility_rules]
    }
  }

  /// Given parameters inside the query string mapped inisde a `Map`,
  /// return a `Query` for ElasticSearch.
  ///
  /// The `VectorOfTerms` are ORred, while `Filter`s are ANDed.
  /// I.e.: given ["Fullstack", "DevOps"] as `work_roles`, found talents
  /// will present at least one of these roles), but both `work_roles`
  /// and `work_languages`, if provided, must not be empty.
  fn search_filters(params: &Map) -> Query {
    let company_id = i32_vec_from_params!(params, "company_id");

    Query::build_filtered(Filter::build_bool()
                                 .with_must(
                                   vec![
                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_roles", &vec_from_params!(params, "work_roles")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_languages", &vec_from_params!(params, "work_languages")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_experience", &vec_from_params!(params, "work_experience")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_locations", &vec_from_params!(params, "work_locations")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                      "work_authorization", &vec_from_params!(params, "work_authorization")),

                                     Talent::visibility_filters(
                                       i32_vec_from_params!(params, "presented_talents"))
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .with_must_not(
                                   vec![
                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "company_ids", &company_id),

                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "blocked_companies", &company_id)
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .build())
          .build()
  }

  /// Return a `Sort` that makes values be sorted for `updated_at`, descendently.
  fn sorting_criteria() -> Sort {
    Sort::new(
      vec![
        SortField::new("updated_at", Some(Order::Desc)).build()
      ])
  }

  /// Query ElasticSearch on given `indexes` and `params` and return the IDs of
  /// the found talents.
  pub fn search(mut es: Client, params: &Map, indexes: &[&str]) -> Vec<u32> {
    let result = es.search_query()
                   .with_indexes(indexes)
                   .with_query(&Talent::search_filters(params))
                   .with_sort(&Talent::sorting_criteria())
                   .send()
                   .ok()
                   .unwrap();

    result.hits.hits.into_iter()
                    .map(|hit| {
                      let talent: TalentsSearchResult = hit.source().unwrap();
                      talent.id
                    })
                    .collect::<Vec<u32>>()
  }
}

#[cfg(test)]
mod tests {
  #[test]
  fn test_visibility_filters() {
    // TODO
  }

  #[test]
  fn test_search_filters() {
    // TODO
  }

  #[test]
  fn test_search() {
    // TODO
  }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    access_path::AccessPath,
    account_config,
    discovery_info::DiscoveryInfo,
    event::{EventHandle, EventKey},
    move_resource::MoveResource,
};
use anyhow::Result;
use move_core_types::identifier::{IdentStr, Identifier};
use once_cell::sync::Lazy;
#[cfg(any(test, feature = "fuzzing"))]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use std::{iter::IntoIterator, ops::Deref, vec};

static DISCOVERY_SET_MODULE_NAME: Lazy<Identifier> =
    Lazy::new(|| Identifier::new("LibraSystem").unwrap());

pub fn discovery_set_module_name() -> &'static IdentStr {
    &*DISCOVERY_SET_MODULE_NAME
}

/// The path to the discovery set change event handle under a DiscoverSetResource.
pub static DISCOVERY_SET_CHANGE_EVENT_PATH: Lazy<Vec<u8>> = Lazy::new(|| {
    let mut path = DiscoverySetResource::resource_path();
    path.extend_from_slice(b"/change_events_count/");
    path
});

/// The AccessPath pointing to the discovery set change event handle under the system discovery set address.
pub static GLOBAL_DISCOVERY_SET_CHANGE_EVENT_PATH: Lazy<AccessPath> = Lazy::new(|| {
    AccessPath::new(
        account_config::discovery_set_address(),
        DISCOVERY_SET_CHANGE_EVENT_PATH.to_vec(),
    )
});

#[derive(Debug, Deserialize, Serialize)]
pub struct DiscoverySetResource {
    /// The current discovery set. Updated only at epoch boundaries via reconfiguration.
    discovery_set: DiscoverySet,
    /// Handle where discovery set change events are emitted
    change_events: EventHandle,
}

impl DiscoverySetResource {
    pub fn change_events(&self) -> &EventHandle {
        &self.change_events
    }

    pub fn discovery_set(&self) -> &DiscoverySet {
        &self.discovery_set
    }
}

impl MoveResource for DiscoverySetResource {
    const MODULE_NAME: &'static str = "LibraSystem";
    const STRUCT_NAME: &'static str = "DiscoverySet";
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct DiscoverySet(Vec<DiscoveryInfo>);

impl DiscoverySet {
    pub fn new(discovery_set: Vec<DiscoveryInfo>) -> Self {
        Self(discovery_set)
    }

    pub fn change_event_key() -> EventKey {
        EventKey::new_from_address(&account_config::discovery_set_address(), 2)
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
        lcs::from_bytes(bytes).map_err(Into::into)
    }
}

impl Deref for DiscoverySet {
    type Target = [DiscoveryInfo];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl IntoIterator for DiscoverySet {
    type Item = DiscoveryInfo;
    type IntoIter = vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

#[cfg(any(test, feature = "fuzzing"))]
pub mod mock {
    use super::*;

    use crate::on_chain_config::ValidatorSet;
    use libra_crypto::{x25519, Uniform};
    use parity_multiaddr::Multiaddr;
    use std::str::FromStr;

    pub fn mock_discovery_set(validator_set: &ValidatorSet) -> DiscoverySet {
        let mock_pubkey = x25519::PrivateKey::generate_for_testing().public_key();
        let mock_addr = Multiaddr::from_str("/ip4/127.0.0.1/tcp/1234").unwrap();

        let discovery_set = validator_set
            .payload()
            .iter()
            .map(|validator_pubkeys| DiscoveryInfo {
                account_address: *validator_pubkeys.account_address(),
                validator_network_identity_pubkey: validator_pubkeys.network_identity_public_key(),
                validator_network_address: mock_addr.clone(),
                fullnodes_network_identity_pubkey: mock_pubkey,
                fullnodes_network_address: mock_addr.clone(),
            })
            .collect::<Vec<_>>();
        DiscoverySet::new(discovery_set)
    }
}
<commit_msg>[types] Add DiscoverySetChangeEvent to deser from ContractEvent<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    access_path::{AccessPath, Accesses},
    account_config,
    contract_event::ContractEvent,
    discovery_info::DiscoveryInfo,
    event::{EventHandle, EventKey},
    language_storage::{StructTag, TypeTag},
    move_resource::MoveResource,
};
use anyhow::{ensure, Error, Result};
use move_core_types::identifier::Identifier;
use once_cell::sync::Lazy;
#[cfg(any(test, feature = "fuzzing"))]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, iter::IntoIterator, ops::Deref, vec};

pub static DISCOVERY_SET_MODULE_NAME: Lazy<Identifier> =
    Lazy::new(|| Identifier::new("LibraSystem").unwrap());

pub static DISCOVERY_SET_STRUCT_NAME: Lazy<Identifier> =
    Lazy::new(|| Identifier::new("DiscoverySet").unwrap());

pub static DISCOVERY_SET_CHANGE_EVENT_STRUCT_NAME: Lazy<Identifier> =
    Lazy::new(|| Identifier::new("DiscoverySetChangeEvent").unwrap());

pub static DISCOVERY_SET_STRUCT_TAG: Lazy<StructTag> = Lazy::new(|| StructTag {
    name: DISCOVERY_SET_STRUCT_NAME.clone(),
    address: account_config::CORE_CODE_ADDRESS,
    module: DISCOVERY_SET_MODULE_NAME.clone(),
    type_params: vec![],
});

pub static DISCOVERY_SET_CHANGE_EVENT_STRUCT_TAG: Lazy<StructTag> = Lazy::new(|| StructTag {
    name: DISCOVERY_SET_CHANGE_EVENT_STRUCT_NAME.clone(),
    address: account_config::CORE_CODE_ADDRESS,
    module: DISCOVERY_SET_MODULE_NAME.clone(),
    type_params: vec![],
});

pub static DISCOVERY_SET_TYPE_TAG: Lazy<TypeTag> =
    Lazy::new(|| TypeTag::Struct(DISCOVERY_SET_STRUCT_TAG.clone()));

pub static DISCOVERY_SET_CHANGE_EVENT_TYPE_TAG: Lazy<TypeTag> =
    Lazy::new(|| TypeTag::Struct(DISCOVERY_SET_CHANGE_EVENT_STRUCT_TAG.clone()));

/// Path to the DiscoverySet resource.
pub static DISCOVERY_SET_RESOURCE_PATH: Lazy<Vec<u8>> =
    Lazy::new(|| AccessPath::resource_access_vec(&*DISCOVERY_SET_STRUCT_TAG, &Accesses::empty()));

/// The path to the discovery set change event handle under a DiscoverSetResource.
pub static DISCOVERY_SET_CHANGE_EVENT_PATH: Lazy<Vec<u8>> = Lazy::new(|| {
    let mut path = DiscoverySetResource::resource_path();
    path.extend_from_slice(b"/change_events_count/");
    path
});

/// The AccessPath pointing to the discovery set change event handle under the system discovery set address.
pub static GLOBAL_DISCOVERY_SET_CHANGE_EVENT_PATH: Lazy<AccessPath> = Lazy::new(|| {
    AccessPath::new(
        account_config::discovery_set_address(),
        DISCOVERY_SET_CHANGE_EVENT_PATH.to_vec(),
    )
});

#[derive(Debug, Deserialize, Serialize)]
pub struct DiscoverySetResource {
    /// The current discovery set. Updated only at epoch boundaries via reconfiguration.
    discovery_set: DiscoverySet,
    /// Handle where discovery set change events are emitted
    change_events: EventHandle,
}

impl DiscoverySetResource {
    pub fn change_events(&self) -> &EventHandle {
        &self.change_events
    }

    pub fn discovery_set(&self) -> &DiscoverySet {
        &self.discovery_set
    }
}

impl MoveResource for DiscoverySetResource {
    const MODULE_NAME: &'static str = "LibraSystem";
    const STRUCT_NAME: &'static str = "DiscoverySet";
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct DiscoverySet(Vec<DiscoveryInfo>);

impl DiscoverySet {
    pub fn new(discovery_set: Vec<DiscoveryInfo>) -> Self {
        Self(discovery_set)
    }

    pub fn change_event_key() -> EventKey {
        EventKey::new_from_address(&account_config::discovery_set_address(), 2)
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
        lcs::from_bytes(bytes).map_err(Into::into)
    }
}

impl Deref for DiscoverySet {
    type Target = [DiscoveryInfo];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl IntoIterator for DiscoverySet {
    type Item = DiscoveryInfo;
    type IntoIter = vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

#[derive(Clone, Debug)]
pub struct DiscoverySetChangeEvent {
    pub event_seq_num: u64,
    pub discovery_set: DiscoverySet,
}

impl TryFrom<&ContractEvent> for DiscoverySetChangeEvent {
    type Error = Error;

    fn try_from(contract_event: &ContractEvent) -> Result<Self> {
        let event_seq_num = contract_event.sequence_number();

        ensure!(
            &*DISCOVERY_SET_CHANGE_EVENT_TYPE_TAG == contract_event.type_tag(),
            "Failed to deserialize DiscoverySetChangeEvent, unexpected type tag: {:?}, expected: {:?}",
            contract_event.type_tag(),
            &*DISCOVERY_SET_CHANGE_EVENT_TYPE_TAG,
        );

        let discovery_set = DiscoverySet::from_bytes(contract_event.event_data())?;

        Ok(DiscoverySetChangeEvent {
            event_seq_num,
            discovery_set,
        })
    }
}

#[cfg(any(test, feature = "fuzzing"))]
pub mod mock {
    use super::*;

    use crate::on_chain_config::ValidatorSet;
    use libra_crypto::{x25519, Uniform};
    use parity_multiaddr::Multiaddr;
    use std::str::FromStr;

    pub fn mock_discovery_set(validator_set: &ValidatorSet) -> DiscoverySet {
        let mock_pubkey = x25519::PrivateKey::generate_for_testing().public_key();
        let mock_addr = Multiaddr::from_str("/ip4/127.0.0.1/tcp/1234").unwrap();

        let discovery_set = validator_set
            .payload()
            .iter()
            .map(|validator_pubkeys| DiscoveryInfo {
                account_address: *validator_pubkeys.account_address(),
                validator_network_identity_pubkey: validator_pubkeys.network_identity_public_key(),
                validator_network_address: mock_addr.clone(),
                fullnodes_network_identity_pubkey: mock_pubkey,
                fullnodes_network_address: mock_addr.clone(),
            })
            .collect::<Vec<_>>();
        DiscoverySet::new(discovery_set)
    }
}
<|endoftext|>


<commit_before>use {time, node};
use hash::SubotaiHash;
use std::collections::HashMap;
use std::sync::RwLock;
use std::cmp;

/// User facing storage entry. This is the data type that can be stored and retrieved
/// in the Subotai network, consisting of either another hash (Value) or a binary blob.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum StorageEntry {
   Value(SubotaiHash),
   Blob(Vec<u8>),
}

/// Storage entry wrapper that includes management information.
#[derive(Debug, Clone)]
struct ExtendedEntry {
   entry           : StorageEntry,
   expiration      : time::Tm,
   republish_ready : bool,
}

/// Groups of extended entries classified by key.
type KeyGroup = Vec<ExtendedEntry>;

pub struct Storage {
   key_groups    : RwLock<HashMap<SubotaiHash, KeyGroup> >,
   parent_id     : SubotaiHash,
   configuration : node::Configuration,
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum StoreResult {
   Success,
   StorageFull,
   BlobTooBig,
}

impl Storage {
   pub fn new(parent_id: SubotaiHash, configuration: node::Configuration) -> Storage {
      Storage {
         key_groups    : RwLock::new(HashMap::with_capacity(configuration.max_storage)),
         parent_id     : parent_id,
         configuration : configuration,
      }
   }

   /// Returns number of entries.
   pub fn len(&self) -> usize {
      self.key_groups.read().unwrap().values().flat_map(|group| group.iter()).count()
   }

   pub fn is_empty(&self) -> bool {
      self.len() == 0
   }

   /// Retrieves all entries in a key_group.
   pub fn retrieve(&self, key: &SubotaiHash) -> Option<Vec<StorageEntry>> {
      if let Some(key_group) = self.key_groups.read().unwrap().get(key) {
         Some(key_group.iter().cloned().map(|extended| extended.entry).collect())
      } else {
         None
      }
   }

   /// Stores an entry in a key_group, with an expiration date, if it wasn't present already.
   /// If it was present, it keeps the latest expiration time and marks as not ready for republishing.
   pub fn store(&self, key: &SubotaiHash, entry: &StorageEntry, expiration: &time::Tm) -> StoreResult {
      if self.is_big_blob(entry) {
         return StoreResult::BlobTooBig;
      }

      // Expiration time is clamped to a reasonable value.
      let expiration = cmp::min(*expiration, time::now() + time::Duration::hours(self.configuration.base_expiration_time_hrs));

      let mut key_groups = self.key_groups.write().unwrap();
      if key_groups.contains_key(key) {
         let key_group = key_groups.get_mut(key).unwrap();
         let already_existed = if let Some(preexisting_pair) = key_group.iter_mut().find(|stored_pair| stored_pair.entry == *entry) {
            preexisting_pair.expiration = cmp::max(preexisting_pair.expiration, expiration); // Take the latest expiration time.
            preexisting_pair.republish_ready = false;
            true
         } else {
            false
         };
         if !already_existed {
            if self.len() > self.configuration.max_storage {
               return StoreResult::StorageFull;
            }
            let new_entry = ExtendedEntry {
               entry           : entry.clone(),
               expiration      : expiration.clone(),
               republish_ready : false,
            };
            key_group.push(new_entry);
         }
      } else {
         if self.len() > self.configuration.max_storage {
            return StoreResult::StorageFull;
         }
         let mut key_group = KeyGroup::new();
         let new_entry = ExtendedEntry {
               entry           : entry.clone(),
               expiration      : expiration.clone(),
               republish_ready : false,
         };
         key_group.push(new_entry);
         key_groups.insert(key.clone(), key_group);
      }
      StoreResult::Success
   }

   fn is_big_blob(&self, entry: &StorageEntry) -> bool {
      match entry {
         &StorageEntry::Blob(ref vec) => vec.len() > self.configuration.max_storage_blob_size,
         _ => false,
      }
   }

   pub fn clear_expired_entries(&self) {
      let now = time::now();
      let mut key_groups = self.key_groups.write().unwrap();
      for mut key_group in key_groups.values_mut() {
         key_group.retain(|&ExtendedEntry{ expiration, .. }| now < expiration);
      }

      // We clear the keygroups that have run out of entries.
      let empty_keys: Vec<_> = key_groups
         .iter()
         .filter_map(|(key, group)| if group.is_empty() { Some(key) } else { None })
         .cloned()
         .collect();

      for key in empty_keys {
         key_groups.remove(&key);
      }
   }

   ///Marks all entries as ready for republishing.
   pub fn mark_all_as_ready(&self) {
      let mut key_groups = self.key_groups.write().unwrap();
      let extended_entries = key_groups.values_mut().flat_map(|group| group.iter_mut());
      for &mut ExtendedEntry {ref mut republish_ready, ..} in extended_entries {
         *republish_ready = true;
      }
   }

   /// Retrieves all key-entry pairs ready for republishing, together with their current expiration date
   pub fn get_all_ready_entries(&self) -> Vec<(SubotaiHash, StorageEntry, time::Tm)>  {
      let key_groups = self.key_groups.read().unwrap();
      let mut all_ready_entries = Vec::<(SubotaiHash, StorageEntry, time::Tm)>::new();
      for (key, group) in key_groups.iter() {
         let mut ready_entries_in_group: Vec<(SubotaiHash, StorageEntry, time::Tm)> = group
         .iter()
         .filter_map(|ext| if ext.republish_ready { Some((key.clone(), ext.entry.clone(), ext.expiration.clone())) } else { None } )
         .collect();
         all_ready_entries.append(&mut ready_entries_in_group);
      }
      all_ready_entries
   }
}

#[cfg(test)]
mod tests {
   use super::*;
   use {hash, time, node};
}
<commit_msg>More tests<commit_after>use {time, node};
use hash::SubotaiHash;
use std::collections::HashMap;
use std::sync::RwLock;
use std::cmp;

/// User facing storage entry. This is the data type that can be stored and retrieved
/// in the Subotai network, consisting of either another hash (Value) or a binary blob.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum StorageEntry {
   Value(SubotaiHash),
   Blob(Vec<u8>),
}

/// Storage entry wrapper that includes management information.
#[derive(Debug, Clone)]
struct ExtendedEntry {
   entry           : StorageEntry,
   expiration      : time::Tm,
   republish_ready : bool,
}

/// Groups of extended entries classified by key.
type KeyGroup = Vec<ExtendedEntry>;

pub struct Storage {
   key_groups    : RwLock<HashMap<SubotaiHash, KeyGroup> >,
   parent_id     : SubotaiHash,
   configuration : node::Configuration,
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum StoreResult {
   Success,
   StorageFull,
   BlobTooBig,
}

impl Storage {
   pub fn new(parent_id: SubotaiHash, configuration: node::Configuration) -> Storage {
      Storage {
         key_groups    : RwLock::new(HashMap::with_capacity(configuration.max_storage)),
         parent_id     : parent_id,
         configuration : configuration,
      }
   }

   /// Returns number of entries.
   pub fn len(&self) -> usize {
      self.key_groups.read().unwrap().values().flat_map(|group| group.iter()).count()
   }

   pub fn is_empty(&self) -> bool {
      self.len() == 0
   }

   /// Retrieves all entries in a key_group.
   pub fn retrieve(&self, key: &SubotaiHash) -> Option<Vec<StorageEntry>> {
      if let Some(key_group) = self.key_groups.read().unwrap().get(key) {
         Some(key_group.iter().cloned().map(|extended| extended.entry).collect())
      } else {
         None
      }
   }

   /// Stores an entry in a key_group, with an expiration date, if it wasn't present already.
   /// If it was present, it keeps the latest expiration time and marks as not ready for republishing.
   pub fn store(&self, key: &SubotaiHash, entry: &StorageEntry, expiration: &time::Tm) -> StoreResult {
      if self.is_big_blob(entry) {
         return StoreResult::BlobTooBig;
      }

      // Expiration time is clamped to a reasonable value.
      let expiration = cmp::min(*expiration, time::now() + time::Duration::hours(self.configuration.base_expiration_time_hrs));
      let initial_length = self.len();

      let mut key_groups = self.key_groups.write().unwrap();
      if key_groups.contains_key(key) {
         let key_group = key_groups.get_mut(key).unwrap();
         let already_existed = if let Some(preexisting_pair) = key_group.iter_mut().find(|stored_pair| stored_pair.entry == *entry) {
            preexisting_pair.expiration = cmp::max(preexisting_pair.expiration, expiration); // Take the latest expiration time.
            preexisting_pair.republish_ready = false;
            true
         } else {
            false
         };
         if !already_existed {
            if initial_length > self.configuration.max_storage {
               return StoreResult::StorageFull;
            }
            let new_entry = ExtendedEntry {
               entry           : entry.clone(),
               expiration      : expiration.clone(),
               republish_ready : false,
            };
            key_group.push(new_entry);
         }
      } else {
         if initial_length > self.configuration.max_storage {
            return StoreResult::StorageFull;
         }
         let mut key_group = KeyGroup::new();
         let new_entry = ExtendedEntry {
               entry           : entry.clone(),
               expiration      : expiration.clone(),
               republish_ready : false,
         };
         key_group.push(new_entry);
         key_groups.insert(key.clone(), key_group);
      }
      StoreResult::Success
   }

   fn is_big_blob(&self, entry: &StorageEntry) -> bool {
      match entry {
         &StorageEntry::Blob(ref vec) => vec.len() > self.configuration.max_storage_blob_size,
         _ => false,
      }
   }

   pub fn clear_expired_entries(&self) {
      let now = time::now();
      let mut key_groups = self.key_groups.write().unwrap();
      for mut key_group in key_groups.values_mut() {
         key_group.retain(|&ExtendedEntry{ expiration, .. }| now < expiration);
      }

      // We clear the keygroups that have run out of entries.
      let empty_keys: Vec<_> = key_groups
         .iter()
         .filter_map(|(key, group)| if group.is_empty() { Some(key) } else { None })
         .cloned()
         .collect();

      for key in empty_keys {
         key_groups.remove(&key);
      }
   }

   /// Marks all entries as ready for republishing.
   pub fn mark_all_as_ready(&self) {
      let mut key_groups = self.key_groups.write().unwrap();
      let extended_entries = key_groups.values_mut().flat_map(|group| group.iter_mut());
      for &mut ExtendedEntry {ref mut republish_ready, ..} in extended_entries {
         *republish_ready = true;
      }
   }

   /// Retrieves all key-entry pairs ready for republishing, together with their current expiration date
   pub fn get_all_ready_entries(&self) -> Vec<(SubotaiHash, StorageEntry, time::Tm)>  {
      let key_groups = self.key_groups.read().unwrap();
      let mut all_ready_entries = Vec::<(SubotaiHash, StorageEntry, time::Tm)>::new();
      for (key, group) in key_groups.iter() {
         let mut ready_entries_in_group: Vec<(SubotaiHash, StorageEntry, time::Tm)> = group
         .iter()
         .filter_map(|ext| if ext.republish_ready { Some((key.clone(), ext.entry.clone(), ext.expiration.clone())) } else { None } )
         .collect();
         all_ready_entries.append(&mut ready_entries_in_group);
      }
      all_ready_entries
   }
}

#[cfg(test)]
mod tests {
   use super::*;
   use {time, node};
   use hash::SubotaiHash;

   #[test]
   fn storing_and_retrieving_on_same_key() {
      let storage = default_storage();
      let key = SubotaiHash::random();
      let entry = StorageEntry::Value(SubotaiHash::random());
      let another_entry = StorageEntry::Blob(Vec::<u8>::new());
      let expiration = time::now() + time::Duration::minutes(30);
      match storage.store(&key, &entry, &expiration) {
         StoreResult::Success => (),
         _ => panic!(),
      }
      match storage.store(&key, &another_entry, &expiration) {
         StoreResult::Success => (),
         _ => panic!(),
      }

      let retrieved_entries = storage.retrieve(&key).unwrap();
      assert_eq!(retrieved_entries.len(), 2);
      assert_eq!(entry, retrieved_entries[0]);
      assert_eq!(another_entry, retrieved_entries[1]);
   }

   #[test]
   fn storing_preexisting_entry_updates_to_max_expiration() {
      let now = time::now();
      let storage = default_storage();
      let key = SubotaiHash::random();
      let entry = StorageEntry::Value(SubotaiHash::random());
      let expiration_soon = now + time::Duration::minutes(30);
      let expiration_later = now + time::Duration::hours(10);

      storage.store(&key, &entry, &expiration_soon);
      storage.store(&key, &entry, &expiration_later);

      // Little trick to get the expiration date through the API
      storage.mark_all_as_ready();
      let entries = storage.get_all_ready_entries();
      assert_eq!(entries.len(), 1);
      assert_eq!(expiration_later, entries[0].2);
   }

   #[test]
   fn storing_preexisting_entry_keeps_max_expiration() {
      let now = time::now();
      let storage = default_storage();
      let key = SubotaiHash::random();
      let entry = StorageEntry::Value(SubotaiHash::random());
      let expiration_soon = now + time::Duration::minutes(30);
      let expiration_later = now + time::Duration::hours(10);

      // Different order!
      storage.store(&key, &entry, &expiration_later);
      storage.store(&key, &entry, &expiration_soon);

      // Little trick to get the expiration date through the API
      storage.mark_all_as_ready();
      let entries = storage.get_all_ready_entries();
      assert_eq!(entries.len(), 1);
      assert_eq!(expiration_later, entries[0].2);
   }

   #[test]
   fn clearing_expired_entries() {
      let now = time::now();
      let storage = default_storage();
      let key_alpha = SubotaiHash::random();
      let entry_alpha = StorageEntry::Value(SubotaiHash::random());
      let expiration_alpha = now + time::Duration::minutes(30);
      let key_beta = SubotaiHash::random();
      let entry_beta = StorageEntry::Value(SubotaiHash::random());
      let expiration_beta = now - time::Duration::minutes(30); // Expired!

      storage.store(&key_alpha, &entry_alpha, &expiration_alpha);
      storage.store(&key_beta, &entry_beta, &expiration_beta);
      assert_eq!(storage.len(), 2);
      storage.clear_expired_entries();
      assert_eq!(storage.len(), 1);

      assert!(storage.retrieve(&key_beta).is_none());
      assert!(storage.retrieve(&key_alpha).is_some());
   }

   fn default_storage() -> Storage {
      let default_config: node::Configuration = Default::default();
      Storage::new(SubotaiHash::random(), default_config)
   }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;

use glob::glob;
use glob::Paths;
use glob::PatternError;

use storage::file::id::FileID;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use module::Module;

/*
 * A path represents either a GLOB ("/tmp/store/module-*-*.imag" for example) or a full path
 *
 * It can be used to generate a File or iterate over some files
 *
 */
struct Path<'a> {

    /*
     * The base part ("/tmp/")
     */
    base: PathBuf,

    /*
     * The store part ("/store/")
     */
    store: PathBuf,

    /*
     * The module
     */
    module: &'a Module<'a>,

    /*
     * The ID
     */
    idtype: Option<FileIDType>,
    idhash: Option<FileHash>,
    id:     Option<FileID>,

}

impl<'a> Path<'a> {

    fn new(base: PathBuf, store: PathBuf, m: &'a Module<'a>, id: FileID) -> Path<'a> {
        Path {
            base:   base,
            store:  store,
            module: m,
            idtype: Some(id.get_type()),
            idhash: Some(id.get_id()),
            id:     Some(id),
        }
    }

    fn new_with_idtype(base: PathBuf, store: PathBuf, m: &'a Module<'a>, id: FileIDType) -> Path<'a> {
        Path {
            base:   base,
            store:  store,
            module: m,
            idtype: Some(id),
            idhash: None,
            id:     None,
        }
    }

    fn new_with_idhash(base: PathBuf, store: PathBuf, m: &'a Module<'a>, id: FileHash) -> Path<'a> {
        Path {
            base:   base,
            store:  store,
            module: m,
            idtype: None,
            idhash: Some(id),
            id:     None,
        }
    }

}

/*
 * Transform Path into str, so we can call glob() on it
 */
impl<'a> Into<&'a str> for Path<'a> {

    fn into(self) -> &'a str {
        let s = self.base.clone();
        s.push(self.store.clone());
        s.push(self.module.name());
        s.push(self.id.into());
        s.set_extension("imag");
        s.to_str().unwrap_or("")
    }
}

impl<'a> Into<Result<Paths, PatternError>> for Path<'a> {

    fn into(self) -> Result<Paths, PatternError> {
        let s : &str = self.into();
        glob(s)
    }

}
<commit_msg>Fixup Into<str> for Path<commit_after>use std::path::PathBuf;

use glob::glob;
use glob::Paths;
use glob::PatternError;

use storage::file::id::FileID;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use module::Module;

/*
 * A path represents either a GLOB ("/tmp/store/module-*-*.imag" for example) or a full path
 *
 * It can be used to generate a File or iterate over some files
 *
 */
struct Path<'a> {

    /*
     * The base part ("/tmp/")
     */
    base: PathBuf,

    /*
     * The store part ("/store/")
     */
    store: PathBuf,

    /*
     * The module
     */
    module: &'a Module<'a>,

    /*
     * The ID
     */
    idtype: Option<FileIDType>,
    idhash: Option<FileHash>,
    id:     Option<FileID>,

}

impl<'a> Path<'a> {

    fn new(base: PathBuf, store: PathBuf, m: &'a Module<'a>, id: FileID) -> Path<'a> {
        Path {
            base:   base,
            store:  store,
            module: m,
            idtype: Some(id.get_type()),
            idhash: Some(id.get_id()),
            id:     Some(id),
        }
    }

    fn new_with_idtype(base: PathBuf, store: PathBuf, m: &'a Module<'a>, id: FileIDType) -> Path<'a> {
        Path {
            base:   base,
            store:  store,
            module: m,
            idtype: Some(id),
            idhash: None,
            id:     None,
        }
    }

    fn new_with_idhash(base: PathBuf, store: PathBuf, m: &'a Module<'a>, id: FileHash) -> Path<'a> {
        Path {
            base:   base,
            store:  store,
            module: m,
            idtype: None,
            idhash: Some(id),
            id:     None,
        }
    }

}

/*
 * Transform Path into str, so we can call glob() on it
 */
impl<'a> Into<&'a str> for Path<'a> {

    fn into(self) -> &'a str {
        let s = self.base.clone();
        s.push(self.store.clone());
        s.push(self.module.name());
        if self.id.is_some() {
            let idstr : String = self.id.unwrap().into();
            s.push(idstr);
        } else {
            s.push("*");
        }
        s.set_extension("imag");
        s.to_str().unwrap_or("")
    }
}

impl<'a> Into<Result<Paths, PatternError>> for Path<'a> {

    fn into(self) -> Result<Paths, PatternError> {
        let s : &str = self.into();
        glob(s)
    }

}
<|endoftext|>


<commit_before>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_ec2::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// The ID of the instance to reboot.
    #[structopt(short, long)]
    instance_id: String,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Reboots an instance.
// snippet-start:[ec2.rust.reboot-instance]
async fn reboot_instance(client: &aws_sdk_ec2::Client, id: &str) -> Result<(), aws_sdk_ec2::Error> {
    client.reboot_instances().instance_ids(id).send().await?;

    println!("Rebooted instance.");
    Ok(())
}
// snippet-end:[ec2.rust.reboot-instance]

/// Reboots an Amazon EC2 instance.
/// # Arguments
///
/// * `-i INSTANCE-ID` - The ID of the instances to reboot.
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display additional information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();
    let Opt {
        region,
        instance_id,
        verbose,
    } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    if verbose {
        println!("EC2 client version: {}", PKG_VERSION);
        println!(
            "Region:             {}",
            region_provider.region().await.unwrap().as_ref()
        );
        println!("Instance ID:        {}", instance_id);
        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    reboot_instance(&client, &instance_id).await.unwrap();

    Ok(())
}

#[actix_rt::test]
async fn test_reboot_instance() {
    let shared_config = aws_config::load_from_env().await;
    let client = Client::new(&shared_config);

    client.reboot_instances().instance_ids("id");
}
<commit_msg>Update reboot-instance.rs<commit_after>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_ec2::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// The ID of the instance to reboot.
    #[structopt(short, long)]
    instance_id: String,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Reboots an instance.
// snippet-start:[ec2.rust.reboot-instance]
async fn reboot_instance(client: &aws_sdk_ec2::Client, id: &str) -> Result<(), aws_sdk_ec2::Error> {
    client.reboot_instances().instance_ids(id).send().await?;

    println!("Rebooted instance.");
    Ok(())
}
// snippet-end:[ec2.rust.reboot-instance]

/// Reboots an Amazon EC2 instance.
/// # Arguments
///
/// * `-i INSTANCE-ID` - The ID of the instances to reboot.
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display additional information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();
    let Opt {
        region,
        instance_id,
        verbose,
    } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    if verbose {
        println!("EC2 client version: {}", PKG_VERSION);
        println!(
            "Region:             {}",
            region_provider.region().await.unwrap().as_ref()
        );
        println!("Instance ID:        {}", instance_id);
        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    reboot_instance(&client, &instance_id).await
}
<|endoftext|>


<commit_before>//! See [`CargoWorkspace`].

use std::convert::TryInto;
use std::iter;
use std::path::PathBuf;
use std::{ops, process::Command};

use anyhow::{Context, Result};
use base_db::Edition;
use cargo_metadata::{CargoOpt, MetadataCommand};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use serde::Deserialize;
use serde_json::from_value;

use crate::CfgOverrides;
use crate::{utf8_stdout, ManifestPath};

/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
///
/// Note that internally, rust analyzer uses a different structure:
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
///
/// We use absolute paths here, `cargo metadata` guarantees to always produce
/// abs paths.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CargoWorkspace {
    packages: Arena<PackageData>,
    targets: Arena<TargetData>,
    workspace_root: AbsPathBuf,
}

impl ops::Index<Package> for CargoWorkspace {
    type Output = PackageData;
    fn index(&self, index: Package) -> &PackageData {
        &self.packages[index]
    }
}

impl ops::Index<Target> for CargoWorkspace {
    type Output = TargetData;
    fn index(&self, index: Target) -> &TargetData {
        &self.targets[index]
    }
}

/// Describes how to set the rustc source directory.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RustcSource {
    /// Explicit path for the rustc source directory.
    Path(AbsPathBuf),
    /// Try to automatically detect where the rustc source directory is.
    Discover,
}

/// Crates to disable `#[cfg(test)]` on.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UnsetTestCrates {
    None,
    Only(Vec<String>),
    All,
}

impl Default for UnsetTestCrates {
    fn default() -> Self {
        Self::None
    }
}

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CargoConfig {
    /// Do not activate the `default` feature.
    pub no_default_features: bool,

    /// Activate all available features
    pub all_features: bool,

    /// List of features to activate.
    /// This will be ignored if `cargo_all_features` is true.
    pub features: Vec<String>,

    /// rustc target
    pub target: Option<String>,

    /// Don't load sysroot crates (`std`, `core` & friends). Might be useful
    /// when debugging isolated issues.
    pub no_sysroot: bool,

    /// rustc private crate source
    pub rustc_source: Option<RustcSource>,

    /// crates to disable `#[cfg(test)]` on
    pub unset_test_crates: UnsetTestCrates,

    pub wrap_rustc_in_build_scripts: bool,
}

impl CargoConfig {
    pub fn cfg_overrides(&self) -> CfgOverrides {
        match &self.unset_test_crates {
            UnsetTestCrates::None => CfgOverrides::Selective(iter::empty().collect()),
            UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective(
                unset_test_crates
                    .iter()
                    .map(|name| name.clone())
                    .zip(iter::repeat_with(|| {
                        cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())])
                            .unwrap()
                    }))
                    .collect(),
            ),
            UnsetTestCrates::All => CfgOverrides::Wildcard(
                cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap(),
            ),
        }
    }
}

pub type Package = Idx<PackageData>;

pub type Target = Idx<TargetData>;

/// Information associated with a cargo crate
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageData {
    /// Version given in the `Cargo.toml`
    pub version: semver::Version,
    /// Name as given in the `Cargo.toml`
    pub name: String,
    /// Path containing the `Cargo.toml`
    pub manifest: ManifestPath,
    /// Targets provided by the crate (lib, bin, example, test, ...)
    pub targets: Vec<Target>,
    /// Is this package a member of the current workspace
    pub is_member: bool,
    /// List of packages this package depends on
    pub dependencies: Vec<PackageDependency>,
    /// Rust edition for this package
    pub edition: Edition,
    /// Features provided by the crate, mapped to the features required by that feature.
    pub features: FxHashMap<String, Vec<String>>,
    /// List of features enabled on this package
    pub active_features: Vec<String>,
    // String representation of package id
    pub id: String,
    // The contents of [package.metadata.rust-analyzer]
    pub metadata: RustAnalyzerPackageMetaData,
}

#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
pub struct RustAnalyzerPackageMetaData {
    pub rustc_private: bool,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageDependency {
    pub pkg: Package,
    pub name: String,
    pub kind: DepKind,
}

#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
pub enum DepKind {
    /// Available to the library, binary, and dev targets in the package (but not the build script).
    Normal,
    /// Available only to test and bench targets (and the library target, when built with `cfg(test)`).
    Dev,
    /// Available only to the build script target.
    Build,
}

impl DepKind {
    fn iter(list: &[cargo_metadata::DepKindInfo]) -> impl Iterator<Item = Self> + '_ {
        let mut dep_kinds = Vec::new();
        if list.is_empty() {
            dep_kinds.push(Self::Normal);
        }
        for info in list {
            let kind = match info.kind {
                cargo_metadata::DependencyKind::Normal => Self::Normal,
                cargo_metadata::DependencyKind::Development => Self::Dev,
                cargo_metadata::DependencyKind::Build => Self::Build,
                cargo_metadata::DependencyKind::Unknown => continue,
            };
            dep_kinds.push(kind);
        }
        dep_kinds.sort_unstable();
        dep_kinds.dedup();
        dep_kinds.into_iter()
    }
}

/// Information associated with a package's target
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TargetData {
    /// Package that provided this target
    pub package: Package,
    /// Name as given in the `Cargo.toml` or generated from the file name
    pub name: String,
    /// Path to the main source file of the target
    pub root: AbsPathBuf,
    /// Kind of target
    pub kind: TargetKind,
    /// Is this target a proc-macro
    pub is_proc_macro: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
    Lib,
    Example,
    Test,
    Bench,
    BuildScript,
    Other,
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                "custom-build" => TargetKind::BuildScript,
                "proc-macro" => TargetKind::Lib,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

#[derive(Deserialize, Default)]
// Deserialise helper for the cargo metadata
struct PackageMetadata {
    #[serde(rename = "rust-analyzer")]
    rust_analyzer: Option<RustAnalyzerPackageMetaData>,
}

impl CargoWorkspace {
    pub fn fetch_metadata(
        cargo_toml: &ManifestPath,
        config: &CargoConfig,
        progress: &dyn Fn(String),
    ) -> Result<cargo_metadata::Metadata> {
        let target = config
            .target
            .clone()
            .or_else(|| cargo_config_build_target(cargo_toml))
            .or_else(|| rustc_discover_host_triple(cargo_toml));

        let mut meta = MetadataCommand::new();
        meta.cargo_path(toolchain::cargo());
        meta.manifest_path(cargo_toml.to_path_buf());
        if config.all_features {
            meta.features(CargoOpt::AllFeatures);
        } else {
            if config.no_default_features {
                // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
                // https://github.com/oli-obk/cargo_metadata/issues/79
                meta.features(CargoOpt::NoDefaultFeatures);
            }
            if !config.features.is_empty() {
                meta.features(CargoOpt::SomeFeatures(config.features.clone()));
            }
        }
        meta.current_dir(cargo_toml.parent().as_os_str());

        if let Some(target) = target {
            meta.other_options(vec![String::from("--filter-platform"), target]);
        }

        // FIXME: Fetching metadata is a slow process, as it might require
        // calling crates.io. We should be reporting progress here, but it's
        // unclear whether cargo itself supports it.
        progress("metadata".to_string());

        let meta = meta.exec().with_context(|| {
            format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display(),)
        })?;

        Ok(meta)
    }

    pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace {
        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Arena::default();
        let mut targets = Arena::default();

        let ws_members = &meta.workspace_members;

        meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
        for meta_pkg in &meta.packages {
            let cargo_metadata::Package {
                id, edition, name, manifest_path, version, metadata, ..
            } = meta_pkg;
            let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
            let is_member = ws_members.contains(id);
            let edition = edition.parse::<Edition>().unwrap_or_else(|err| {
                tracing::error!("Failed to parse edition {}", err);
                Edition::CURRENT
            });

            let pkg = packages.alloc(PackageData {
                id: id.repr.clone(),
                name: name.clone(),
                version: version.clone(),
                manifest: AbsPathBuf::assert(PathBuf::from(&manifest_path)).try_into().unwrap(),
                targets: Vec::new(),
                is_member,
                edition,
                dependencies: Vec::new(),
                features: meta_pkg.features.clone().into_iter().collect(),
                active_features: Vec::new(),
                metadata: meta.rust_analyzer.unwrap_or_default(),
            });
            let pkg_data = &mut packages[pkg];
            pkg_by_id.insert(id, pkg);
            for meta_tgt in &meta_pkg.targets {
                let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
                let tgt = targets.alloc(TargetData {
                    package: pkg,
                    name: meta_tgt.name.clone(),
                    root: AbsPathBuf::assert(PathBuf::from(&meta_tgt.src_path)),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                    is_proc_macro,
                });
                pkg_data.targets.push(tgt);
            }
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for mut node in resolve.nodes {
            let source = match pkg_by_id.get(&node.id) {
                Some(&src) => src,
                // FIXME: replace this and a similar branch below with `.unwrap`, once
                // https://github.com/rust-lang/cargo/issues/7841
                // is fixed and hits stable (around 1.43-is probably?).
                None => {
                    tracing::error!("Node id do not match in cargo metadata, ignoring {}", node.id);
                    continue;
                }
            };
            node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
            for (dep_node, kind) in node
                .deps
                .iter()
                .flat_map(|dep| DepKind::iter(&dep.dep_kinds).map(move |kind| (dep, kind)))
            {
                let pkg = match pkg_by_id.get(&dep_node.pkg) {
                    Some(&pkg) => pkg,
                    None => {
                        tracing::error!(
                            "Dep node id do not match in cargo metadata, ignoring {}",
                            dep_node.pkg
                        );
                        continue;
                    }
                };
                let dep = PackageDependency { name: dep_node.name.clone(), pkg, kind };
                packages[source].dependencies.push(dep);
            }
            packages[source].active_features.extend(node.features);
        }

        let workspace_root =
            AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string()));

        CargoWorkspace { packages, targets, workspace_root }
    }

    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
        self.packages.iter().map(|(id, _pkg)| id)
    }

    pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
        self.packages()
            .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
            .next()
            .copied()
    }

    pub fn workspace_root(&self) -> &AbsPath {
        &self.workspace_root
    }

    pub fn package_flag(&self, package: &PackageData) -> String {
        if self.is_unique(&*package.name) {
            package.name.clone()
        } else {
            format!("{}:{}", package.name, package.version)
        }
    }

    fn is_unique(&self, name: &str) -> bool {
        self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
    }
}

fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
    let mut rustc = Command::new(toolchain::rustc());
    rustc.current_dir(cargo_toml.parent()).arg("-vV");
    tracing::debug!("Discovering host platform by {:?}", rustc);
    match utf8_stdout(rustc) {
        Ok(stdout) => {
            let field = "host: ";
            let target = stdout.lines().find_map(|l| l.strip_prefix(field));
            if let Some(target) = target {
                Some(target.to_string())
            } else {
                // If we fail to resolve the host platform, it's not the end of the world.
                tracing::info!("rustc -vV did not report host platform, got:\n{}", stdout);
                None
            }
        }
        Err(e) => {
            tracing::warn!("Failed to discover host platform: {}", e);
            None
        }
    }
}

fn cargo_config_build_target(cargo_toml: &ManifestPath) -> Option<String> {
    let mut cargo_config = Command::new(toolchain::cargo());
    cargo_config
        .current_dir(cargo_toml.parent())
        .args(&["-Z", "unstable-options", "config", "get", "build.target"])
        .env("RUSTC_BOOTSTRAP", "1");
    // if successful we receive `build.target = "target-triple"`
    tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
    match utf8_stdout(cargo_config) {
        Ok(stdout) => stdout
            .strip_prefix("build.target = \"")
            .and_then(|stdout| stdout.strip_suffix('"'))
            .map(ToOwned::to_owned),
        Err(_) => None,
    }
}
<commit_msg>Treat path dependencies like workspace members<commit_after>//! See [`CargoWorkspace`].

use std::convert::TryInto;
use std::iter;
use std::path::PathBuf;
use std::{ops, process::Command};

use anyhow::{Context, Result};
use base_db::Edition;
use cargo_metadata::{CargoOpt, MetadataCommand};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use serde::Deserialize;
use serde_json::from_value;

use crate::CfgOverrides;
use crate::{utf8_stdout, ManifestPath};

/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
///
/// Note that internally, rust analyzer uses a different structure:
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
///
/// We use absolute paths here, `cargo metadata` guarantees to always produce
/// abs paths.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CargoWorkspace {
    packages: Arena<PackageData>,
    targets: Arena<TargetData>,
    workspace_root: AbsPathBuf,
}

impl ops::Index<Package> for CargoWorkspace {
    type Output = PackageData;
    fn index(&self, index: Package) -> &PackageData {
        &self.packages[index]
    }
}

impl ops::Index<Target> for CargoWorkspace {
    type Output = TargetData;
    fn index(&self, index: Target) -> &TargetData {
        &self.targets[index]
    }
}

/// Describes how to set the rustc source directory.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RustcSource {
    /// Explicit path for the rustc source directory.
    Path(AbsPathBuf),
    /// Try to automatically detect where the rustc source directory is.
    Discover,
}

/// Crates to disable `#[cfg(test)]` on.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UnsetTestCrates {
    None,
    Only(Vec<String>),
    All,
}

impl Default for UnsetTestCrates {
    fn default() -> Self {
        Self::None
    }
}

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CargoConfig {
    /// Do not activate the `default` feature.
    pub no_default_features: bool,

    /// Activate all available features
    pub all_features: bool,

    /// List of features to activate.
    /// This will be ignored if `cargo_all_features` is true.
    pub features: Vec<String>,

    /// rustc target
    pub target: Option<String>,

    /// Don't load sysroot crates (`std`, `core` & friends). Might be useful
    /// when debugging isolated issues.
    pub no_sysroot: bool,

    /// rustc private crate source
    pub rustc_source: Option<RustcSource>,

    /// crates to disable `#[cfg(test)]` on
    pub unset_test_crates: UnsetTestCrates,

    pub wrap_rustc_in_build_scripts: bool,
}

impl CargoConfig {
    pub fn cfg_overrides(&self) -> CfgOverrides {
        match &self.unset_test_crates {
            UnsetTestCrates::None => CfgOverrides::Selective(iter::empty().collect()),
            UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective(
                unset_test_crates
                    .iter()
                    .map(|name| name.clone())
                    .zip(iter::repeat_with(|| {
                        cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())])
                            .unwrap()
                    }))
                    .collect(),
            ),
            UnsetTestCrates::All => CfgOverrides::Wildcard(
                cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap(),
            ),
        }
    }
}

pub type Package = Idx<PackageData>;

pub type Target = Idx<TargetData>;

/// Information associated with a cargo crate
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageData {
    /// Version given in the `Cargo.toml`
    pub version: semver::Version,
    /// Name as given in the `Cargo.toml`
    pub name: String,
    /// Path containing the `Cargo.toml`
    pub manifest: ManifestPath,
    /// Targets provided by the crate (lib, bin, example, test, ...)
    pub targets: Vec<Target>,
    /// Is this package a member of the current workspace
    pub is_member: bool,
    /// List of packages this package depends on
    pub dependencies: Vec<PackageDependency>,
    /// Rust edition for this package
    pub edition: Edition,
    /// Features provided by the crate, mapped to the features required by that feature.
    pub features: FxHashMap<String, Vec<String>>,
    /// List of features enabled on this package
    pub active_features: Vec<String>,
    // String representation of package id
    pub id: String,
    // The contents of [package.metadata.rust-analyzer]
    pub metadata: RustAnalyzerPackageMetaData,
}

#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
pub struct RustAnalyzerPackageMetaData {
    pub rustc_private: bool,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageDependency {
    pub pkg: Package,
    pub name: String,
    pub kind: DepKind,
}

#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
pub enum DepKind {
    /// Available to the library, binary, and dev targets in the package (but not the build script).
    Normal,
    /// Available only to test and bench targets (and the library target, when built with `cfg(test)`).
    Dev,
    /// Available only to the build script target.
    Build,
}

impl DepKind {
    fn iter(list: &[cargo_metadata::DepKindInfo]) -> impl Iterator<Item = Self> + '_ {
        let mut dep_kinds = Vec::new();
        if list.is_empty() {
            dep_kinds.push(Self::Normal);
        }
        for info in list {
            let kind = match info.kind {
                cargo_metadata::DependencyKind::Normal => Self::Normal,
                cargo_metadata::DependencyKind::Development => Self::Dev,
                cargo_metadata::DependencyKind::Build => Self::Build,
                cargo_metadata::DependencyKind::Unknown => continue,
            };
            dep_kinds.push(kind);
        }
        dep_kinds.sort_unstable();
        dep_kinds.dedup();
        dep_kinds.into_iter()
    }
}

/// Information associated with a package's target
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TargetData {
    /// Package that provided this target
    pub package: Package,
    /// Name as given in the `Cargo.toml` or generated from the file name
    pub name: String,
    /// Path to the main source file of the target
    pub root: AbsPathBuf,
    /// Kind of target
    pub kind: TargetKind,
    /// Is this target a proc-macro
    pub is_proc_macro: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
    Lib,
    Example,
    Test,
    Bench,
    BuildScript,
    Other,
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                "custom-build" => TargetKind::BuildScript,
                "proc-macro" => TargetKind::Lib,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

#[derive(Deserialize, Default)]
// Deserialise helper for the cargo metadata
struct PackageMetadata {
    #[serde(rename = "rust-analyzer")]
    rust_analyzer: Option<RustAnalyzerPackageMetaData>,
}

impl CargoWorkspace {
    pub fn fetch_metadata(
        cargo_toml: &ManifestPath,
        config: &CargoConfig,
        progress: &dyn Fn(String),
    ) -> Result<cargo_metadata::Metadata> {
        let target = config
            .target
            .clone()
            .or_else(|| cargo_config_build_target(cargo_toml))
            .or_else(|| rustc_discover_host_triple(cargo_toml));

        let mut meta = MetadataCommand::new();
        meta.cargo_path(toolchain::cargo());
        meta.manifest_path(cargo_toml.to_path_buf());
        if config.all_features {
            meta.features(CargoOpt::AllFeatures);
        } else {
            if config.no_default_features {
                // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
                // https://github.com/oli-obk/cargo_metadata/issues/79
                meta.features(CargoOpt::NoDefaultFeatures);
            }
            if !config.features.is_empty() {
                meta.features(CargoOpt::SomeFeatures(config.features.clone()));
            }
        }
        meta.current_dir(cargo_toml.parent().as_os_str());

        if let Some(target) = target {
            meta.other_options(vec![String::from("--filter-platform"), target]);
        }

        // FIXME: Fetching metadata is a slow process, as it might require
        // calling crates.io. We should be reporting progress here, but it's
        // unclear whether cargo itself supports it.
        progress("metadata".to_string());

        let meta = meta.exec().with_context(|| {
            format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display(),)
        })?;

        Ok(meta)
    }

    pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace {
        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Arena::default();
        let mut targets = Arena::default();

        meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
        for meta_pkg in &meta.packages {
            let cargo_metadata::Package {
                id, edition, name, manifest_path, version, metadata, ..
            } = meta_pkg;
            let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
            let edition = edition.parse::<Edition>().unwrap_or_else(|err| {
                tracing::error!("Failed to parse edition {}", err);
                Edition::CURRENT
            });
            // We treat packages without source as "local" packages. That includes all members of
            // the current workspace, as well as any path dependency outside the workspace.
            let is_member = meta_pkg.source.is_none();

            let pkg = packages.alloc(PackageData {
                id: id.repr.clone(),
                name: name.clone(),
                version: version.clone(),
                manifest: AbsPathBuf::assert(PathBuf::from(&manifest_path)).try_into().unwrap(),
                targets: Vec::new(),
                is_member,
                edition,
                dependencies: Vec::new(),
                features: meta_pkg.features.clone().into_iter().collect(),
                active_features: Vec::new(),
                metadata: meta.rust_analyzer.unwrap_or_default(),
            });
            let pkg_data = &mut packages[pkg];
            pkg_by_id.insert(id, pkg);
            for meta_tgt in &meta_pkg.targets {
                let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
                let tgt = targets.alloc(TargetData {
                    package: pkg,
                    name: meta_tgt.name.clone(),
                    root: AbsPathBuf::assert(PathBuf::from(&meta_tgt.src_path)),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                    is_proc_macro,
                });
                pkg_data.targets.push(tgt);
            }
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for mut node in resolve.nodes {
            let source = match pkg_by_id.get(&node.id) {
                Some(&src) => src,
                // FIXME: replace this and a similar branch below with `.unwrap`, once
                // https://github.com/rust-lang/cargo/issues/7841
                // is fixed and hits stable (around 1.43-is probably?).
                None => {
                    tracing::error!("Node id do not match in cargo metadata, ignoring {}", node.id);
                    continue;
                }
            };
            node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
            for (dep_node, kind) in node
                .deps
                .iter()
                .flat_map(|dep| DepKind::iter(&dep.dep_kinds).map(move |kind| (dep, kind)))
            {
                let pkg = match pkg_by_id.get(&dep_node.pkg) {
                    Some(&pkg) => pkg,
                    None => {
                        tracing::error!(
                            "Dep node id do not match in cargo metadata, ignoring {}",
                            dep_node.pkg
                        );
                        continue;
                    }
                };
                let dep = PackageDependency { name: dep_node.name.clone(), pkg, kind };
                packages[source].dependencies.push(dep);
            }
            packages[source].active_features.extend(node.features);
        }

        let workspace_root =
            AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string()));

        CargoWorkspace { packages, targets, workspace_root }
    }

    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
        self.packages.iter().map(|(id, _pkg)| id)
    }

    pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
        self.packages()
            .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
            .next()
            .copied()
    }

    pub fn workspace_root(&self) -> &AbsPath {
        &self.workspace_root
    }

    pub fn package_flag(&self, package: &PackageData) -> String {
        if self.is_unique(&*package.name) {
            package.name.clone()
        } else {
            format!("{}:{}", package.name, package.version)
        }
    }

    fn is_unique(&self, name: &str) -> bool {
        self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
    }
}

fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
    let mut rustc = Command::new(toolchain::rustc());
    rustc.current_dir(cargo_toml.parent()).arg("-vV");
    tracing::debug!("Discovering host platform by {:?}", rustc);
    match utf8_stdout(rustc) {
        Ok(stdout) => {
            let field = "host: ";
            let target = stdout.lines().find_map(|l| l.strip_prefix(field));
            if let Some(target) = target {
                Some(target.to_string())
            } else {
                // If we fail to resolve the host platform, it's not the end of the world.
                tracing::info!("rustc -vV did not report host platform, got:\n{}", stdout);
                None
            }
        }
        Err(e) => {
            tracing::warn!("Failed to discover host platform: {}", e);
            None
        }
    }
}

fn cargo_config_build_target(cargo_toml: &ManifestPath) -> Option<String> {
    let mut cargo_config = Command::new(toolchain::cargo());
    cargo_config
        .current_dir(cargo_toml.parent())
        .args(&["-Z", "unstable-options", "config", "get", "build.target"])
        .env("RUSTC_BOOTSTRAP", "1");
    // if successful we receive `build.target = "target-triple"`
    tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
    match utf8_stdout(cargo_config) {
        Ok(stdout) => stdout
            .strip_prefix("build.target = \"")
            .and_then(|stdout| stdout.strip_suffix('"'))
            .map(ToOwned::to_owned),
        Err(_) => None,
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Ident, LitStr};

use super::event_parse::{EventEnumEntry, EventEnumInput, EventKind};

pub fn expand_event_type_enum(
    input: EventEnumInput,
    ruma_common: TokenStream,
) -> syn::Result<TokenStream> {
    let mut room: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut state: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut message: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut ephemeral: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut room_account: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut global_account: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut to_device: Vec<&Vec<EventEnumEntry>> = vec![];
    for event in &input.enums {
        match event.kind {
            EventKind::GlobalAccountData => global_account.push(&event.events),
            EventKind::RoomAccountData => room_account.push(&event.events),
            EventKind::Ephemeral => ephemeral.push(&event.events),
            EventKind::MessageLike => {
                message.push(&event.events);
                room.push(&event.events);
            }
            EventKind::State => {
                state.push(&event.events);
                room.push(&event.events);
            }
            EventKind::ToDevice => to_device.push(&event.events),
            EventKind::RoomRedaction
            | EventKind::Presence
            | EventKind::Decrypted
            | EventKind::HierarchySpaceChild => {}
        }
    }
    let presence = vec![EventEnumEntry {
        attrs: vec![],
        ev_type: LitStr::new("m.presence", Span::call_site()),
    }];
    let mut all = input.enums.iter().map(|e| &e.events).collect::<Vec<_>>();
    all.push(&presence);
    let (all_event_types, all_str_ev_types) = generate_variants(&all)?;
    let all =
        generate_enum(format_ident!("EventType"), all_str_ev_types, all_event_types, &ruma_common);

    let (room_event_types, room_str_ev_types) = generate_variants(&room)?;
    let room = generate_enum(
        format_ident!("RoomEventType"),
        room_str_ev_types,
        room_event_types,
        &ruma_common,
    );

    let (state_event_types, state_str_ev_types) = generate_variants(&state)?;
    let state = generate_enum(
        format_ident!("StateEventType"),
        state_str_ev_types,
        state_event_types,
        &ruma_common,
    );

    let (message_event_types, message_str_ev_types) = generate_variants(&message)?;
    let message = generate_enum(
        format_ident!("MessageLikeEventType"),
        message_str_ev_types,
        message_event_types,
        &ruma_common,
    );

    let (ephemeral_event_types, ephemeral_str_ev_types) = generate_variants(&ephemeral)?;
    let ephemeral = generate_enum(
        format_ident!("EphemeralRoomEventType"),
        ephemeral_str_ev_types,
        ephemeral_event_types,
        &ruma_common,
    );

    let (room_account_event_types, room_account_str_ev_types) = generate_variants(&room_account)?;
    let room_account = generate_enum(
        format_ident!("RoomAccountDataEventType"),
        room_account_str_ev_types,
        room_account_event_types,
        &ruma_common,
    );

    let (global_account_event_types, global_account_str_ev_types) =
        generate_variants(&global_account)?;
    let global_account = generate_enum(
        format_ident!("GlobalAccountDataEventType"),
        global_account_str_ev_types,
        global_account_event_types,
        &ruma_common,
    );

    let (to_device_event_types, to_device_str_ev_types) = generate_variants(&to_device)?;
    let to_device = generate_enum(
        format_ident!("ToDeviceEventType"),
        to_device_str_ev_types,
        to_device_event_types,
        &ruma_common,
    );

    Ok(quote! {
        #all
        #room
        #state
        #message
        #ephemeral
        #room_account
        #global_account
        #to_device
    })
}

fn generate_enum(
    ident: Ident,
    ev_type_strings: Vec<&LitStr>,
    variants: Vec<TokenStream>,
    ruma_common: &TokenStream,
) -> TokenStream {
    let str_doc = format!("Creates a string slice from this `{}`.", ident);
    let byte_doc = format!("Creates a byte slice from this `{}`.", ident);
    let enum_doc = format!("The type of `{}` this is.", ident);
    quote! {
        #[doc = #enum_doc]
        ///
        /// This type can hold an arbitrary string. To check for events that are not available as a
        /// documented variant here, use its string representation, obtained through `.as_str()`.
        #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, #ruma_common::serde::StringEnum)]
        #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
        pub enum #ident {
            #(
                #[doc = #ev_type_strings]
                #[ruma_enum(rename = #ev_type_strings)]
                #variants,
            )*
            #[doc(hidden)]
            _Custom(crate::PrivOwnedStr),
        }

        impl #ident {
            #[doc = #str_doc]
            pub fn as_str(&self) -> &str {
                self.as_ref()
            }

            #[doc = #byte_doc]
            pub fn as_bytes(&self) -> &[u8] {
                self.as_str().as_bytes()
            }
        }
    }
}

fn generate_variants<'a>(
    input: &'a [&Vec<EventEnumEntry>],
) -> syn::Result<(Vec<TokenStream>, Vec<&'a LitStr>)> {
    let mut deduped: Vec<&EventEnumEntry> = vec![];
    for item in input.iter().copied().flatten() {
        if let Some(idx) = deduped.iter().position(|e| e.ev_type == item.ev_type) {
            // If there is a variant without config attributes use that
            if deduped[idx].attrs != item.attrs && item.attrs.is_empty() {
                deduped[idx] = item;
            }
        } else {
            deduped.push(item);
        }
    }
    let event_types = deduped.iter().map(|e| &e.ev_type).collect();

    let event_types_variants =
        deduped.iter().map(|e| Ok(e.to_variant()?.decl())).collect::<syn::Result<Vec<_>>>()?;

    Ok((event_types_variants, event_types))
}
<commit_msg>macros: Simplify *EventType generation<commit_after>use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Ident, LitStr};

use super::event_parse::{EventEnumEntry, EventEnumInput, EventKind};

pub fn expand_event_type_enum(
    input: EventEnumInput,
    ruma_common: TokenStream,
) -> syn::Result<TokenStream> {
    let mut room: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut state: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut message: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut ephemeral: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut room_account: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut global_account: Vec<&Vec<EventEnumEntry>> = vec![];
    let mut to_device: Vec<&Vec<EventEnumEntry>> = vec![];
    for event in &input.enums {
        match event.kind {
            EventKind::GlobalAccountData => global_account.push(&event.events),
            EventKind::RoomAccountData => room_account.push(&event.events),
            EventKind::Ephemeral => ephemeral.push(&event.events),
            EventKind::MessageLike => {
                message.push(&event.events);
                room.push(&event.events);
            }
            EventKind::State => {
                state.push(&event.events);
                room.push(&event.events);
            }
            EventKind::ToDevice => to_device.push(&event.events),
            EventKind::RoomRedaction
            | EventKind::Presence
            | EventKind::Decrypted
            | EventKind::HierarchySpaceChild => {}
        }
    }
    let presence = vec![EventEnumEntry {
        attrs: vec![],
        ev_type: LitStr::new("m.presence", Span::call_site()),
    }];
    let mut all = input.enums.iter().map(|e| &e.events).collect::<Vec<_>>();
    all.push(&presence);

    let mut res = TokenStream::new();

    res.extend(generate_enum("EventType", &all, &ruma_common)?);
    res.extend(generate_enum("RoomEventType", &room, &ruma_common)?);
    res.extend(generate_enum("StateEventType", &state, &ruma_common)?);
    res.extend(generate_enum("MessageLikeEventType", &message, &ruma_common)?);
    res.extend(generate_enum("EphemeralRoomEventType", &ephemeral, &ruma_common)?);
    res.extend(generate_enum("RoomAccountDataEventType", &room_account, &ruma_common)?);
    res.extend(generate_enum("GlobalAccountDataEventType", &global_account, &ruma_common)?);
    res.extend(generate_enum("ToDeviceEventType", &to_device, &ruma_common)?);

    Ok(res)
}

fn generate_enum(
    ident: &str,
    input: &[&Vec<EventEnumEntry>],
    ruma_common: &TokenStream,
) -> syn::Result<TokenStream> {
    let str_doc = format!("Creates a string slice from this `{}`.", ident);
    let byte_doc = format!("Creates a byte slice from this `{}`.", ident);
    let enum_doc = format!("The type of `{}` this is.", ident.strip_suffix("Type").unwrap());

    let ident = Ident::new(ident, Span::call_site());

    let mut deduped: Vec<&EventEnumEntry> = vec![];
    for item in input.iter().copied().flatten() {
        if let Some(idx) = deduped.iter().position(|e| e.ev_type == item.ev_type) {
            // If there is a variant without config attributes use that
            if deduped[idx].attrs != item.attrs && item.attrs.is_empty() {
                deduped[idx] = item;
            }
        } else {
            deduped.push(item);
        }
    }

    let event_types = deduped.iter().map(|e| &e.ev_type);
    let variants =
        deduped.iter().map(|e| Ok(e.to_variant()?.decl())).collect::<syn::Result<Vec<_>>>()?;

    Ok(quote! {
        #[doc = #enum_doc]
        ///
        /// This type can hold an arbitrary string. To check for events that are not available as a
        /// documented variant here, use its string representation, obtained through `.as_str()`.
        #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, #ruma_common::serde::StringEnum)]
        #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
        pub enum #ident {
            #(
                #[doc = #event_types]
                #[ruma_enum(rename = #event_types)]
                #variants,
            )*
            #[doc(hidden)]
            _Custom(crate::PrivOwnedStr),
        }

        impl #ident {
            #[doc = #str_doc]
            pub fn as_str(&self) -> &str {
                self.as_ref()
            }

            #[doc = #byte_doc]
            pub fn as_bytes(&self) -> &[u8] {
                self.as_str().as_bytes()
            }
        }
    })
}
<|endoftext|>