<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::string::String;
use std::path::Path;
use std::vec::Vec;
use std::io::BufReader;
use json;
use game::constants::{CHARACTER_JSON_PATH, CHARACTER_BUF_LENGTH, ZOMBIE_JSON_PATH};
use tiled::Map;
use tiled;
use critter::CritterData;

pub fn load_map_file(filename: &str) -> Map {
  let file = match File::open(&Path::new(&filename)) {
    Ok(f) => f,
    Err(e) => panic!("File {} not found: {}", filename, e),
  };
  let reader = BufReader::new(file);
  match tiled::parse(reader) {
    Ok(m) => m,
    Err(e) => panic!("Map parse error {:?}", e)
  }
}

pub fn get_map_tile(map: &Map, layer_index: usize, x: usize, y: usize) -> u32 {
  let layer = match map.layers.get(layer_index) {
    None => panic!("Layer_index value out of index {:?}", map.layers),
    Some(l) => l
  };
  let y_index = match layer.tiles.iter().rev().nth(y) {
    None => panic!("X value out of index {:?}", map.layers[0]),
    Some(x) => x
  };
  match y_index.get(x) {
    None => panic!("Y value out of index {:?}", layer.tiles[y]),
    Some(val) => *val
  }
}

fn read_sprite_file(filename: &str) -> String {
  let path = Path::new(&filename);
  let mut file = match File::open(&path) {
    Ok(f) => f,
    Err(e) => panic!("File {} not found: {}", filename, e),
  };
  let mut buf = String::new();
  match file.read_to_string(&mut buf) {
    Ok(_) => buf,
    Err(_) => panic!("Couldn't read file {}", filename),
  }
}

pub fn load_character() -> Vec<CritterData> {
  let mut sprites = Vec::with_capacity(CHARACTER_BUF_LENGTH + 64);
  let character_json = read_sprite_file(CHARACTER_JSON_PATH);
  let character = match json::parse(&character_json) {
    Ok(res) => res,
    Err(e) => panic!("Character JSON parse error {:?}", e),
  };
  for x in 0..16 {
    for y in 0..14 {
      let key = &format!("run_{}_{}", x, y);
      sprites.push(CritterData::new([
        character["frames"][key]["frame"]["x"].as_f32().unwrap(),
        character["frames"][key]["frame"]["y"].as_f32().unwrap(),
        character["frames"][key]["frame"]["w"].as_f32().unwrap(),
        character["frames"][key]["frame"]["h"].as_f32().unwrap(),
      ]));
    }
  }

  for x in 0..15 {
    for y in 0..4 {
      let key = &format!("fire_{}_{}", x, y);
      sprites.push(CritterData::new([
        character["frames"][key]["frame"]["x"].as_f32().unwrap(),
        character["frames"][key]["frame"]["y"].as_f32().unwrap(),
        character["frames"][key]["frame"]["w"].as_f32().unwrap(),
        character["frames"][key]["frame"]["h"].as_f32().unwrap(),
      ]));
    }
  }

  sprites
}

pub fn load_zombie() -> Vec<CritterData> {
  let mut sprites = Vec::with_capacity(128);
  let character_json = read_sprite_file(ZOMBIE_JSON_PATH);
  let zombie = match json::parse(&character_json) {
    Ok(res) => res,
    Err(e) => panic!("Character JSON parse error {:?}", e),
  };

  for x in 0..7 {
    for y in 0..7 {
      let key = &format!("zombie_{}_{}", x, y);
      sprites.push(CritterData::new([
        zombie["frames"][key]["frame"]["x"].as_f32().unwrap(),
        zombie["frames"][key]["frame"]["y"].as_f32().unwrap(),
        zombie["frames"][key]["frame"]["w"].as_f32().unwrap(),
        zombie["frames"][key]["frame"]["h"].as_f32().unwrap()
      ]));
    }
  }

  sprites
}
<commit_msg>Log error reason on failed file open<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::string::String;
use std::path::Path;
use std::vec::Vec;
use std::io::BufReader;
use json;
use game::constants::{CHARACTER_JSON_PATH, CHARACTER_BUF_LENGTH, ZOMBIE_JSON_PATH};
use tiled::Map;
use tiled;
use critter::CritterData;

pub fn load_map_file(filename: &str) -> Map {
  let file = match File::open(&Path::new(&filename)) {
    Ok(f) => f,
    Err(e) => panic!("File {} not found: {}", filename, e),
  };
  let reader = BufReader::new(file);
  match tiled::parse(reader) {
    Ok(m) => m,
    Err(e) => panic!("Map parse error {:?}", e)
  }
}

pub fn get_map_tile(map: &Map, layer_index: usize, x: usize, y: usize) -> u32 {
  let layer = match map.layers.get(layer_index) {
    None => panic!("Layer_index value out of index {:?}", map.layers),
    Some(l) => l
  };
  let y_index = match layer.tiles.iter().rev().nth(y) {
    None => panic!("X value out of index {:?}", map.layers[0]),
    Some(x) => x
  };
  match y_index.get(x) {
    None => panic!("Y value out of index {:?}", layer.tiles[y]),
    Some(val) => *val
  }
}

fn read_sprite_file(filename: &str) -> String {
  let path = Path::new(&filename);
  let mut file = match File::open(&path) {
    Ok(f) => f,
    Err(e) => panic!("File {} not found: {}", filename, e),
  };
  let mut buf = String::new();
  match file.read_to_string(&mut buf) {
    Ok(_) => buf,
    Err(e) => panic!("read file {} error {}", filename, e),
  }
}

pub fn load_character() -> Vec<CritterData> {
  let mut sprites = Vec::with_capacity(CHARACTER_BUF_LENGTH + 64);
  let character_json = read_sprite_file(CHARACTER_JSON_PATH);
  let character = match json::parse(&character_json) {
    Ok(res) => res,
    Err(e) => panic!("Character JSON parse error {:?}", e),
  };
  for x in 0..16 {
    for y in 0..14 {
      let key = &format!("run_{}_{}", x, y);
      sprites.push(CritterData::new([
        character["frames"][key]["frame"]["x"].as_f32().unwrap(),
        character["frames"][key]["frame"]["y"].as_f32().unwrap(),
        character["frames"][key]["frame"]["w"].as_f32().unwrap(),
        character["frames"][key]["frame"]["h"].as_f32().unwrap(),
      ]));
    }
  }

  for x in 0..15 {
    for y in 0..4 {
      let key = &format!("fire_{}_{}", x, y);
      sprites.push(CritterData::new([
        character["frames"][key]["frame"]["x"].as_f32().unwrap(),
        character["frames"][key]["frame"]["y"].as_f32().unwrap(),
        character["frames"][key]["frame"]["w"].as_f32().unwrap(),
        character["frames"][key]["frame"]["h"].as_f32().unwrap(),
      ]));
    }
  }

  sprites
}

pub fn load_zombie() -> Vec<CritterData> {
  let mut sprites = Vec::with_capacity(128);
  let character_json = read_sprite_file(ZOMBIE_JSON_PATH);
  let zombie = match json::parse(&character_json) {
    Ok(res) => res,
    Err(e) => panic!("Character JSON parse error {:?}", e),
  };

  for x in 0..7 {
    for y in 0..7 {
      let key = &format!("zombie_{}_{}", x, y);
      sprites.push(CritterData::new([
        zombie["frames"][key]["frame"]["x"].as_f32().unwrap(),
        zombie["frames"][key]["frame"]["y"].as_f32().unwrap(),
        zombie["frames"][key]["frame"]["w"].as_f32().unwrap(),
        zombie["frames"][key]["frame"]["h"].as_f32().unwrap()
      ]));
    }
  }

  sprites
}
<|endoftext|>


<commit_before>use std::error::Error as StdError;
use std::fmt;
use std::str::Utf8Error;
use std::io;
use std::string::FromUtf8Error;

use serde::de;

use parse::Position;

/// Deserialization result.
pub type Result<T> = ::std::result::Result<T, Error>;

#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    IOError(String),
    Message(String),
    Parser(ParseError, Position),
}

#[derive(Clone, Debug, PartialEq)]
pub enum ParseError {
    Eof,
    ExpectedArray,
    ExpectedArrayEnd,
    ExpectedBoolean,
    ExpectedComma,
    ExpectedEnum,
    ExpectedChar,
    ExpectedFloat,
    ExpectedInteger,
    ExpectedOption,
    ExpectedOptionEnd,
    ExpectedMap,
    ExpectedMapColon,
    ExpectedMapEnd,
    ExpectedStruct,
    ExpectedStructEnd,
    ExpectedUnit,
    ExpectedStructName,
    ExpectedString,
    ExpectedIdentifier,

    InvalidEscape,

    Utf8Error(Utf8Error),
    TrailingCharacters,

    #[doc(hidden)]
    __NonExhaustive,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::IOError(ref s) => write!(f, "{}", s),
            Error::Message(ref s) => write!(f, "{}", s),
            Error::Parser(_, pos) => write!(f, "{}: {}", pos, self.description()),
        }
    }
}

impl de::Error for Error {
    fn custom<T: fmt::Display>(msg: T) -> Self {
        Error::Message(msg.to_string())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IOError(ref s) => s,
            Error::Message(ref e) => e,
            Error::Parser(ref kind, _) => match *kind {
                ParseError::Eof => "Unexpected end of file",
                ParseError::ExpectedArray => "Expected array",
                ParseError::ExpectedArrayEnd => "Expected end of array",
                ParseError::ExpectedBoolean => "Expected boolean",
                ParseError::ExpectedComma => "Expected comma",
                ParseError::ExpectedEnum => "Expected enum",
                ParseError::ExpectedChar => "Expected char",
                ParseError::ExpectedFloat => "Expected float",
                ParseError::ExpectedInteger => "Expected integer",
                ParseError::ExpectedOption => "Expected option",
                ParseError::ExpectedOptionEnd => "Expected end of option",
                ParseError::ExpectedMap => "Expected map",
                ParseError::ExpectedMapColon => "Expected colon",
                ParseError::ExpectedMapEnd => "Expected end of map",
                ParseError::ExpectedStruct => "Expected struct",
                ParseError::ExpectedStructEnd => "Expected end of struct",
                ParseError::ExpectedUnit => "Expected unit",
                ParseError::ExpectedStructName => "Expected struct name",
                ParseError::ExpectedString => "Expected string",
                ParseError::ExpectedIdentifier => "Expected identifier",

                ParseError::InvalidEscape => "Invalid escape sequence",

                ParseError::Utf8Error(ref e) => e.description(),
                ParseError::TrailingCharacters => "Non-whitespace trailing characters",

                _ => unimplemented!(),
            }
        }
    }
}

impl From<Utf8Error> for ParseError {
    fn from(e: Utf8Error) -> Self {
        ParseError::Utf8Error(e)
    }
}

impl From<FromUtf8Error> for ParseError {
    fn from(e: FromUtf8Error) -> Self {
        ParseError::Utf8Error(e.utf8_error())
    }
}

impl From<Utf8Error> for Error {
    fn from(e: Utf8Error) -> Self {
        Error::Parser(ParseError::Utf8Error(e), Position { line : 0, col : 0})
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::IOError(e.description().to_string())
    }
}<commit_msg>Updated Error variant based on feedback<commit_after>use std::error::Error as StdError;
use std::fmt;
use std::str::Utf8Error;
use std::io;
use std::string::FromUtf8Error;

use serde::de;

use parse::Position;

/// Deserialization result.
pub type Result<T> = ::std::result::Result<T, Error>;

#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    IoError(String),
    Message(String),
    Parser(ParseError, Position),
}

#[derive(Clone, Debug, PartialEq)]
pub enum ParseError {
    Eof,
    ExpectedArray,
    ExpectedArrayEnd,
    ExpectedBoolean,
    ExpectedComma,
    ExpectedEnum,
    ExpectedChar,
    ExpectedFloat,
    ExpectedInteger,
    ExpectedOption,
    ExpectedOptionEnd,
    ExpectedMap,
    ExpectedMapColon,
    ExpectedMapEnd,
    ExpectedStruct,
    ExpectedStructEnd,
    ExpectedUnit,
    ExpectedStructName,
    ExpectedString,
    ExpectedIdentifier,

    InvalidEscape,

    Utf8Error(Utf8Error),
    TrailingCharacters,

    #[doc(hidden)]
    __NonExhaustive,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::IoError(ref s) => write!(f, "{}", s),
            Error::Message(ref s) => write!(f, "{}", s),
            Error::Parser(_, pos) => write!(f, "{}: {}", pos, self.description()),
        }
    }
}

impl de::Error for Error {
    fn custom<T: fmt::Display>(msg: T) -> Self {
        Error::Message(msg.to_string())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IoError(ref s) => s,
            Error::Message(ref e) => e,
            Error::Parser(ref kind, _) => match *kind {
                ParseError::Eof => "Unexpected end of file",
                ParseError::ExpectedArray => "Expected array",
                ParseError::ExpectedArrayEnd => "Expected end of array",
                ParseError::ExpectedBoolean => "Expected boolean",
                ParseError::ExpectedComma => "Expected comma",
                ParseError::ExpectedEnum => "Expected enum",
                ParseError::ExpectedChar => "Expected char",
                ParseError::ExpectedFloat => "Expected float",
                ParseError::ExpectedInteger => "Expected integer",
                ParseError::ExpectedOption => "Expected option",
                ParseError::ExpectedOptionEnd => "Expected end of option",
                ParseError::ExpectedMap => "Expected map",
                ParseError::ExpectedMapColon => "Expected colon",
                ParseError::ExpectedMapEnd => "Expected end of map",
                ParseError::ExpectedStruct => "Expected struct",
                ParseError::ExpectedStructEnd => "Expected end of struct",
                ParseError::ExpectedUnit => "Expected unit",
                ParseError::ExpectedStructName => "Expected struct name",
                ParseError::ExpectedString => "Expected string",
                ParseError::ExpectedIdentifier => "Expected identifier",

                ParseError::InvalidEscape => "Invalid escape sequence",

                ParseError::Utf8Error(ref e) => e.description(),
                ParseError::TrailingCharacters => "Non-whitespace trailing characters",

                _ => unimplemented!(),
            }
        }
    }
}

impl From<Utf8Error> for ParseError {
    fn from(e: Utf8Error) -> Self {
        ParseError::Utf8Error(e)
    }
}

impl From<FromUtf8Error> for ParseError {
    fn from(e: FromUtf8Error) -> Self {
        ParseError::Utf8Error(e.utf8_error())
    }
}

impl From<Utf8Error> for Error {
    fn from(e: Utf8Error) -> Self {
        Error::Parser(ParseError::Utf8Error(e), Position { line : 0, col : 0})
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::IoError(e.description().to_string())
    }
}<|endoftext|>


<commit_before>//! Derive macros for easily translating between objects in Qualia and Rust structs.

extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned, ToTokens};
use syn::{parse_macro_input, DeriveInput};

macro_rules! error_stream {
    ( $span:expr, $message:expr ) => {
        quote_spanned!($span=> compile_error!($message);).into()
    }
}

macro_rules! try_or_context {
    ( $expr:expr, $message:expr$(,)? ) => {
        match $expr {
            Ok(x) => x,
            Err(span) => return error_stream!(span, $message),
        }
    };
}

macro_rules! try_or_error {
    ( $expr:expr$(,)? ) => {
        match $expr {
            Ok(x) => x,
            Err(e) => return e.to_compile_error().into(),
        }
    };
}

fn parse_field_name(field: &syn::Field) -> syn::Result<String> {
    if let Some(attr) = field
        .attrs
        .iter()
        .find(|attr| attr.style == syn::AttrStyle::Outer && attr.path.is_ident("object_field"))
    {
        attr.parse_args::<syn::LitStr>().map(|lit| lit.value())
    } else {
        Ok(field.ident.clone().unwrap().to_string())
    }
}

#[derive(Debug)]
struct ParsedField {
    ident: proc_macro2::Ident,
    name: String,
    accessor: TokenStream2,
}

fn base_accessor(field_name: &String) -> TokenStream2 {
    quote!(
        get(#field_name)
        .ok_or(qualia::ConversionError::FieldMissing(#field_name.to_string()))?
    )
}

fn number_accessor(field_name: &String) -> TokenStream2 {
    let base_accessor = base_accessor(field_name);

    quote!(
        #base_accessor
        .as_number()
        .ok_or(
            qualia::ConversionError::FieldWrongType(
                #field_name.to_string(),
                "number".to_string(),
            ),
        )?
    )
}

fn string_accessor(field_name: &String) -> TokenStream2 {
    let base_accessor = base_accessor(field_name);

    quote!(
        #base_accessor
        .as_str()
        .ok_or(
            qualia::ConversionError::FieldWrongType(
                #field_name.to_string(),
                "string".to_string(),
            ),
        )?
        .clone()
    )
}

fn parse_fields(
    named_fields: &syn::FieldsNamed,
) -> syn::Result<(Vec<ParsedField>, Option<syn::Ident>)> {
    let mut rest_field_ident = None;

    Ok((
        named_fields
            .named
            .iter()
            .map(|field| {
                if let Some(_) = field.attrs.iter().find(|attr| {
                    attr.style == syn::AttrStyle::Outer && attr.path.is_ident("object_rest_fields")
                }) {
                    rest_field_ident = Some(field.ident.clone().unwrap());
                    return Ok(None);
                }

                let field_type = match &field.ty {
                    syn::Type::Path(p) => p,
                    _ => {
                        return Err(syn::Error::new_spanned(
                            &field.ty,
                            "fields in ObjectShape must be i64 or String",
                        ))
                    }
                };

                let field_name = parse_field_name(&field)?;

                let field_type_accessor = if field_type.path.is_ident("i64") {
                    Ok(number_accessor(&field_name))
                } else if field_type.path.is_ident("String") {
                    Ok(string_accessor(&field_name))
                } else {
                    Err(syn::Error::new_spanned(
                        &field_type.path,
                        "fields in ObjectShape must be i64 or String",
                    ))
                }?;

                Ok(Some(ParsedField {
                    ident: field.ident.clone().unwrap(),
                    name: field_name,
                    accessor: field_type_accessor,
                }))
            })
            .collect::<syn::Result<Vec<_>>>()?
            .into_iter()
            .filter_map(|x| x)
            .collect(),
        rest_field_ident,
    ))
}

#[derive(Debug)]
enum FixedFieldValue {
    Number(syn::LitInt),
    String(syn::LitStr),
}

impl syn::parse::Parse for FixedFieldValue {
    fn parse(input: &syn::parse::ParseBuffer<'_>) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(syn::LitInt) {
            input.parse().map(FixedFieldValue::Number)
        } else if lookahead.peek(syn::LitStr) {
            input.parse().map(FixedFieldValue::String)
        } else {
            Err(lookahead.error())
        }
    }
}

impl quote::ToTokens for FixedFieldValue {
    fn to_tokens(&self, tokens: &mut TokenStream2) {
        match self {
            FixedFieldValue::Number(n) => n.to_tokens(tokens),
            FixedFieldValue::String(s) => s.to_tokens(tokens),
        }
    }
}

#[derive(Debug)]
struct FixedField {
    name: syn::LitStr,
    _arrow_token: syn::token::FatArrow,
    value: FixedFieldValue,
}

impl FixedField {
    fn accessor(&self) -> TokenStream2 {
        match self.value {
            FixedFieldValue::Number(_) => number_accessor(&self.name.value()),
            FixedFieldValue::String(_) => string_accessor(&self.name.value()),
        }
    }
}

impl syn::parse::Parse for FixedField {
    fn parse(input: &syn::parse::ParseBuffer<'_>) -> syn::Result<Self> {
        Ok(FixedField {
            name: input.parse()?,
            _arrow_token: input.parse()?,
            value: input.parse()?,
        })
    }
}

#[derive(Debug)]
struct FixedFields {
    fields: syn::punctuated::Punctuated<FixedField, syn::Token![,]>,
}

impl syn::parse::Parse for FixedFields {
    fn parse(input: &syn::parse::ParseBuffer<'_>) -> syn::Result<Self> {
        Ok(FixedFields {
            fields: input.parse_terminated(FixedField::parse)?,
        })
    }
}

fn parse_fixed_fields(attrs: &Vec<syn::Attribute>) -> syn::Result<Vec<FixedField>> {
    let attr = match attrs.iter().find(|attr| {
        attr.style == syn::AttrStyle::Outer && attr.path.is_ident("object_fixed_fields")
    }) {
        Some(a) => a,
        None => return Ok(Vec::new()),
    };

    let fixed_fields: FixedFields = attr.parse_args()?;

    Ok(fixed_fields
        .fields
        .into_pairs()
        .map(|p| match p {
            syn::punctuated::Pair::Punctuated(f, _) => f,
            syn::punctuated::Pair::End(f) => f,
        })
        .collect())
}

/// Automatically translate between properties of Qualia objects and fields of structs.
///
/// A basic example:
///
/// ```
/// # use qualia::{object, Object};
/// # use qualia_derive::ObjectShape;
/// # use std::convert::{Infallible, TryFrom};
/// #[derive(Debug, ObjectShape, PartialEq)]
/// struct CustomShape {
///     name: String,
///     width: i64,
/// }
///
/// let shape: Object = CustomShape {
///     name: "letter".to_string(),
///     width: 8,
/// }
/// .into();
///
/// assert_eq!(
///     shape,
///     object!("name" => "letter", "width" => 8),
/// );
///
/// let obj: Object = object!(
///     "name" => "letter",
///     "width" => 8,
/// );
///
/// assert_eq!(
///     CustomShape::try_from(obj),
///     Ok(CustomShape {
///         name: "letter".to_string(),
///         width: 8,
///     })
/// );
/// ```
///
/// By default, properties get the same name as the field in the struct. This can be changed with
/// the `object_field` attribute:
///
/// ```
/// # use qualia::{object, Object};
/// # use qualia_derive::ObjectShape;
/// # use std::convert::{Infallible, TryFrom};
/// #[derive(Debug, ObjectShape, PartialEq)]
/// struct CustomShape {
///     #[object_field("my-name")]
///     name: String,
///     width: i64,
/// }
///
/// let shape: Object = CustomShape {
///     name: "letter".to_string(),
///     width: 8,
/// }
/// .into();
///
/// assert_eq!(
///     shape,
///     object!("my-name" => "letter", "width" => 8),
/// );
///
/// let obj: Object = object!(
///     "my-name" => "letter",
///     "width" => 8,
/// );
///
/// assert_eq!(
///     CustomShape::try_from(obj),
///     Ok(CustomShape {
///         name: "letter".to_string(),
///         width: 8,
///     })
/// );
/// ```
#[proc_macro_derive(
    ObjectShape,
    attributes(object_field, object_fixed_fields, object_rest_fields)
)]
pub fn derive_object_shape(input: TokenStream) -> TokenStream {
    let parsed_struct = parse_macro_input!(input as DeriveInput);
    let orig_type_name = parsed_struct.ident;

    let fixed_fields = try_or_error!(parse_fixed_fields(&parsed_struct.attrs));

    let mut fixed_field_names = Vec::new();
    let mut fixed_field_values = Vec::new();
    let mut fixed_field_accessors = Vec::new();

    for f in fixed_fields.into_iter() {
        fixed_field_names.push(f.name.clone());
        fixed_field_values.push(f.value.to_token_stream());
        fixed_field_accessors.push(f.accessor());
    }

    let struct_data = try_or_context!(
        match parsed_struct.data {
            syn::Data::Struct(s) => Ok(s),
            syn::Data::Enum(e) => Err(e.enum_token.span),
            syn::Data::Union(u) => Err(u.union_token.span),
        },
        "can only derive ObjectShape on a struct",
    );

    let named_fields = try_or_context!(
        match struct_data.fields {
            syn::Fields::Named(ref n) => Ok(n),
            syn::Fields::Unnamed(ref u) => Err(u.paren_token.span),
            syn::Fields::Unit => Err(struct_data.semi_token.unwrap().span),
        },
        "Can only derive ObjectType from a struct with named fields",
    );

    let (parsed_fields, rest_field_ident) = try_or_error!(parse_fields(&named_fields));

    let mut field_names = Vec::new();
    let mut field_idents = Vec::new();
    let mut field_accessors = Vec::new();

    for f in parsed_fields.into_iter() {
        field_names.push(f.name);
        field_idents.push(f.ident);
        field_accessors.push(f.accessor);
    }

    let rest_field_try_from = if let Some(ref rest_field_ident) = rest_field_ident {
        quote!(
            ,#rest_field_ident: object.iter().filter_map(|(k, v)| {
                if (#(k == #field_names)||*) {
                    None
                } else {
                    Some((k.clone(), v.clone()))
                }
            }).collect()
        )
    } else {
        quote!()
    };

    let rest_field_into = if let Some(ref rest_field_ident) = rest_field_ident {
        quote!(
            result.extend(self.#rest_field_ident.iter().map(|(k, v)| (k.clone(), v.clone())));
        )
    } else {
        quote!()
    };

    quote!(
        impl std::convert::TryFrom<qualia::Object> for #orig_type_name {
            type Error = qualia::ConversionError;

            fn try_from(object: qualia::Object) -> std::result::Result<#orig_type_name, qualia::ConversionError> {
                #(
                    {
                        let value = object.#fixed_field_accessors;

                        if value != #fixed_field_values {
                            return Err(
                                qualia::ConversionError::FixedFieldWrongValue(
                                    #fixed_field_names.to_string(),
                                    #fixed_field_values.into(),
                                    value.into(),
                                )
                            );
                        }
                    }
                )*

                Ok(#orig_type_name {
                    #(#field_idents: object.#field_accessors),*
                    #rest_field_try_from
                })
            }
        }

        impl std::convert::Into<qualia::Object> for #orig_type_name {
            fn into(self) -> qualia::Object {
                use qualia::{object, Object};

                let mut result = object!(
                    #(#fixed_field_names => #fixed_field_values),*
                    #(#field_names => self.#field_idents),*
                );
                #rest_field_into

                result
            }
        }

        impl qualia::ObjectShape for #orig_type_name {}
    ).into()
}
<commit_msg>Add more docs to qualia_derive, remove unnecessary copies for rest fields<commit_after>//! Derive macros for easily translating between objects in Qualia and Rust structs.

extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned, ToTokens};
use syn::{parse_macro_input, DeriveInput};

macro_rules! error_stream {
    ( $span:expr, $message:expr ) => {
        quote_spanned!($span=> compile_error!($message);).into()
    }
}

macro_rules! try_or_context {
    ( $expr:expr, $message:expr$(,)? ) => {
        match $expr {
            Ok(x) => x,
            Err(span) => return error_stream!(span, $message),
        }
    };
}

macro_rules! try_or_error {
    ( $expr:expr$(,)? ) => {
        match $expr {
            Ok(x) => x,
            Err(e) => return e.to_compile_error().into(),
        }
    };
}

fn parse_field_name(field: &syn::Field) -> syn::Result<String> {
    if let Some(attr) = field
        .attrs
        .iter()
        .find(|attr| attr.style == syn::AttrStyle::Outer && attr.path.is_ident("object_field"))
    {
        attr.parse_args::<syn::LitStr>().map(|lit| lit.value())
    } else {
        Ok(field.ident.clone().unwrap().to_string())
    }
}

#[derive(Debug)]
struct ParsedField {
    ident: proc_macro2::Ident,
    name: String,
    accessor: TokenStream2,
}

fn base_accessor(field_name: &String) -> TokenStream2 {
    quote!(
        get(#field_name)
        .ok_or(qualia::ConversionError::FieldMissing(#field_name.to_string()))?
    )
}

fn number_accessor(field_name: &String) -> TokenStream2 {
    let base_accessor = base_accessor(field_name);

    quote!(
        #base_accessor
        .as_number()
        .ok_or(
            qualia::ConversionError::FieldWrongType(
                #field_name.to_string(),
                "number".to_string(),
            ),
        )?
    )
}

fn string_accessor(field_name: &String) -> TokenStream2 {
    let base_accessor = base_accessor(field_name);

    quote!(
        #base_accessor
        .as_str()
        .ok_or(
            qualia::ConversionError::FieldWrongType(
                #field_name.to_string(),
                "string".to_string(),
            ),
        )?
        .clone()
    )
}

fn parse_fields(
    named_fields: &syn::FieldsNamed,
) -> syn::Result<(Vec<ParsedField>, Option<syn::Ident>)> {
    let mut rest_field_ident = None;

    Ok((
        named_fields
            .named
            .iter()
            .map(|field| {
                if let Some(_) = field.attrs.iter().find(|attr| {
                    attr.style == syn::AttrStyle::Outer && attr.path.is_ident("object_rest_fields")
                }) {
                    rest_field_ident = Some(field.ident.clone().unwrap());
                    return Ok(None);
                }

                let field_type = match &field.ty {
                    syn::Type::Path(p) => p,
                    _ => {
                        return Err(syn::Error::new_spanned(
                            &field.ty,
                            "fields in ObjectShape must be i64 or String",
                        ))
                    }
                };

                let field_name = parse_field_name(&field)?;

                let field_type_accessor = if field_type.path.is_ident("i64") {
                    Ok(number_accessor(&field_name))
                } else if field_type.path.is_ident("String") {
                    Ok(string_accessor(&field_name))
                } else {
                    Err(syn::Error::new_spanned(
                        &field_type.path,
                        "fields in ObjectShape must be i64 or String",
                    ))
                }?;

                Ok(Some(ParsedField {
                    ident: field.ident.clone().unwrap(),
                    name: field_name,
                    accessor: field_type_accessor,
                }))
            })
            .collect::<syn::Result<Vec<_>>>()?
            .into_iter()
            .filter_map(|x| x)
            .collect(),
        rest_field_ident,
    ))
}

#[derive(Debug)]
enum FixedFieldValue {
    Number(syn::LitInt),
    String(syn::LitStr),
}

impl syn::parse::Parse for FixedFieldValue {
    fn parse(input: &syn::parse::ParseBuffer<'_>) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(syn::LitInt) {
            input.parse().map(FixedFieldValue::Number)
        } else if lookahead.peek(syn::LitStr) {
            input.parse().map(FixedFieldValue::String)
        } else {
            Err(lookahead.error())
        }
    }
}

impl quote::ToTokens for FixedFieldValue {
    fn to_tokens(&self, tokens: &mut TokenStream2) {
        match self {
            FixedFieldValue::Number(n) => n.to_tokens(tokens),
            FixedFieldValue::String(s) => s.to_tokens(tokens),
        }
    }
}

#[derive(Debug)]
struct FixedField {
    name: syn::LitStr,
    _arrow_token: syn::token::FatArrow,
    value: FixedFieldValue,
}

impl FixedField {
    fn accessor(&self) -> TokenStream2 {
        match self.value {
            FixedFieldValue::Number(_) => number_accessor(&self.name.value()),
            FixedFieldValue::String(_) => string_accessor(&self.name.value()),
        }
    }
}

impl syn::parse::Parse for FixedField {
    fn parse(input: &syn::parse::ParseBuffer<'_>) -> syn::Result<Self> {
        Ok(FixedField {
            name: input.parse()?,
            _arrow_token: input.parse()?,
            value: input.parse()?,
        })
    }
}

#[derive(Debug)]
struct FixedFields {
    fields: syn::punctuated::Punctuated<FixedField, syn::Token![,]>,
}

impl syn::parse::Parse for FixedFields {
    fn parse(input: &syn::parse::ParseBuffer<'_>) -> syn::Result<Self> {
        Ok(FixedFields {
            fields: input.parse_terminated(FixedField::parse)?,
        })
    }
}

fn parse_fixed_fields(attrs: &Vec<syn::Attribute>) -> syn::Result<Vec<FixedField>> {
    let attr = match attrs.iter().find(|attr| {
        attr.style == syn::AttrStyle::Outer && attr.path.is_ident("object_fixed_fields")
    }) {
        Some(a) => a,
        None => return Ok(Vec::new()),
    };

    let fixed_fields: FixedFields = attr.parse_args()?;

    Ok(fixed_fields
        .fields
        .into_pairs()
        .map(|p| match p {
            syn::punctuated::Pair::Punctuated(f, _) => f,
            syn::punctuated::Pair::End(f) => f,
        })
        .collect())
}

/// Automatically translate between properties of Qualia objects and fields of structs.
///
/// # Basic example
///
/// ```
/// # use qualia::{object, Object};
/// # use qualia_derive::ObjectShape;
/// # use std::convert::{Infallible, TryFrom};
/// #[derive(Debug, ObjectShape, PartialEq)]
/// struct CustomShape {
///     name: String,
///     width: i64,
/// }
///
/// let shape: Object = CustomShape {
///     name: "letter".to_string(),
///     width: 8,
/// }
/// .into();
///
/// assert_eq!(
///     shape,
///     object!("name" => "letter", "width" => 8),
/// );
///
/// let obj: Object = object!(
///     "name" => "letter",
///     "width" => 8,
/// );
///
/// assert_eq!(
///     CustomShape::try_from(obj),
///     Ok(CustomShape {
///         name: "letter".to_string(),
///         width: 8,
///     })
/// );
/// ```
///
/// # Renaming properties
///
/// By default, properties get the same name as the field in the struct. This can be changed with
/// the `object_field` attribute:
///
/// ```
/// # use qualia::{object, Object};
/// # use qualia_derive::ObjectShape;
/// # use std::convert::{Infallible, TryFrom};
/// #[derive(Debug, ObjectShape, PartialEq)]
/// struct CustomShape {
///     #[object_field("my-name")]
///     name: String,
///     width: i64,
/// }
///
/// let shape: Object = CustomShape {
///     name: "letter".to_string(),
///     width: 8,
/// }
/// .into();
///
/// assert_eq!(
///     shape,
///     object!("my-name" => "letter", "width" => 8),
/// );
/// ```
///
/// # Adding fixed properties
///
/// Additional fields with fixed values can be added with the `object_fixed_fields` attribute on
/// the struct:
///
/// ```
/// # use qualia::{object, Object};
/// # use qualia_derive::ObjectShape;
/// # use std::convert::{Infallible, TryFrom};
/// #[derive(Debug, ObjectShape, PartialEq)]
/// #[object_fixed_fields("kind" => "custom")]
/// struct CustomShape {
///     width: i64,
///     height: i64,
/// }
///
/// let shape: Object = CustomShape {
///     width: 8,
///     height: 11,
/// }
/// .into();
///
/// assert_eq!(
///     shape,
///     object!("kind" => "custom", "width" => 8, "height" => 11),
/// );
/// ```
#[proc_macro_derive(
    ObjectShape,
    attributes(object_field, object_fixed_fields, object_rest_fields)
)]
pub fn derive_object_shape(input: TokenStream) -> TokenStream {
    let parsed_struct = parse_macro_input!(input as DeriveInput);
    let orig_type_name = parsed_struct.ident;

    let fixed_fields = try_or_error!(parse_fixed_fields(&parsed_struct.attrs));

    let mut fixed_field_names = Vec::new();
    let mut fixed_field_values = Vec::new();
    let mut fixed_field_accessors = Vec::new();

    for f in fixed_fields.into_iter() {
        fixed_field_names.push(f.name.clone());
        fixed_field_values.push(f.value.to_token_stream());
        fixed_field_accessors.push(f.accessor());
    }

    let struct_data = try_or_context!(
        match parsed_struct.data {
            syn::Data::Struct(s) => Ok(s),
            syn::Data::Enum(e) => Err(e.enum_token.span),
            syn::Data::Union(u) => Err(u.union_token.span),
        },
        "can only derive ObjectShape on a struct",
    );

    let named_fields = try_or_context!(
        match struct_data.fields {
            syn::Fields::Named(ref n) => Ok(n),
            syn::Fields::Unnamed(ref u) => Err(u.paren_token.span),
            syn::Fields::Unit => Err(struct_data.semi_token.unwrap().span),
        },
        "Can only derive ObjectType from a struct with named fields",
    );

    let (parsed_fields, rest_field_ident) = try_or_error!(parse_fields(&named_fields));

    let mut field_names = Vec::new();
    let mut field_idents = Vec::new();
    let mut field_accessors = Vec::new();

    for f in parsed_fields.into_iter() {
        field_names.push(f.name);
        field_idents.push(f.ident);
        field_accessors.push(f.accessor);
    }

    let rest_field_try_from = if let Some(ref rest_field_ident) = rest_field_ident {
        quote!(
            ,#rest_field_ident: object.into_iter().filter_map(|(k, v)| {
                if (#(k == #field_names)||*) {
                    None
                } else {
                    Some((k, v))
                }
            }).collect()
        )
    } else {
        quote!()
    };

    let rest_field_into = if let Some(ref rest_field_ident) = rest_field_ident {
        quote!(
            result.extend(self.#rest_field_ident.into_iter());
        )
    } else {
        quote!()
    };

    quote!(
        impl std::convert::TryFrom<qualia::Object> for #orig_type_name {
            type Error = qualia::ConversionError;

            fn try_from(object: qualia::Object) -> std::result::Result<#orig_type_name, qualia::ConversionError> {
                #(
                    {
                        let value = object.#fixed_field_accessors;

                        if value != #fixed_field_values {
                            return Err(
                                qualia::ConversionError::FixedFieldWrongValue(
                                    #fixed_field_names.to_string(),
                                    #fixed_field_values.into(),
                                    value.into(),
                                )
                            );
                        }
                    }
                )*

                Ok(#orig_type_name {
                    #(#field_idents: object.#field_accessors),*
                    #rest_field_try_from
                })
            }
        }

        impl std::convert::Into<qualia::Object> for #orig_type_name {
            fn into(self) -> qualia::Object {
                use qualia::{object, Object};

                let mut result = object!(
                    #(#fixed_field_names => #fixed_field_values),*
                    #(#field_names => self.#field_idents),*
                );
                #rest_field_into

                result
            }
        }

        impl qualia::ObjectShape for #orig_type_name {}
    ).into()
}
<|endoftext|>


<commit_before>//! Check if a reduction makes file smaller, and keep best reductions.
//! Works asynchronously when possible

use crate::atomicmin::AtomicMin;
use crate::deflate;
use crate::png::PngData;
use crate::png::PngImage;
use crate::png::STD_COMPRESSION;
use crate::png::STD_FILTERS;
use crate::png::STD_STRATEGY;
use crate::png::STD_WINDOW;
#[cfg(not(feature = "parallel"))]
use crate::rayon;
use crate::Deadline;
#[cfg(feature = "parallel")]
use rayon;
use rayon::prelude::*;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
#[cfg(feature = "parallel")]
use std::sync::mpsc::*;
use std::sync::Arc;
use std::thread;

struct Candidate {
    image: PngData,
    filter: u8,
    // first wins tie-breaker
    nth: usize,
}

impl Candidate {
    fn cmp_key(&self) -> impl Ord {
        (
            self.image.idat_data.len(),
            self.image.raw.data.len(),
            self.image.raw.ihdr.bit_depth,
            self.filter,
            self.nth,
        )
    }
}

/// Collect image versions and pick one that compresses best
pub(crate) struct Evaluator {
    deadline: Arc<Deadline>,
    nth: AtomicUsize,
    best_candidate_size: Arc<AtomicMin>,
    /// images are sent to the thread for evaluation
    #[cfg(feature = "parallel")]
    eval_send: SyncSender<Candidate>,
    // the thread helps evaluate images asynchronously
    #[cfg(feature = "parallel")]
    eval_thread: thread::JoinHandle<Option<Candidate>>,
    // in non-parallel mode, images are evaluated synchronously
    #[cfg(not(feature = "parallel"))]
    eval_best_candidate: Option<Candidate>,
}

impl Evaluator {
    pub fn new(deadline: Arc<Deadline>) -> Self {
        #[cfg(feature = "parallel")]
        let (tx, rx) = sync_channel(4);
        Self {
            deadline,
            best_candidate_size: Arc::new(AtomicMin::new(None)),
            nth: AtomicUsize::new(0),
            #[cfg(feature = "parallel")]
            eval_send: tx,
            #[cfg(feature = "parallel")]
            eval_thread: thread::spawn(move || rx.into_iter().min_by_key(Candidate::cmp_key)),
            #[cfg(not(feature = "parallel"))]
            eval_best_candidate: None,
        }
    }

    /// Wait for all evaluations to finish and return smallest reduction
    /// Or `None` if all reductions were worse than baseline.
    #[cfg(feature = "parallel")]
    fn get_best_candidate(self) -> Option<Candidate> {
        drop(self.eval_send); // disconnect the sender, breaking the loop in the thread
        self.eval_thread.join().expect("eval thread")
    }

    #[cfg(not(feature = "parallel"))]
    fn get_best_candidate(self) -> Option<Candidate> {
        self.eval_best_candidate
    }

    pub fn get_result(self) -> Option<PngData> {
        self.get_best_candidate().map(|candidate| candidate.image)
    }

    /// Set baseline image. It will be used only to measure minimum compression level required
    pub fn set_baseline(&self, image: Arc<PngImage>) {
        self.try_image_inner(image, false)
    }

    /// Check if the image is smaller than others
    pub fn try_image(&self, image: Arc<PngImage>) {
        self.try_image_inner(image, true)
    }

    fn try_image_inner(&self, image: Arc<PngImage>, is_reduction: bool) {
        let nth = self.nth.fetch_add(1, SeqCst);
        // These clones are only cheap refcounts
        let deadline = self.deadline.clone();
        let best_candidate_size = self.best_candidate_size.clone();
        // sends it off asynchronously for compression,
        // but results will be collected via the message queue
        #[cfg(feature = "parallel")]
        let eval_send = self.eval_send.clone();
        rayon::spawn(move || {
            let filters_iter = STD_FILTERS.par_iter().with_max_len(1);

            // Updating of best result inside the parallel loop would require locks,
            // which are dangerous to do in side Rayon's loop.
            // Instead, only update (atomic) best size in real time,
            // and the best result later without need for locks.
            filters_iter.for_each(|&filter| {
                if deadline.passed() {
                    return;
                }
                if let Ok(idat_data) = deflate::deflate(
                    &image.filter_image(filter),
                    STD_COMPRESSION,
                    STD_STRATEGY,
                    STD_WINDOW,
                    &best_candidate_size,
                    &deadline,
                ) {
                    best_candidate_size.set_min(idat_data.len());
                    // ignore baseline images after this point
                    if !is_reduction {
                        return;
                    }
                    // the rest is shipped to the evavluation/collection thread
                    let new = Candidate {
                        image: PngData {
                            idat_data,
                            raw: Arc::clone(&image),
                        },
                        filter,
                        nth,
                    };

                    #[cfg(feature = "parallel")]
                    {
                        eval_send.send(new).expect("send");
                    }

                    #[cfg(not(feature = "parallel"))]
                    {
                        match self.eval_best_candidate {
                            Some(prev) if prev.cmp_key() < new.cmp_key() => {}
                            _ => self.eval_best_candidate = Some(new),
                        }
                    }
                }
            });
        });
    }
}
<commit_msg>Unbreak no-default-features<commit_after>//! Check if a reduction makes file smaller, and keep best reductions.
//! Works asynchronously when possible

use crate::atomicmin::AtomicMin;
use crate::deflate;
use crate::png::PngData;
use crate::png::PngImage;
use crate::png::STD_COMPRESSION;
use crate::png::STD_FILTERS;
use crate::png::STD_STRATEGY;
use crate::png::STD_WINDOW;
#[cfg(not(feature = "parallel"))]
use crate::rayon;
use crate::Deadline;
#[cfg(feature = "parallel")]
use rayon;
use rayon::prelude::*;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::mpsc::*;
use std::sync::Arc;
use std::thread;

struct Candidate {
    image: PngData,
    filter: u8,
    // first wins tie-breaker
    nth: usize,
}

impl Candidate {
    fn cmp_key(&self) -> impl Ord {
        (
            self.image.idat_data.len(),
            self.image.raw.data.len(),
            self.image.raw.ihdr.bit_depth,
            self.filter,
            self.nth,
        )
    }
}

/// Collect image versions and pick one that compresses best
pub(crate) struct Evaluator {
    deadline: Arc<Deadline>,
    nth: AtomicUsize,
    best_candidate_size: Arc<AtomicMin>,
    /// images are sent to the thread for evaluation
    eval_send: SyncSender<Candidate>,
    // the thread helps evaluate images asynchronously
    eval_thread: thread::JoinHandle<Option<Candidate>>,
}

impl Evaluator {
    pub fn new(deadline: Arc<Deadline>) -> Self {
        let (tx, rx) = sync_channel(4);
        Self {
            deadline,
            best_candidate_size: Arc::new(AtomicMin::new(None)),
            nth: AtomicUsize::new(0),
            eval_send: tx,
            eval_thread: thread::spawn(move || rx.into_iter().min_by_key(Candidate::cmp_key)),
        }
    }

    /// Wait for all evaluations to finish and return smallest reduction
    /// Or `None` if all reductions were worse than baseline.
    fn get_best_candidate(self) -> Option<Candidate> {
        drop(self.eval_send); // disconnect the sender, breaking the loop in the thread
        self.eval_thread.join().expect("eval thread")
    }

    pub fn get_result(self) -> Option<PngData> {
        self.get_best_candidate().map(|candidate| candidate.image)
    }

    /// Set baseline image. It will be used only to measure minimum compression level required
    pub fn set_baseline(&self, image: Arc<PngImage>) {
        self.try_image_inner(image, false)
    }

    /// Check if the image is smaller than others
    pub fn try_image(&self, image: Arc<PngImage>) {
        self.try_image_inner(image, true)
    }

    fn try_image_inner(&self, image: Arc<PngImage>, is_reduction: bool) {
        let nth = self.nth.fetch_add(1, SeqCst);
        // These clones are only cheap refcounts
        let deadline = self.deadline.clone();
        let best_candidate_size = self.best_candidate_size.clone();
        // sends it off asynchronously for compression,
        // but results will be collected via the message queue
        let eval_send = self.eval_send.clone();
        rayon::spawn(move || {
            let filters_iter = STD_FILTERS.par_iter().with_max_len(1);

            // Updating of best result inside the parallel loop would require locks,
            // which are dangerous to do in side Rayon's loop.
            // Instead, only update (atomic) best size in real time,
            // and the best result later without need for locks.
            filters_iter.for_each(|&filter| {
                if deadline.passed() {
                    return;
                }
                if let Ok(idat_data) = deflate::deflate(
                    &image.filter_image(filter),
                    STD_COMPRESSION,
                    STD_STRATEGY,
                    STD_WINDOW,
                    &best_candidate_size,
                    &deadline,
                ) {
                    best_candidate_size.set_min(idat_data.len());
                    // ignore baseline images after this point
                    if !is_reduction {
                        return;
                    }
                    // the rest is shipped to the evavluation/collection thread
                    let new = Candidate {
                        image: PngData {
                            idat_data,
                            raw: Arc::clone(&image),
                        },
                        filter,
                        nth,
                    };

                    eval_send.send(new).expect("send");
                }
            });
        });
    }
}
<|endoftext|>


<commit_before>use std::{env, path::PathBuf};

/// Get *the* Jupyter data directory.
///
/// See https://jupyter.readthedocs.io/en/latest/use/jupyter-directories.html.
pub fn data_dir() -> PathBuf {
    if let Ok(path) = env::var("JUPYTER_DATA_DIR") {
        PathBuf::from(path)
    } else if let Some(data_dir) = ::dirs::data_dir() {
        #[cfg(target_os = "macos")]
        return data_dir
            .parent()
            .expect("Should have a parent dir")
            .join("Jupyter");

        #[cfg(not(target_os = "macos"))]
        return data_dir.join("jupyter");
    } else {
        PathBuf::from(".")
    }
}

/// Get all the directories where Jupyter stores data files.
///
/// See https://jupyter.readthedocs.io/en/latest/use/jupyter-directories.html
/// and `jupyter --paths`.
pub fn data_dirs() -> Vec<PathBuf> {
    let mut dirs = if let Ok(path) = env::var("JUPYTER_PATH") {
        #[cfg(target_os = "windows")]
        const SEP: char = ';';
        #[cfg(not(target_os = "windows"))]
        const SEP: char = ':';
        path.split(SEP).map(PathBuf::from).collect()
    } else {
        vec![]
    };

    dirs.push(data_dir());
    dirs.push(PathBuf::from("/usr/local/share/jupyter"));
    dirs.push(PathBuf::from("/usr/share/jupyter"));

    dirs
}

/// Get the directories where Jupyter may store kernel specs.
pub fn kernel_dirs() -> Vec<PathBuf> {
    data_dirs()
        .into_iter()
        .map(|path| path.join("kernels"))
        .collect()
}

/// Get the directories where Jupyter may store runtime files e.g. connection files.
///
/// See https://jupyter.readthedocs.io/en/latest/use/jupyter-directories.html
/// and `jupyter --runtime-dir`. To avoid brittleness this returns multiple options.
pub fn runtime_dirs() -> Vec<PathBuf> {
    let mut dirs = Vec::new();

    if let Ok(path) = env::var("JUPYTER_RUNTIME_DIR") {
        dirs.push(PathBuf::from(path))
    }

    dirs.push(data_dir().join("runtime"));

    if let Some(runtime_dir) = ::dirs::runtime_dir() {
        dirs.push(runtime_dir.join("jupyter"));
    }

    dirs
}
<commit_msg>fix(Jupyter kernels): Get paths from `jupyter --paths` if possible<commit_after>use std::{env, path::PathBuf, process::Command};

/// Run the `jupyter` command to get lists of directories
///
/// This can be necessary if Jupyter has been installed using something
/// like `mamba` (and maybe Nix?) in which case the rules described in
/// the Jupyter documentation and implemented below may not apply.
///
/// An optimization could be to store `static` results and throttle
/// calls to `jupyter`.
fn jupyter_paths() -> (Vec<PathBuf>, Vec<PathBuf>) {
    let mut data = Vec::new();
    let mut runtime = Vec::new();
    if let Ok(output) = Command::new("jupyter").arg("--path").output() {
        if let Ok(stdout) = std::str::from_utf8(&output.stdout) {
            let mut group = "";
            for line in stdout.lines() {
                let line = line.trim();
                if line.ends_with(':') {
                    group = line;
                } else if group == "data:" {
                    data.push(PathBuf::from(line));
                } else if group == "runtime:" {
                    runtime.push(PathBuf::from(line));
                }
            }
        }
    }
    (data, runtime)
}

/// Get *the* Jupyter data directory.
///
/// See https://jupyter.readthedocs.io/en/latest/use/jupyter-directories.html.
pub fn data_dir() -> PathBuf {
    let (dirs, ..) = jupyter_paths();
    if let Some(first) = dirs.first() {
        return first.clone();
    }

    if let Ok(path) = env::var("JUPYTER_DATA_DIR") {
        PathBuf::from(path)
    } else if let Some(data_dir) = ::dirs::data_dir() {
        #[cfg(target_os = "macos")]
        return data_dir
            .parent()
            .expect("Should have a parent dir")
            .join("Jupyter");

        #[cfg(not(target_os = "macos"))]
        return data_dir.join("jupyter");
    } else {
        PathBuf::from(".")
    }
}

/// Get all the directories where Jupyter stores data files.
///
/// See https://jupyter.readthedocs.io/en/latest/use/jupyter-directories.html
/// and `jupyter --paths`.
pub fn data_dirs() -> Vec<PathBuf> {
    let (mut dirs, ..) = jupyter_paths();

    if let Ok(path) = env::var("JUPYTER_PATH") {
        #[cfg(target_os = "windows")]
        const SEP: char = ';';
        #[cfg(not(target_os = "windows"))]
        const SEP: char = ':';

        let paths = path.split(SEP).map(PathBuf::from).collect();
        append_missing(&mut dirs, paths);
    }

    append_missing(
        &mut dirs,
        vec![
            data_dir(),
            #[cfg(not(target_os = "windows"))]
            PathBuf::from("/usr/local/share/jupyter"),
            #[cfg(not(target_os = "windows"))]
            PathBuf::from("/usr/share/jupyter"),
        ],
    );

    dirs
}

/// Get the directories where Jupyter may store kernel specs.
pub fn kernel_dirs() -> Vec<PathBuf> {
    data_dirs()
        .into_iter()
        .map(|path| path.join("kernels"))
        .collect()
}

/// Get the directories where Jupyter may store runtime files e.g. connection files.
///
/// See https://jupyter.readthedocs.io/en/latest/use/jupyter-directories.html
/// and `jupyter --runtime-dir`. To avoid brittleness this returns multiple options.
pub fn runtime_dirs() -> Vec<PathBuf> {
    let (.., mut dirs) = jupyter_paths();

    if let Ok(path) = env::var("JUPYTER_RUNTIME_DIR") {
        push_missing(&mut dirs, PathBuf::from(path));
    }

    push_missing(&mut dirs, data_dir().join("runtime"));

    if let Some(runtime_dir) = ::dirs::runtime_dir() {
        push_missing(&mut dirs, runtime_dir.join("jupyter"));
    }

    dirs
}

/// Add a path if it is missing from a set of paths
fn push_missing(paths: &mut Vec<PathBuf>, path: PathBuf) {
    if !paths.contains(&path) {
        paths.push(path);
    }
}

/// Append paths if they are missing from a set of paths
fn append_missing(paths: &mut Vec<PathBuf>, others: Vec<PathBuf>) {
    for path in others {
        if !paths.contains(&path) {
            paths.push(path);
        }
    }
}
<|endoftext|>


<commit_before>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

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

use pipeliner::Pipeline;

use esr_crate::{CrateInfoWithScore, CrateGeneralInfo};
use esr_github::RepoInfoWithScore;
use esr_printer::EsrPrinter;
use esr_errors::*;

use std::f64;

pub struct CrateScores {
    crate_full: CrateInfoWithScore,
    repo_full: Option<Result<RepoInfoWithScore>>,
    printer: EsrPrinter,
}

impl CrateScores {
    pub fn from_id(id: &str, printer: EsrPrinter) -> Result<Self> {
        let crate_full = CrateInfoWithScore::from_id(id)?;
        let repo_full = if let Some(gh_id) = crate_full.get_info().github_id() {
            Some(RepoInfoWithScore::from_id(&gh_id))
        } else {
            None
        };

        Ok(Self {
            crate_full,
            repo_full,
            printer,
        })
    }

    pub fn from_id_with_token(id: &str, gh_token: &str, printer: EsrPrinter) -> Result<Self> {
        let crate_full = CrateInfoWithScore::from_id(id)?;
        let repo_full = if let Some(gh_id) = crate_full.get_info().github_id() {
            Some(RepoInfoWithScore::from_id_with_token(&gh_id, gh_token))
        } else {
            None
        };

        Ok(Self {
            crate_full,
            repo_full,
            printer,
        })
    }

    pub fn from_id_crate_only(id: &str, printer: EsrPrinter) -> Result<Self> {
        let crate_full = CrateInfoWithScore::from_id(id)?;

        Ok(Self {
            crate_full,
            repo_full: None,
            printer,
        })
    }

    // ====================
    fn score_crate(&self) -> String {
        let (pos, neg) = self.crate_full.get_score_tuple();
        self.printer.score_overview("Crate Score", pos, neg)
    }

    fn score_repo(&self) -> String {
        if let Some(ref repo_full_res) = self.repo_full {
            if let Ok(ref repo_full) = *repo_full_res {
                let (pos, neg) = repo_full.get_score_tuple();
                self.printer.score_overview("Repo Score ", pos, neg)
            } else {
                self.printer.score_error("Repo Score ")
            }
        } else {
            self.printer.score_na("Repo Score ")
        }
    }

    pub fn print_detailed_scores(&self) {
        let table = self.crate_full.get_score_table();
        println!("{}", self.printer.score_details("Crate Score Details", table));
        println!("{}", self.score_crate());

        if let Some(ref repo_full_res) = self.repo_full {
            if let Ok(ref repo_full) = *repo_full_res {
                let table = repo_full.get_score_table();
                println!("{}", self.printer.score_details("Repo Score Details", table));
            }
        }
        println!("{}", self.score_repo());
    }

    // =================

    pub fn collect_scores(crates: &[CrateGeneralInfo], token: &str, crate_only: bool, limit: usize, printer: EsrPrinter) -> Vec<(String, Result<Self>)> {
        let id_token_pair: Vec<_> = crates.iter()
            .map(|cr| (String::from(cr.get_id()), String::from(token)))
            .collect();

        if crate_only {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, _)| (id.clone(), CrateScores::from_id_crate_only(&id, printer)))
                .collect()
        } else {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, token)| (id.clone(), CrateScores::from_id_with_token(&id, &token, printer)))
                .collect()
        }
    }

    fn info_pair(&self, id: &str, sort_positive: bool) -> (f64, String) {
        let (pos, neg) = self.crate_full.get_score_tuple();

        let empty_or_all_yanked_str = match self.crate_full.get_info().empty_or_all_yanked() {
            true => self.printer.red_bold("(empty/all yanked)"),
            false => String::new(),
        };

        let sort_score = match sort_positive {
            true => pos,
            false => pos + neg,
        };

        let releases = self.crate_full.get_score_info().get_releases();
        let non_yanked = self.crate_full.get_score_info().get_non_yanked_releases();
        let yanked = releases - non_yanked;
        let m_s_l_r = self.crate_full.get_score_info().get_months_since_last_release();
        let releases_msg = format!("{}+{} ({:.1} months since last non-yanked release)",
                                   self.printer.green_bold(&format!("{}", non_yanked)),
                                   self.printer.red_bold(&format!("{}", yanked)),
                                   m_s_l_r);

        let dependants = self.crate_full.get_score_info().get_dependants();
        let d_b_n_o = self.crate_full.get_score_info().get_dependants_from_non_owners();
        let dependants_msg = format!("{} ({} from non owners)", dependants, d_b_n_o);

        let info_str = format!("{} {}\n  {}\n  {}\n  {}\n  {}\n  {}\n  {}\n  {}\n  {}\n",
                               self.printer.blue_bold(id),
                               empty_or_all_yanked_str,
                               self.score_crate(),
                               self.score_repo(),
                               self.printer.msg_pair("Releases   ", &releases_msg),
                               self.printer.msg_pair("Dependants ", &dependants_msg),
                               self.printer.msg_pair("Max Version",
                                              self.crate_full.get_info().get_max_version()),
                               self.printer.msg_pair("License    ",
                                              self.crate_full.get_info().get_license().unwrap_or("N/A")),
                               self.printer.msg_pair("Repository ",
                                              self.crate_full.get_info().get_repository().unwrap_or("N/A")),
                               self.printer.msg_pair("Description",
                                              self.crate_full.get_info().get_description().unwrap_or("N/A")
                                              .trim()),
                               );

        (sort_score, info_str)
    }

    pub fn print_search_results(results: &[(String, Result<Self>)], sort_positive: bool, limit: usize, printer: EsrPrinter) {
        let mut results_vec = Vec::with_capacity(32);
        for res in results {
            match *res {
                (ref id, Ok(ref score_info)) => {
                    results_vec.push(score_info.info_pair(id, sort_positive));
                },
                (ref id, Err(_)) => {
                    results_vec.push((f64::MIN, format!("{}: Failed to get score info.", id)));
                },
            }
        }

        // -sort_score to get scores in reverse
        results_vec.sort_by_key(|&(sort_score, _)| -sort_score as i64);
        for (num, result) in results_vec.iter().take(limit).enumerate() {
            println!("{} {}",
                     printer.blue_bold(&format!("({})", num + 1)),
                     result.1);
        }
    }
}
<commit_msg>Fix the order of results if some scores share the same integral part<commit_after>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

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

use pipeliner::Pipeline;

use esr_crate::{CrateInfoWithScore, CrateGeneralInfo};
use esr_github::RepoInfoWithScore;
use esr_printer::EsrPrinter;
use esr_errors::*;

use std::f64;

pub struct CrateScores {
    crate_full: CrateInfoWithScore,
    repo_full: Option<Result<RepoInfoWithScore>>,
    printer: EsrPrinter,
}

impl CrateScores {
    pub fn from_id(id: &str, printer: EsrPrinter) -> Result<Self> {
        let crate_full = CrateInfoWithScore::from_id(id)?;
        let repo_full = if let Some(gh_id) = crate_full.get_info().github_id() {
            Some(RepoInfoWithScore::from_id(&gh_id))
        } else {
            None
        };

        Ok(Self {
            crate_full,
            repo_full,
            printer,
        })
    }

    pub fn from_id_with_token(id: &str, gh_token: &str, printer: EsrPrinter) -> Result<Self> {
        let crate_full = CrateInfoWithScore::from_id(id)?;
        let repo_full = if let Some(gh_id) = crate_full.get_info().github_id() {
            Some(RepoInfoWithScore::from_id_with_token(&gh_id, gh_token))
        } else {
            None
        };

        Ok(Self {
            crate_full,
            repo_full,
            printer,
        })
    }

    pub fn from_id_crate_only(id: &str, printer: EsrPrinter) -> Result<Self> {
        let crate_full = CrateInfoWithScore::from_id(id)?;

        Ok(Self {
            crate_full,
            repo_full: None,
            printer,
        })
    }

    // ====================
    fn score_crate(&self) -> String {
        let (pos, neg) = self.crate_full.get_score_tuple();
        self.printer.score_overview("Crate Score", pos, neg)
    }

    fn score_repo(&self) -> String {
        if let Some(ref repo_full_res) = self.repo_full {
            if let Ok(ref repo_full) = *repo_full_res {
                let (pos, neg) = repo_full.get_score_tuple();
                self.printer.score_overview("Repo Score ", pos, neg)
            } else {
                self.printer.score_error("Repo Score ")
            }
        } else {
            self.printer.score_na("Repo Score ")
        }
    }

    pub fn print_detailed_scores(&self) {
        let table = self.crate_full.get_score_table();
        println!("{}", self.printer.score_details("Crate Score Details", table));
        println!("{}", self.score_crate());

        if let Some(ref repo_full_res) = self.repo_full {
            if let Ok(ref repo_full) = *repo_full_res {
                let table = repo_full.get_score_table();
                println!("{}", self.printer.score_details("Repo Score Details", table));
            }
        }
        println!("{}", self.score_repo());
    }

    // =================

    pub fn collect_scores(crates: &[CrateGeneralInfo], token: &str, crate_only: bool, limit: usize, printer: EsrPrinter) -> Vec<(String, Result<Self>)> {
        let id_token_pair: Vec<_> = crates.iter()
            .map(|cr| (String::from(cr.get_id()), String::from(token)))
            .collect();

        if crate_only {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, _)| (id.clone(), CrateScores::from_id_crate_only(&id, printer)))
                .collect()
        } else {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, token)| (id.clone(), CrateScores::from_id_with_token(&id, &token, printer)))
                .collect()
        }
    }

    fn info_pair(&self, id: &str, sort_positive: bool) -> (f64, String) {
        let (pos, neg) = self.crate_full.get_score_tuple();

        let empty_or_all_yanked_str = match self.crate_full.get_info().empty_or_all_yanked() {
            true => self.printer.red_bold("(empty/all yanked)"),
            false => String::new(),
        };

        let sort_score = match sort_positive {
            true => pos,
            false => pos + neg,
        };

        let releases = self.crate_full.get_score_info().get_releases();
        let non_yanked = self.crate_full.get_score_info().get_non_yanked_releases();
        let yanked = releases - non_yanked;
        let m_s_l_r = self.crate_full.get_score_info().get_months_since_last_release();
        let releases_msg = format!("{}+{} ({:.1} months since last non-yanked release)",
                                   self.printer.green_bold(&format!("{}", non_yanked)),
                                   self.printer.red_bold(&format!("{}", yanked)),
                                   m_s_l_r);

        let dependants = self.crate_full.get_score_info().get_dependants();
        let d_b_n_o = self.crate_full.get_score_info().get_dependants_from_non_owners();
        let dependants_msg = format!("{} ({} from non owners)", dependants, d_b_n_o);

        let info_str = format!("{} {}\n  {}\n  {}\n  {}\n  {}\n  {}\n  {}\n  {}\n  {}\n",
                               self.printer.blue_bold(id),
                               empty_or_all_yanked_str,
                               self.score_crate(),
                               self.score_repo(),
                               self.printer.msg_pair("Releases   ", &releases_msg),
                               self.printer.msg_pair("Dependants ", &dependants_msg),
                               self.printer.msg_pair("Max Version",
                                              self.crate_full.get_info().get_max_version()),
                               self.printer.msg_pair("License    ",
                                              self.crate_full.get_info().get_license().unwrap_or("N/A")),
                               self.printer.msg_pair("Repository ",
                                              self.crate_full.get_info().get_repository().unwrap_or("N/A")),
                               self.printer.msg_pair("Description",
                                              self.crate_full.get_info().get_description().unwrap_or("N/A")
                                              .trim()),
                               );

        (sort_score, info_str)
    }

    pub fn print_search_results(results: &[(String, Result<Self>)], sort_positive: bool, limit: usize, printer: EsrPrinter) {
        let mut results_vec = Vec::with_capacity(32);
        for res in results {
            match *res {
                (ref id, Ok(ref score_info)) => {
                    results_vec.push(score_info.info_pair(id, sort_positive));
                },
                (ref id, Err(_)) => {
                    results_vec.push((f64::MIN, format!("{}: Failed to get score info.", id)));
                },
            }
        }

        // Negation to get scores in reverse.
        // `* 10000.0` to not lose order accuracy after casting.
        results_vec.sort_by_key(|&(sort_score, _)| -(sort_score * 10000.0) as i64);
        for (num, result) in results_vec.iter().take(limit).enumerate() {
            println!("{} {}",
                     printer.blue_bold(&format!("({})", num + 1)),
                     result.1);
        }
    }
}
<|endoftext|>


<commit_before>//! Contains code for selecting features

#![deny(missing_docs)]
#![deny(unused_extern_crates)]

use std::io;
use std::str::FromStr;

/// Define RustTarget struct definition, Default impl, and conversions
/// between RustTarget and String.
macro_rules! rust_target_def {
    ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => {
        /// Represents the version of the Rust language to target.
        ///
        /// To support a beta release, use the corresponding stable release.
        ///
        /// This enum will have more variants added as necessary.
        #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash)]
        #[allow(non_camel_case_types)]
        pub enum RustTarget {
            $(
                $(
                    #[$attr]
                )*
                $release,
            )*
        }

        impl Default for RustTarget {
            /// Gives the latest stable Rust version
            fn default() -> RustTarget {
                LATEST_STABLE_RUST
            }
        }

        impl FromStr for RustTarget {
            type Err = io::Error;

            /// Create a `RustTarget` from a string.
            ///
            /// * The stable/beta versions of Rust are of the form "1.0",
            /// "1.19", etc.
            /// * The nightly version should be specified with "nightly".
            fn from_str(s: &str) -> Result<Self, Self::Err> {
                match s.as_ref() {
                    $(
                        stringify!($value) => Ok(RustTarget::$release),
                    )*
                    _ => Err(
                        io::Error::new(
                            io::ErrorKind::InvalidInput,
                            concat!(
                                "Got an invalid rust target. Accepted values ",
                                "are of the form ",
                                "\"1.0\" or \"nightly\"."))),
                }
            }
        }

        impl From<RustTarget> for String {
            fn from(target: RustTarget) -> Self {
                match target {
                    $(
                        RustTarget::$release => stringify!($value),
                    )*
                }.into()
            }
        }
    }
}

/// Defines an array slice with all RustTarget values
macro_rules! rust_target_values_def {
    ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => {
        /// Strings of allowed `RustTarget` values
        pub static RUST_TARGET_STRINGS: &'static [&str] = &[
            $(
                stringify!($value),
            )*
        ];
    }
}

/// Defines macro which takes a macro
macro_rules! rust_target_base {
    ( $x_macro:ident ) => {
        $x_macro!(
            /// Rust stable 1.0
            => Stable_1_0 => 1.0;
            /// Rust stable 1.1
            => Stable_1_1 => 1.1;
            /// Rust stable 1.19
            ///  * Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md))
            => Stable_1_19 => 1.19;
            /// Rust stable 1.20
            ///  * Associated constants ([PR](https://github.com/rust-lang/rust/pull/42809))
            => Stable_1_20 => 1.20;
            /// Rust stable 1.21
            ///  * Builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690))
            => Stable_1_21 => 1.21;
            /// Rust stable 1.25
            ///  * `repr(align)` ([PR](https://github.com/rust-lang/rust/pull/47006))
            => Stable_1_25 => 1.25;
            /// Rust stable 1.26
            ///  * [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html)
            => Stable_1_26 => 1.26;
            /// Rust stable 1.27
            ///  * `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925))
            => Stable_1_27 => 1.27;
            /// Rust stable 1.28
            ///  * `repr(transparent)` ([PR](https://github.com/rust-lang/rust/pull/51562))
            => Stable_1_28 => 1.28;
            /// Rust stable 1.30
            ///  * `const fn` support for limited cases ([PR](https://github.com/rust-lang/rust/pull/54835/)
            /// *  [c_void available in core](https://doc.rust-lang.org/core/ffi/enum.c_void.html)
            => Stable_1_30 => 1.30;
            /// Rust stable 1.33
            ///  * repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049))
            => Stable_1_33 => 1.33;
            /// Rust stable 1.36
            ///  * `MaybeUninit` instead of `mem::uninitialized()` ([PR](https://github.com/rust-lang/rust/pull/60445))
            => Stable_1_36 => 1.36;
            /// Nightly rust
            ///  * `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
            ///  * `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109))
            => Nightly => nightly;
        );
    }
}

rust_target_base!(rust_target_def);
rust_target_base!(rust_target_values_def);

/// Latest stable release of Rust
pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_36;

/// Create RustFeatures struct definition, new(), and a getter for each field
macro_rules! rust_feature_def {
    (
        $( $rust_target:ident {
            $( $( #[$attr:meta] )* => $feature:ident; )*
        } )*
    ) => {
        /// Features supported by a rust target
        #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
        #[allow(missing_docs)] // Documentation should go into the relevant variants.
        pub(crate) struct RustFeatures {
            $( $(
                $(
                    #[$attr]
                )*
                pub $feature: bool,
            )* )*
        }

        impl RustFeatures {
            /// Gives a RustFeatures struct with all features disabled
            fn new() -> Self {
                RustFeatures {
                    $( $(
                        $feature: false,
                    )* )*
                }
            }
        }

        impl From<RustTarget> for RustFeatures {
            fn from(rust_target: RustTarget) -> Self {
                let mut features = RustFeatures::new();

                $(
                if rust_target >= RustTarget::$rust_target {
                    $(
                    features.$feature = true;
                    )*
                }
                )*

                features
            }
        }
    }
}

// NOTE(emilio): When adding or removing features here, make sure to update the
// documentation for the relevant variant in the rust_target_base macro
// definition.
rust_feature_def!(
    Stable_1_19 {
        => untagged_union;
    }
    Stable_1_20 {
        => associated_const;
    }
    Stable_1_21 {
        => builtin_clone_impls;
    }
    Stable_1_25 {
        => repr_align;
    }
    Stable_1_26 {
        => i128_and_u128;
    }
    Stable_1_27 {
        => must_use_function;
    }
    Stable_1_28 {
        => repr_transparent;
    }
    Stable_1_30 {
        => min_const_fn;
        => core_ffi_c_void;
    }
    Stable_1_33 {
        => repr_packed_n;
    }
    Stable_1_36 {
        => maybe_uninit;
    }
    Nightly {
        => thiscall_abi;
        => non_exhaustive;
    }
);

impl Default for RustFeatures {
    fn default() -> Self {
        let default_rust_target: RustTarget = Default::default();
        Self::from(default_rust_target)
    }
}

#[cfg(test)]
mod test {
    #![allow(unused_imports)]
    use super::*;

    #[test]
    fn target_features() {
        let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0);
        assert!(
            !f_1_0.core_ffi_c_void &&
                !f_1_0.untagged_union &&
                !f_1_0.associated_const &&
                !f_1_0.builtin_clone_impls &&
                !f_1_0.repr_align &&
                !f_1_0.thiscall_abi
        );
        let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21);
        assert!(
            !f_1_21.core_ffi_c_void &&
                f_1_21.untagged_union &&
                f_1_21.associated_const &&
                f_1_21.builtin_clone_impls &&
                !f_1_21.repr_align &&
                !f_1_21.thiscall_abi
        );
        let f_nightly = RustFeatures::from(RustTarget::Nightly);
        assert!(
            f_nightly.core_ffi_c_void &&
                f_nightly.untagged_union &&
                f_nightly.associated_const &&
                f_nightly.builtin_clone_impls &&
                f_nightly.maybe_uninit &&
                f_nightly.repr_align &&
                f_nightly.thiscall_abi
        );
    }

    fn test_target(target_str: &str, target: RustTarget) {
        let target_string: String = target.into();
        assert_eq!(target_str, target_string);
        assert_eq!(target, RustTarget::from_str(target_str).unwrap());
    }

    #[test]
    fn str_to_target() {
        test_target("1.0", RustTarget::Stable_1_0);
        test_target("1.19", RustTarget::Stable_1_19);
        test_target("1.21", RustTarget::Stable_1_21);
        test_target("1.25", RustTarget::Stable_1_25);
        test_target("nightly", RustTarget::Nightly);
    }
}
<commit_msg>Update latest stable rust to 1.40 and give non_exhaustive feature<commit_after>//! Contains code for selecting features

#![deny(missing_docs)]
#![deny(unused_extern_crates)]

use std::io;
use std::str::FromStr;

/// Define RustTarget struct definition, Default impl, and conversions
/// between RustTarget and String.
macro_rules! rust_target_def {
    ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => {
        /// Represents the version of the Rust language to target.
        ///
        /// To support a beta release, use the corresponding stable release.
        ///
        /// This enum will have more variants added as necessary.
        #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash)]
        #[allow(non_camel_case_types)]
        pub enum RustTarget {
            $(
                $(
                    #[$attr]
                )*
                $release,
            )*
        }

        impl Default for RustTarget {
            /// Gives the latest stable Rust version
            fn default() -> RustTarget {
                LATEST_STABLE_RUST
            }
        }

        impl FromStr for RustTarget {
            type Err = io::Error;

            /// Create a `RustTarget` from a string.
            ///
            /// * The stable/beta versions of Rust are of the form "1.0",
            /// "1.19", etc.
            /// * The nightly version should be specified with "nightly".
            fn from_str(s: &str) -> Result<Self, Self::Err> {
                match s.as_ref() {
                    $(
                        stringify!($value) => Ok(RustTarget::$release),
                    )*
                    _ => Err(
                        io::Error::new(
                            io::ErrorKind::InvalidInput,
                            concat!(
                                "Got an invalid rust target. Accepted values ",
                                "are of the form ",
                                "\"1.0\" or \"nightly\"."))),
                }
            }
        }

        impl From<RustTarget> for String {
            fn from(target: RustTarget) -> Self {
                match target {
                    $(
                        RustTarget::$release => stringify!($value),
                    )*
                }.into()
            }
        }
    }
}

/// Defines an array slice with all RustTarget values
macro_rules! rust_target_values_def {
    ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => {
        /// Strings of allowed `RustTarget` values
        pub static RUST_TARGET_STRINGS: &'static [&str] = &[
            $(
                stringify!($value),
            )*
        ];
    }
}

/// Defines macro which takes a macro
macro_rules! rust_target_base {
    ( $x_macro:ident ) => {
        $x_macro!(
            /// Rust stable 1.0
            => Stable_1_0 => 1.0;
            /// Rust stable 1.1
            => Stable_1_1 => 1.1;
            /// Rust stable 1.19
            ///  * Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md))
            => Stable_1_19 => 1.19;
            /// Rust stable 1.20
            ///  * Associated constants ([PR](https://github.com/rust-lang/rust/pull/42809))
            => Stable_1_20 => 1.20;
            /// Rust stable 1.21
            ///  * Builtin impls for `Clone` ([PR](https://github.com/rust-lang/rust/pull/43690))
            => Stable_1_21 => 1.21;
            /// Rust stable 1.25
            ///  * `repr(align)` ([PR](https://github.com/rust-lang/rust/pull/47006))
            => Stable_1_25 => 1.25;
            /// Rust stable 1.26
            ///  * [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html)
            => Stable_1_26 => 1.26;
            /// Rust stable 1.27
            ///  * `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925))
            => Stable_1_27 => 1.27;
            /// Rust stable 1.28
            ///  * `repr(transparent)` ([PR](https://github.com/rust-lang/rust/pull/51562))
            => Stable_1_28 => 1.28;
            /// Rust stable 1.30
            ///  * `const fn` support for limited cases ([PR](https://github.com/rust-lang/rust/pull/54835/)
            /// *  [c_void available in core](https://doc.rust-lang.org/core/ffi/enum.c_void.html)
            => Stable_1_30 => 1.30;
            /// Rust stable 1.33
            ///  * repr(packed(N)) ([PR](https://github.com/rust-lang/rust/pull/57049))
            => Stable_1_33 => 1.33;
            /// Rust stable 1.36
            ///  * `MaybeUninit` instead of `mem::uninitialized()` ([PR](https://github.com/rust-lang/rust/pull/60445))
            => Stable_1_36 => 1.36;
            /// Rust stable 1.40
            /// * `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109))
            => Stable_1_40 => 1.40;
            /// Nightly rust
            ///  * `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
            => Nightly => nightly;
        );
    }
}

rust_target_base!(rust_target_def);
rust_target_base!(rust_target_values_def);

/// Latest stable release of Rust
pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_40;

/// Create RustFeatures struct definition, new(), and a getter for each field
macro_rules! rust_feature_def {
    (
        $( $rust_target:ident {
            $( $( #[$attr:meta] )* => $feature:ident; )*
        } )*
    ) => {
        /// Features supported by a rust target
        #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
        #[allow(missing_docs)] // Documentation should go into the relevant variants.
        pub(crate) struct RustFeatures {
            $( $(
                $(
                    #[$attr]
                )*
                pub $feature: bool,
            )* )*
        }

        impl RustFeatures {
            /// Gives a RustFeatures struct with all features disabled
            fn new() -> Self {
                RustFeatures {
                    $( $(
                        $feature: false,
                    )* )*
                }
            }
        }

        impl From<RustTarget> for RustFeatures {
            fn from(rust_target: RustTarget) -> Self {
                let mut features = RustFeatures::new();

                $(
                if rust_target >= RustTarget::$rust_target {
                    $(
                    features.$feature = true;
                    )*
                }
                )*

                features
            }
        }
    }
}

// NOTE(emilio): When adding or removing features here, make sure to update the
// documentation for the relevant variant in the rust_target_base macro
// definition.
rust_feature_def!(
    Stable_1_19 {
        => untagged_union;
    }
    Stable_1_20 {
        => associated_const;
    }
    Stable_1_21 {
        => builtin_clone_impls;
    }
    Stable_1_25 {
        => repr_align;
    }
    Stable_1_26 {
        => i128_and_u128;
    }
    Stable_1_27 {
        => must_use_function;
    }
    Stable_1_28 {
        => repr_transparent;
    }
    Stable_1_30 {
        => min_const_fn;
        => core_ffi_c_void;
    }
    Stable_1_33 {
        => repr_packed_n;
    }
    Stable_1_36 {
        => maybe_uninit;
    }
    Stable_1_40 {
        => non_exhaustive;
    }
    Nightly {
        => thiscall_abi;
    }
);

impl Default for RustFeatures {
    fn default() -> Self {
        let default_rust_target: RustTarget = Default::default();
        Self::from(default_rust_target)
    }
}

#[cfg(test)]
mod test {
    #![allow(unused_imports)]
    use super::*;

    #[test]
    fn target_features() {
        let f_1_0 = RustFeatures::from(RustTarget::Stable_1_0);
        assert!(
            !f_1_0.core_ffi_c_void &&
                !f_1_0.untagged_union &&
                !f_1_0.associated_const &&
                !f_1_0.builtin_clone_impls &&
                !f_1_0.repr_align &&
                !f_1_0.thiscall_abi
        );
        let f_1_21 = RustFeatures::from(RustTarget::Stable_1_21);
        assert!(
            !f_1_21.core_ffi_c_void &&
                f_1_21.untagged_union &&
                f_1_21.associated_const &&
                f_1_21.builtin_clone_impls &&
                !f_1_21.repr_align &&
                !f_1_21.thiscall_abi
        );
        let f_nightly = RustFeatures::from(RustTarget::Nightly);
        assert!(
            f_nightly.core_ffi_c_void &&
                f_nightly.untagged_union &&
                f_nightly.associated_const &&
                f_nightly.builtin_clone_impls &&
                f_nightly.maybe_uninit &&
                f_nightly.repr_align &&
                f_nightly.thiscall_abi
        );
    }

    fn test_target(target_str: &str, target: RustTarget) {
        let target_string: String = target.into();
        assert_eq!(target_str, target_string);
        assert_eq!(target, RustTarget::from_str(target_str).unwrap());
    }

    #[test]
    fn str_to_target() {
        test_target("1.0", RustTarget::Stable_1_0);
        test_target("1.19", RustTarget::Stable_1_19);
        test_target("1.21", RustTarget::Stable_1_21);
        test_target("1.25", RustTarget::Stable_1_25);
        test_target("nightly", RustTarget::Nightly);
    }
}
<|endoftext|>


<commit_before>use libc::{self, c_char, c_void, size_t};

use std::mem;
use std::ptr;

use num::{Integer, NumCast};
use num::traits::cast;
use std::ffi::{CStr, CString};

use error::H5Result;

pub fn string_from_cstr(string: *const c_char) -> String {
    unsafe {
        String::from_utf8_unchecked(CStr::from_ptr(string).to_bytes().to_vec())
    }
}

pub fn string_to_cstr(string: String) -> *const c_char {
    CString::from_vec_unchecked(string.into_bytes()).as_ptr()
}

pub fn get_h5_str<T, F>(func: F) -> H5Result<String>
                 where F: Fn(*mut c_char, size_t) -> T, T: Integer + NumCast {
    unsafe {
        let len: isize = 1 + cast::<T, isize>(func(ptr::null_mut::<c_char>(), 0)).unwrap();
        ensure!(len > 0, "negative string length in get_h5_str()");
        let buf = libc::malloc((len as size_t) * mem::size_of::<c_char>() as size_t) as *mut c_char;
        func(buf, len as size_t);
        let msg = string_from_cstr(buf);
        libc::free(buf as *mut c_void);
        Ok(msg)
    }
}

#[test]
pub fn test_get_h5_str() {
    use ffi::h5e::{H5E_type_t, H5Eget_msg, H5E_CANTOPENOBJ};
    let s = unsafe {
        get_h5_str(|msg, size| {
            H5Eget_msg(*H5E_CANTOPENOBJ, ptr::null_mut::<H5E_type_t>(), msg, size)
        }).ok().unwrap()
    };
    assert_eq!(s, "Can't open object");
}
<commit_msg>Use unsafe block in util::string_to_cstr<commit_after>use libc::{self, c_char, c_void, size_t};

use std::mem;
use std::ptr;

use num::{Integer, NumCast};
use num::traits::cast;
use std::ffi::{CStr, CString};

use error::H5Result;

pub fn string_from_cstr(string: *const c_char) -> String {
    unsafe {
        String::from_utf8_unchecked(CStr::from_ptr(string).to_bytes().to_vec())
    }
}

pub fn string_to_cstr(string: String) -> *const c_char {
    unsafe {
        CString::from_vec_unchecked(string.into_bytes()).as_ptr()
    }
}

pub fn get_h5_str<T, F>(func: F) -> H5Result<String>
                 where F: Fn(*mut c_char, size_t) -> T, T: Integer + NumCast {
    unsafe {
        let len: isize = 1 + cast::<T, isize>(func(ptr::null_mut::<c_char>(), 0)).unwrap();
        ensure!(len > 0, "negative string length in get_h5_str()");
        let buf = libc::malloc((len as size_t) * mem::size_of::<c_char>() as size_t) as *mut c_char;
        func(buf, len as size_t);
        let msg = string_from_cstr(buf);
        libc::free(buf as *mut c_void);
        Ok(msg)
    }
}

#[test]
pub fn test_get_h5_str() {
    use ffi::h5e::{H5E_type_t, H5Eget_msg, H5E_CANTOPENOBJ};
    let s = unsafe {
        get_h5_str(|msg, size| {
            H5Eget_msg(*H5E_CANTOPENOBJ, ptr::null_mut::<H5E_type_t>(), msg, size)
        }).ok().unwrap()
    };
    assert_eq!(s, "Can't open object");
}
<|endoftext|>