<commit_before>use std::sync::Arc;

use num_complex::Complex;
use num_traits::Zero;

use common::{FFTnum, verify_length, verify_length_divisible};

use ::{Length, IsInverse, FFT};

/// Implementation of Bluestein's Algorithm
///
/// This algorithm computes a FFT of any size by using converting it to a convolution, where a longer power-of-two length FFT is used.
/// The primary use is for prime-sized FFTs of lengths where Rader's alorithm is slow.
///
/// ~~~
/// // Computes a forward FFT of size 1201 (prime number), using Bluesteins's Algorithm
/// use rustfft::algorithm::Bluesteins;
/// use rustfft::{FFT, FFTplanner};
/// use rustfft::num_complex::Complex;
/// use rustfft::num_traits::Zero;
///
/// let mut input:  Vec<Complex<f32>> = vec![Zero::zero(); 1201];
/// let mut output: Vec<Complex<f32>> = vec![Zero::zero(); 1201];
///
/// // plan a forward FFT for the nearest power of two larger or equal to 2 * 1201 - 1 -> 4096
/// let mut planner = FFTplanner::new(false);
/// let inner_fft_fw = planner.plan_fft(4096);
///
/// // plan an inverse FFT for the nearest power of two larger or equal to 2 * 1201 - 1 -> 4096
/// let mut planner = FFTplanner::new(true);
/// let inner_fft_inv = planner.plan_fft(4096);
///
/// let fft = Bluesteins::new(1201, inner_fft_fw, inner_fft_inv, false);
/// fft.process(&mut input, &mut output);
/// ~~~
///
/// Bluestein's algorithm is mainly useful for prime-length FFTs, and in particular for the lengths where
/// Rader's algorithm is slow due to hitting a Cunningham Chain.

pub struct Bluesteins<T> {
    len: usize,
    inner_fft_fw: Arc<FFT<T>>,
    inner_fft_inv: Arc<FFT<T>>,
    w_twiddles: Box<[Complex<T>]>,
    x_twiddles: Box<[Complex<T>]>,
    inverse: bool,
}

fn calculate_twiddle<T: FFTnum>(index: f64, len: usize) -> Complex<T> {
    let theta = index * core::f64::consts::PI / len as f64;
    Complex::new(
        T::from_f64(theta.cos()).unwrap(),
        T::from_f64(-theta.sin()).unwrap(),
    )
}

/// Calculate the "w" twiddles.
fn calculate_w_twiddles<T: FFTnum>(
    len: usize,
    fft: &Arc<FFT<T>>,
    twiddles: &mut [Complex<T>],
    inverse: bool,
) {
    let mut scratch = vec![Complex::zero(); fft.len()];
    let scale = T::one() / T::from_usize(fft.len()).unwrap();
    for (i, tw) in scratch.iter_mut().enumerate() {
        if let Some(index) = {
            if i < len {
                Some((i as f64).powi(2))
            } else if i > fft.len() - len {
                Some(((i as f64) - (fft.len() as f64)).powi(2))
            } else {
                None
            }
        } {
            let twiddle = calculate_twiddle(index, len)*scale;
            if inverse {
                *tw = twiddle;
            }
            else {
                *tw = twiddle.conj();
            }
        }
    }
    fft.process(&mut scratch, &mut twiddles[..]);
}

/// Calculate the "x" twiddles.
fn calculate_x_twiddles<T: FFTnum>(
    len: usize,
    twiddles: &mut [Complex<T>],
    inverse: bool,
) {
    if inverse {
        for (i, tw) in twiddles.iter_mut().enumerate() {
            *tw = calculate_twiddle(-(i as f64).powi(2), len);
        }
    }
    else {
        for (i, tw) in twiddles.iter_mut().enumerate() {
            *tw = calculate_twiddle(-(i as f64).powi(2), len).conj();
        }
    }
}

impl<T: FFTnum > Bluesteins<T> {
    /// Creates a FFT instance which will process inputs/outputs of size `len`. `inner_fft_fw.len()` and `inner_fft_inv.len()` must be the nearest
    /// power of two that is equal to or larger than `2 * len - 1`.
    ///
    /// Note that this constructor is quite expensive to run; This algorithm must run a FFT within the
    /// constructor.
    pub fn new(len: usize, inner_fft_fw: Arc<FFT<T>>, inner_fft_inv: Arc<FFT<T>>, inverse: bool) -> Self {
        let inner_fft_len = (2 * len - 1).checked_next_power_of_two().unwrap();
        assert_eq!(inner_fft_len, inner_fft_fw.len(), "For Bluesteins algorithm, inner_fft_fw.len() must be a power of to larger than or equal to 2*self.len() - 1. Expected {}, got {}", inner_fft_len, inner_fft_fw.len());
        assert_eq!(inner_fft_len, inner_fft_inv.len(), "For Bluesteins algorithm, inner_fft_inv.len() must be a power of to larger than or equal to 2*self.len() - 1. Expected {}, got {}", inner_fft_len, inner_fft_inv.len());

        let mut w_twiddles = vec![Complex::zero(); inner_fft_len];
        let mut x_twiddles = vec![Complex::zero(); len];
        calculate_w_twiddles(len, &inner_fft_fw, &mut w_twiddles, inverse);
        calculate_x_twiddles(len, &mut x_twiddles, inverse);
        Self {
            len,
            inner_fft_fw,
            inner_fft_inv,
            w_twiddles: w_twiddles.into_boxed_slice(),
            x_twiddles: x_twiddles.into_boxed_slice(),
            inverse,
        }
    }

    fn perform_fft(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        assert_eq!(self.len(), input.len());

        let mut scratch_a = vec![Complex::zero(); self.inner_fft_fw.len()];
        let mut scratch_b = vec![Complex::zero(); self.inner_fft_fw.len()];
        for (w, (x, i)) in scratch_a.iter_mut().zip(self.x_twiddles.iter().zip(input.iter())) {
            *w = x * i;
        }
        self.inner_fft_fw.process(&mut scratch_a, &mut scratch_b);
        for (w, wi) in scratch_b.iter_mut().zip(self.w_twiddles.iter()) {
            *w = *w * wi;
        }
        self.inner_fft_inv.process(&mut scratch_b, &mut scratch_a);
        for (i, (w, xi)) in output.iter_mut().zip(scratch_a.iter().zip(self.x_twiddles.iter())) {
            *i = w * xi;
        }
    }

}

impl<T: FFTnum> FFT<T> for Bluesteins<T> {
    fn process(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        verify_length(input, output, self.len());

        self.perform_fft(input, output);
    }
    fn process_multi(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        verify_length_divisible(input, output, self.len());

        for (in_chunk, out_chunk) in input.chunks_mut(self.len()).zip(output.chunks_mut(self.len())) {
            self.perform_fft(in_chunk, out_chunk);
        }
    }
}
impl<T> Length for Bluesteins<T> {
    #[inline(always)]
    fn len(&self) -> usize {
        self.len
    }
}
impl<T> IsInverse for Bluesteins<T> {
    #[inline(always)]
    fn is_inverse(&self) -> bool {
        self.inverse
    }
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use std::sync::Arc;
    use test_utils::check_fft_algorithm;
    use algorithm::DFT;

    #[test]
    fn test_bluestein() {
        for &len in &[3,5,7,11,13] {
            test_bluestein_with_length(len, false);
            test_bluestein_with_length(len, true);
        }
    }

    fn test_bluestein_with_length(len: usize, inverse: bool) {
        let inner_fft_len = (2 * len - 1).checked_next_power_of_two().unwrap();
        let inner_fft_fw = Arc::new(DFT::new(inner_fft_len, false));
        let inner_fft_inv = Arc::new(DFT::new(inner_fft_len, true));
        let fft = Bluesteins::new(len, inner_fft_fw, inner_fft_inv, inverse);

        check_fft_algorithm(&fft, len, inverse);
    }
}
<commit_msg>Add credits<commit_after>use std::sync::Arc;

use num_complex::Complex;
use num_traits::Zero;

use common::{FFTnum, verify_length, verify_length_divisible};

use ::{Length, IsInverse, FFT};

/// Implementation of Bluestein's Algorithm
///
/// This algorithm computes a FFT of any size by using converting it to a convolution, where a longer power-of-two length FFT is used.
/// The primary use is for prime-sized FFTs of lengths where Rader's alorithm is slow.
///
/// This implementation is based on the one in the [fourier library](https://github.com/calebzulawski/fourier) by Caleb Zulawski.
/// ~~~
/// // Computes a forward FFT of size 1201 (prime number), using Bluesteins's Algorithm
/// use rustfft::algorithm::Bluesteins;
/// use rustfft::{FFT, FFTplanner};
/// use rustfft::num_complex::Complex;
/// use rustfft::num_traits::Zero;
///
/// let mut input:  Vec<Complex<f32>> = vec![Zero::zero(); 1201];
/// let mut output: Vec<Complex<f32>> = vec![Zero::zero(); 1201];
///
/// // plan a forward FFT for the nearest power of two larger or equal to 2 * 1201 - 1 -> 4096
/// let mut planner = FFTplanner::new(false);
/// let inner_fft_fw = planner.plan_fft(4096);
///
/// // plan an inverse FFT for the nearest power of two larger or equal to 2 * 1201 - 1 -> 4096
/// let mut planner = FFTplanner::new(true);
/// let inner_fft_inv = planner.plan_fft(4096);
///
/// let fft = Bluesteins::new(1201, inner_fft_fw, inner_fft_inv, false);
/// fft.process(&mut input, &mut output);
/// ~~~
///
/// Bluestein's algorithm is mainly useful for prime-length FFTs, and in particular for the lengths where
/// Rader's algorithm is slow due to hitting a Cunningham Chain.

pub struct Bluesteins<T> {
    len: usize,
    inner_fft_fw: Arc<FFT<T>>,
    inner_fft_inv: Arc<FFT<T>>,
    w_twiddles: Box<[Complex<T>]>,
    x_twiddles: Box<[Complex<T>]>,
    inverse: bool,
}

fn calculate_twiddle<T: FFTnum>(index: f64, len: usize) -> Complex<T> {
    let theta = index * core::f64::consts::PI / len as f64;
    Complex::new(
        T::from_f64(theta.cos()).unwrap(),
        T::from_f64(-theta.sin()).unwrap(),
    )
}

/// Calculate the "w" twiddles.
fn calculate_w_twiddles<T: FFTnum>(
    len: usize,
    fft: &Arc<FFT<T>>,
    twiddles: &mut [Complex<T>],
    inverse: bool,
) {
    let mut scratch = vec![Complex::zero(); fft.len()];
    let scale = T::one() / T::from_usize(fft.len()).unwrap();
    for (i, tw) in scratch.iter_mut().enumerate() {
        if let Some(index) = {
            if i < len {
                Some((i as f64).powi(2))
            } else if i > fft.len() - len {
                Some(((i as f64) - (fft.len() as f64)).powi(2))
            } else {
                None
            }
        } {
            let twiddle = calculate_twiddle(index, len)*scale;
            if inverse {
                *tw = twiddle;
            }
            else {
                *tw = twiddle.conj();
            }
        }
    }
    fft.process(&mut scratch, &mut twiddles[..]);
}

/// Calculate the "x" twiddles.
fn calculate_x_twiddles<T: FFTnum>(
    len: usize,
    twiddles: &mut [Complex<T>],
    inverse: bool,
) {
    if inverse {
        for (i, tw) in twiddles.iter_mut().enumerate() {
            *tw = calculate_twiddle(-(i as f64).powi(2), len);
        }
    }
    else {
        for (i, tw) in twiddles.iter_mut().enumerate() {
            *tw = calculate_twiddle(-(i as f64).powi(2), len).conj();
        }
    }
}

impl<T: FFTnum > Bluesteins<T> {
    /// Creates a FFT instance which will process inputs/outputs of size `len`. `inner_fft_fw.len()` and `inner_fft_inv.len()` must be the nearest
    /// power of two that is equal to or larger than `2 * len - 1`.
    ///
    /// Note that this constructor is quite expensive to run; This algorithm must run a FFT within the
    /// constructor.
    pub fn new(len: usize, inner_fft_fw: Arc<FFT<T>>, inner_fft_inv: Arc<FFT<T>>, inverse: bool) -> Self {
        let inner_fft_len = (2 * len - 1).checked_next_power_of_two().unwrap();
        assert_eq!(inner_fft_len, inner_fft_fw.len(), "For Bluesteins algorithm, inner_fft_fw.len() must be a power of to larger than or equal to 2*self.len() - 1. Expected {}, got {}", inner_fft_len, inner_fft_fw.len());
        assert_eq!(inner_fft_len, inner_fft_inv.len(), "For Bluesteins algorithm, inner_fft_inv.len() must be a power of to larger than or equal to 2*self.len() - 1. Expected {}, got {}", inner_fft_len, inner_fft_inv.len());

        let mut w_twiddles = vec![Complex::zero(); inner_fft_len];
        let mut x_twiddles = vec![Complex::zero(); len];
        calculate_w_twiddles(len, &inner_fft_fw, &mut w_twiddles, inverse);
        calculate_x_twiddles(len, &mut x_twiddles, inverse);
        Self {
            len,
            inner_fft_fw,
            inner_fft_inv,
            w_twiddles: w_twiddles.into_boxed_slice(),
            x_twiddles: x_twiddles.into_boxed_slice(),
            inverse,
        }
    }

    fn perform_fft(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        assert_eq!(self.len(), input.len());

        let mut scratch_a = vec![Complex::zero(); self.inner_fft_fw.len()];
        let mut scratch_b = vec![Complex::zero(); self.inner_fft_fw.len()];
        for (w, (x, i)) in scratch_a.iter_mut().zip(self.x_twiddles.iter().zip(input.iter())) {
            *w = x * i;
        }
        self.inner_fft_fw.process(&mut scratch_a, &mut scratch_b);
        for (w, wi) in scratch_b.iter_mut().zip(self.w_twiddles.iter()) {
            *w = *w * wi;
        }
        self.inner_fft_inv.process(&mut scratch_b, &mut scratch_a);
        for (i, (w, xi)) in output.iter_mut().zip(scratch_a.iter().zip(self.x_twiddles.iter())) {
            *i = w * xi;
        }
    }

}

impl<T: FFTnum> FFT<T> for Bluesteins<T> {
    fn process(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        verify_length(input, output, self.len());

        self.perform_fft(input, output);
    }
    fn process_multi(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        verify_length_divisible(input, output, self.len());

        for (in_chunk, out_chunk) in input.chunks_mut(self.len()).zip(output.chunks_mut(self.len())) {
            self.perform_fft(in_chunk, out_chunk);
        }
    }
}
impl<T> Length for Bluesteins<T> {
    #[inline(always)]
    fn len(&self) -> usize {
        self.len
    }
}
impl<T> IsInverse for Bluesteins<T> {
    #[inline(always)]
    fn is_inverse(&self) -> bool {
        self.inverse
    }
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use std::sync::Arc;
    use test_utils::check_fft_algorithm;
    use algorithm::DFT;

    #[test]
    fn test_bluestein() {
        for &len in &[3,5,7,11,13] {
            test_bluestein_with_length(len, false);
            test_bluestein_with_length(len, true);
        }
    }

    fn test_bluestein_with_length(len: usize, inverse: bool) {
        let inner_fft_len = (2 * len - 1).checked_next_power_of_two().unwrap();
        let inner_fft_fw = Arc::new(DFT::new(inner_fft_len, false));
        let inner_fft_inv = Arc::new(DFT::new(inner_fft_len, true));
        let fft = Bluesteins::new(len, inner_fft_fw, inner_fft_inv, inverse);

        check_fft_algorithm(&fft, len, inverse);
    }
}
<|endoftext|>


<commit_before>use analysis::imports::Imports;
use analysis::ref_mode::RefMode;
use analysis::rust_type::*;
use analysis::signatures::{Signature, Signatures};
use config;
use config::gobjects::GObject;
use env::Env;
use library;
use nameutil;
use traits::*;
use version::Version;

#[derive(Debug)]
pub struct Property {
    pub name: String,
    pub var_name: String,
    pub typ: library::TypeId,
    pub is_get: bool,
    pub func_name: String,
    pub nullable: library::Nullable,
    pub conversion: PropertyConversion,
    pub default_value: Option<String>, //for getter
    pub get_out_ref_mode: RefMode,
    pub set_in_ref_mode: RefMode,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
}

pub fn analyze(env: &Env, props: &[library::Property], type_tid: library::TypeId,
               obj: &GObject, imports: &mut Imports, signatures: &Signatures)
               -> Vec<Property> {
    let mut properties = Vec::new();

    for prop in props {
        if prop.construct_only { continue; }
        let configured_properties = obj.properties.matched(&prop.name);
        if configured_properties.iter().any(|f| f.ignore) {
            continue;
        }

        if env.is_totally_deprecated(prop.deprecated_version) { continue; }

        let (getter, setter) = analyze_property(env, prop, type_tid, &configured_properties,
                                                signatures);
        if let Some(prop) = getter {
            if let Ok(s) = used_rust_type(env, prop.typ) {
                imports.add_used_type(&s, prop.version);
            }
            if prop.conversion != PropertyConversion::Direct {
                imports.add("std::mem::transmute", prop.version);
            }

            properties.push(prop);
        }
        if let Some(prop) = setter {
            if let Ok(s) = used_rust_type(env, prop.typ) {
                imports.add_used_type(&s, prop.version);
            }

            properties.push(prop);
        }
    }

    if !properties.is_empty() {
        imports.add("glib::object::IsA", None);
        imports.add("glib::Value", None);
        imports.add("gobject_ffi", None);
    }

    properties
}

fn analyze_property(env: &Env, prop: &library::Property, type_tid: library::TypeId,
                    configured_properties: &[&config::properties::Property],
                    signatures: &Signatures) -> (Option<Property>, Option<Property>) {
    let name = prop.name.clone();
    let type_ = env.type_(prop.typ);

    let prop_version = configured_properties.iter().filter_map(|f| f.version).min()
        .or(prop.version);
    let name_for_func = nameutil::signal_to_snake(&name);
    let var_name = nameutil::mangle_keywords(&*name_for_func).into_owned();
    let get_func_name = format!("get_property_{}", name_for_func);
    let set_func_name = format!("set_property_{}", name_for_func);
    let check_get_func_name = format!("get_{}", name_for_func);
    let check_set_func_name = format!("set_{}", name_for_func);

    let mut readable = prop.readable;
    let mut writable = prop.writable;
    if readable {
        let (has, version) = Signature::has_by_name(&check_get_func_name, signatures);
        if has {
            if env.is_totally_deprecated(version) || version <= prop_version {
                readable = false;
            }
        }
    }
    if writable {
        let (has, version) = Signature::has_by_name(&check_set_func_name, signatures);
        if has {
            if env.is_totally_deprecated(version) || version <= prop_version {
                writable = false;
            }
        }
    }

    let default_value = get_type_default_value(env, prop.typ, type_);
    if default_value.is_none() && readable {
        readable = false;
        let owner_name = rust_type(env, type_tid).into_string();
        error!("No default value for getter of property `{}` for `{}`", name, owner_name);
    }
    let conversion = PropertyConversion::of(type_);
    let get_out_ref_mode = RefMode::of(&env.library, prop.typ, library::ParameterDirection::Return);
    let mut set_in_ref_mode = RefMode::of(&env.library, prop.typ, library::ParameterDirection::In);
    if set_in_ref_mode == RefMode::ByRefMut {
        set_in_ref_mode = RefMode::ByRef;
    }
    let nullable = library::Nullable(set_in_ref_mode.is_ref());
    let getter = if readable {
        Some(Property{
            name: name.clone(),
            var_name: String::new(),
            typ: prop.typ,
            is_get: true,
            func_name: get_func_name,
            nullable: nullable,
            conversion: conversion,
            default_value: default_value,
            get_out_ref_mode: get_out_ref_mode,
            set_in_ref_mode: set_in_ref_mode,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
        })
    } else { None };

    let setter = if writable {
        Some(Property{
            name: name.clone(),
            var_name: var_name,
            typ: prop.typ,
            is_get: false,
            func_name: set_func_name,
            nullable: nullable,
            conversion: conversion,
            default_value: None,
            get_out_ref_mode: get_out_ref_mode,
            set_in_ref_mode: set_in_ref_mode,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
        })
    } else { None };

    (getter, setter)
}

pub fn get_type_default_value(env: &Env, type_tid: library::TypeId, type_: &library::Type)
                          -> Option<String> {
    use library::Type;
    use library::Fundamental;
    let some = |s: &str| Some(s.to_string());
    match *type_ {
        Type::Fundamental(fund) => {
            match fund {
                Fundamental::Boolean => some("&false"),
                Fundamental::Int => some("&0"),
                Fundamental::UInt => some("&0u32"),
                Fundamental::Utf8 => some("None::<&str>"),
                Fundamental::Float => some("&0f32"),
                Fundamental::Double => some("&0f64"),
                _ => None,
            }
        }
        Type::Bitfield(_) => some("&0u32"),
        Type::Enumeration(_) => some("&0"),
        Type::Class(..) |
        Type::Interface(..) => {
            let type_str = rust_type(env, type_tid).into_string();
            Some(format!("None::<&{}>", type_str))
        }
        _ => None,
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PropertyConversion {
    Direct,
    AsI32,
    Bitflag,
}

impl PropertyConversion {
    pub fn of(type_: &library::Type) -> PropertyConversion {
        use library::Type;
        use self::PropertyConversion::*;
        match *type_ {
            Type::Bitfield(_) => Bitflag,
            Type::Enumeration(_) => AsI32,
            _ => Direct,
        }
    }
}

impl Default for PropertyConversion {
    fn default() -> Self {
        PropertyConversion::Direct
    }
}
<commit_msg>Remove unused import in properties<commit_after>use analysis::imports::Imports;
use analysis::ref_mode::RefMode;
use analysis::rust_type::*;
use analysis::signatures::{Signature, Signatures};
use config;
use config::gobjects::GObject;
use env::Env;
use library;
use nameutil;
use traits::*;
use version::Version;

#[derive(Debug)]
pub struct Property {
    pub name: String,
    pub var_name: String,
    pub typ: library::TypeId,
    pub is_get: bool,
    pub func_name: String,
    pub nullable: library::Nullable,
    pub conversion: PropertyConversion,
    pub default_value: Option<String>, //for getter
    pub get_out_ref_mode: RefMode,
    pub set_in_ref_mode: RefMode,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
}

pub fn analyze(env: &Env, props: &[library::Property], type_tid: library::TypeId,
               obj: &GObject, imports: &mut Imports, signatures: &Signatures)
               -> Vec<Property> {
    let mut properties = Vec::new();

    for prop in props {
        if prop.construct_only { continue; }
        let configured_properties = obj.properties.matched(&prop.name);
        if configured_properties.iter().any(|f| f.ignore) {
            continue;
        }

        if env.is_totally_deprecated(prop.deprecated_version) { continue; }

        let (getter, setter) = analyze_property(env, prop, type_tid, &configured_properties,
                                                signatures);
        if getter.is_none() && setter.is_none() {
            continue;
        }

        let type_string = rust_type(env, prop.typ);
        let used_type_string = used_rust_type(env, prop.typ);
        if let Some(prop) = getter {
            if let Ok(ref s) = used_type_string {
                imports.add_used_type(&s, prop.version);
            }
            if prop.conversion != PropertyConversion::Direct {
                imports.add("std::mem::transmute", prop.version);
            }
            if type_string.is_ok() && prop.default_value.is_some() {
                imports.add("glib::Value", prop.version);
                imports.add("gobject_ffi", prop.version);
            }

            properties.push(prop);
        }
        if let Some(prop) = setter {
            if let Ok(ref s) = used_type_string {
                imports.add_used_type(&s, prop.version);
            }
            if type_string.is_ok() {
                imports.add("glib::Value", prop.version);
                imports.add("gobject_ffi", prop.version);
            }

            properties.push(prop);
        }
    }

    properties
}

fn analyze_property(env: &Env, prop: &library::Property, type_tid: library::TypeId,
                    configured_properties: &[&config::properties::Property],
                    signatures: &Signatures) -> (Option<Property>, Option<Property>) {
    let name = prop.name.clone();
    let type_ = env.type_(prop.typ);

    let prop_version = configured_properties.iter().filter_map(|f| f.version).min()
        .or(prop.version);
    let name_for_func = nameutil::signal_to_snake(&name);
    let var_name = nameutil::mangle_keywords(&*name_for_func).into_owned();
    let get_func_name = format!("get_property_{}", name_for_func);
    let set_func_name = format!("set_property_{}", name_for_func);
    let check_get_func_name = format!("get_{}", name_for_func);
    let check_set_func_name = format!("set_{}", name_for_func);

    let mut readable = prop.readable;
    let mut writable = prop.writable;
    if readable {
        let (has, version) = Signature::has_by_name(&check_get_func_name, signatures);
        if has {
            if env.is_totally_deprecated(version) || version <= prop_version {
                readable = false;
            }
        }
    }
    if writable {
        let (has, version) = Signature::has_by_name(&check_set_func_name, signatures);
        if has {
            if env.is_totally_deprecated(version) || version <= prop_version {
                writable = false;
            }
        }
    }

    let default_value = get_type_default_value(env, prop.typ, type_);
    if default_value.is_none() && readable {
        readable = false;
        let owner_name = rust_type(env, type_tid).into_string();
        error!("No default value for getter of property `{}` for `{}`", name, owner_name);
    }
    let conversion = PropertyConversion::of(type_);
    let get_out_ref_mode = RefMode::of(&env.library, prop.typ, library::ParameterDirection::Return);
    let mut set_in_ref_mode = RefMode::of(&env.library, prop.typ, library::ParameterDirection::In);
    if set_in_ref_mode == RefMode::ByRefMut {
        set_in_ref_mode = RefMode::ByRef;
    }
    let nullable = library::Nullable(set_in_ref_mode.is_ref());
    let getter = if readable {
        Some(Property{
            name: name.clone(),
            var_name: String::new(),
            typ: prop.typ,
            is_get: true,
            func_name: get_func_name,
            nullable: nullable,
            conversion: conversion,
            default_value: default_value,
            get_out_ref_mode: get_out_ref_mode,
            set_in_ref_mode: set_in_ref_mode,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
        })
    } else { None };

    let setter = if writable {
        Some(Property{
            name: name.clone(),
            var_name: var_name,
            typ: prop.typ,
            is_get: false,
            func_name: set_func_name,
            nullable: nullable,
            conversion: conversion,
            default_value: None,
            get_out_ref_mode: get_out_ref_mode,
            set_in_ref_mode: set_in_ref_mode,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
        })
    } else { None };

    (getter, setter)
}

pub fn get_type_default_value(env: &Env, type_tid: library::TypeId, type_: &library::Type)
                          -> Option<String> {
    use library::Type;
    use library::Fundamental;
    let some = |s: &str| Some(s.to_string());
    match *type_ {
        Type::Fundamental(fund) => {
            match fund {
                Fundamental::Boolean => some("&false"),
                Fundamental::Int => some("&0"),
                Fundamental::UInt => some("&0u32"),
                Fundamental::Utf8 => some("None::<&str>"),
                Fundamental::Float => some("&0f32"),
                Fundamental::Double => some("&0f64"),
                _ => None,
            }
        }
        Type::Bitfield(_) => some("&0u32"),
        Type::Enumeration(_) => some("&0"),
        Type::Class(..) |
        Type::Interface(..) => {
            let type_str = rust_type(env, type_tid).into_string();
            Some(format!("None::<&{}>", type_str))
        }
        _ => None,
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PropertyConversion {
    Direct,
    AsI32,
    Bitflag,
}

impl PropertyConversion {
    pub fn of(type_: &library::Type) -> PropertyConversion {
        use library::Type;
        use self::PropertyConversion::*;
        match *type_ {
            Type::Bitfield(_) => Bitflag,
            Type::Enumeration(_) => AsI32,
            _ => Direct,
        }
    }
}

impl Default for PropertyConversion {
    fn default() -> Self {
        PropertyConversion::Direct
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use rustc_hash::FxHashSet;

use syntax::{
    ast::{self, AstNode, AstToken, VisibilityOwner},
    Direction, NodeOrToken, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, TextRange, TextSize,
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Mods,
    Block,
    ArgList,
    Region,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();
    let mut visited_mods = FxHashSet::default();
    // regions can be nested, here is a LIFO buffer
    let mut regions_starts: Vec<TextSize> = vec![];

    for element in file.syntax().descendants_with_tokens() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(element.kind()) {
            let is_multiline = match &element {
                NodeOrToken::Node(node) => node.text().contains_char('\n'),
                NodeOrToken::Token(token) => token.text().contains('\n'),
            };
            if is_multiline {
                res.push(Fold { range: element.text_range(), kind });
                continue;
            }
        }

        match element {
            NodeOrToken::Token(token) => {
                // Fold groups of comments
                if let Some(comment) = ast::Comment::cast(token) {
                    if !visited_comments.contains(&comment) {
                        // regions are not real comments
                        if comment.text().trim().starts_with("// region:") {
                            regions_starts.push(comment.syntax().text_range().start());
                        } else if comment.text().trim().starts_with("// endregion") {
                            if !regions_starts.is_empty() {
                                res.push(Fold {
                                    range: TextRange::new(
                                        regions_starts.pop().unwrap(),
                                        comment.syntax().text_range().end(),
                                    ),
                                    kind: FoldKind::Region,
                                })
                            }
                        } else {
                            if let Some(range) =
                                contiguous_range_for_comment(comment, &mut visited_comments)
                            {
                                res.push(Fold { range, kind: FoldKind::Comment })
                            }
                        }
                    }
                }
            }
            NodeOrToken::Node(node) => {
                // Fold groups of imports
                if node.kind() == USE && !visited_imports.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
                        res.push(Fold { range, kind: FoldKind::Imports })
                    }
                }

                // Fold groups of mods
                if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node)
                {
                    if let Some(range) =
                        contiguous_range_for_group_unless(&node, has_visibility, &mut visited_mods)
                    {
                        res.push(Fold { range, kind: FoldKind::Mods })
                    }
                }
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
        ASSOC_ITEM_LIST
        | RECORD_FIELD_LIST
        | RECORD_PAT_FIELD_LIST
        | RECORD_EXPR_FIELD_LIST
        | ITEM_LIST
        | EXTERN_ITEM_LIST
        | USE_TREE_LIST
        | BLOCK_EXPR
        | MATCH_ARM_LIST
        | VARIANT_LIST
        | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_visibility(node: &SyntaxNode) -> bool {
    ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
}

fn contiguous_range_for_group(
    first: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    contiguous_range_for_group_unless(first, |_| false, visited)
}

fn contiguous_range_for_group_unless(
    first: &SyntaxNode,
    unless: impl Fn(&SyntaxNode) -> bool,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    let mut last = first.clone();
    for element in first.siblings_with_tokens(Direction::Next) {
        let node = match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                // There is a blank line or another token, which means that the
                // group ends here
                break;
            }
            NodeOrToken::Node(node) => node,
        };

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() || unless(&node) {
            break;
        }

        visited.insert(node.clone());
        last = node;
    }

    if first != &last {
        Some(TextRange::new(first.text_range().start(), last.text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment(
    first: ast::Comment,
    visited: &mut FxHashSet<ast::Comment>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    // Only fold comments of the same flavor
    let group_kind = first.kind();
    if !group_kind.shape.is_line() {
        return None;
    }

    let mut last = first.clone();
    for element in first.syntax().siblings_with_tokens(Direction::Next) {
        match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token.clone()) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                if let Some(c) = ast::Comment::cast(token) {
                    if c.kind() == group_kind {
                        // regions are not real comments
                        if c.text().trim().starts_with("// region:")
                            || c.text().trim().starts_with("// endregion")
                        {
                            break;
                        } else {
                            visited.insert(c.clone());
                            last = c;
                            continue;
                        }
                    }
                }
                // The comment group ends because either:
                // * An element of a different kind was reached
                // * A comment of a different flavor was reached
                break;
            }
            NodeOrToken::Node(_) => break,
        };
    }

    if first != last {
        Some(TextRange::new(first.syntax().text_range().start(), last.syntax().text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

#[cfg(test)]
mod tests {
    use test_utils::extract_tags;

    use super::*;

    fn check(ra_fixture: &str) {
        let (ranges, text) = extract_tags(ra_fixture, "fold");

        let parse = SourceFile::parse(&text);
        let folds = folding_ranges(&parse.tree());
        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );

        for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());

            let kind = match fold.kind {
                FoldKind::Comment => "comment",
                FoldKind::Imports => "imports",
                FoldKind::Mods => "mods",
                FoldKind::Block => "block",
                FoldKind::ArgList => "arglist",
                FoldKind::Region => "region",
            };
            assert_eq!(kind, &attr.unwrap());
        }
    }

    #[test]
    fn test_fold_comments() {
        check(
            r#"
<fold comment>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold block>{
    <fold comment>// We should
    // also
    // fold
    // this one.</fold>
    <fold comment>//! But this one is different
    //! because it has another flavor</fold>
    <fold comment>/* As does this
    multiline comment */</fold>
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_imports() {
        check(
            r#"
use std::<fold block>{
    str,
    vec,
    io as iop
}</fold>;

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_mods() {
        check(
            r#"

pub mod foo;
<fold mods>mod after_pub;
mod after_pub_next;</fold>

<fold mods>mod before_pub;
mod before_pub_next;</fold>
pub mod bar;

mod not_folding_single;
pub mod foobar;
pub not_folding_single_next;

<fold mods>#[cfg(test)]
mod with_attribute;
mod with_attribute_next;</fold>

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_import_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

<fold imports>use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;</fold>

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_import_and_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

use std::collections::<fold block>{
    HashMap,
    VecDeque,
}</fold>;
// Some random comment

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_folds_structs() {
        check(
            r#"
struct Foo <fold block>{
}</fold>
"#,
        );
    }

    #[test]
    fn test_folds_traits() {
        check(
            r#"
trait Foo <fold block>{
}</fold>
"#,
        );
    }

    #[test]
    fn test_folds_macros() {
        check(
            r#"
macro_rules! foo <fold block>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_match_arms() {
        check(
            r#"
fn main() <fold block>{
    match 0 <fold block>{
        0 => 0,
        _ => 1,
    }</fold>
}</fold>
"#,
        );
    }

    #[test]
    fn fold_big_calls() {
        check(
            r#"
fn main() <fold block>{
    frobnicate<fold arglist>(
        1,
        2,
        3,
    )</fold>
}</fold>
"#,
        )
    }

    #[test]
    fn fold_record_literals() {
        check(
            r#"
const _: S = S <fold block>{

}</fold>;
"#,
        )
    }

    #[test]
    fn fold_multiline_params() {
        check(
            r#"
fn foo<fold arglist>(
    x: i32,
    y: String,
)</fold> {}
"#,
        )
    }

    #[test]
    fn fold_region() {
        log_init_for_test_debug();
        // only error level log is printed on the terminal
        log::error!("test fold_region");
        check(
            r#"
// 1. some normal comment
<fold region>// region: test
// 2. some normal comment
calling_function(x,y);
// endregion: test</fold>
"#,
        )
    }

    fn log_init_for_test_debug() {
        let _ = env_logger::builder().is_test(true).try_init();
    }
}
<commit_msg>Update crates/ide/src/folding_ranges.rs<commit_after>//! FIXME: write short doc here

use rustc_hash::FxHashSet;

use syntax::{
    ast::{self, AstNode, AstToken, VisibilityOwner},
    Direction, NodeOrToken, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, TextRange, TextSize,
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Mods,
    Block,
    ArgList,
    Region,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();
    let mut visited_mods = FxHashSet::default();
    // regions can be nested, here is a LIFO buffer
    let mut regions_starts: Vec<TextSize> = vec![];

    for element in file.syntax().descendants_with_tokens() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(element.kind()) {
            let is_multiline = match &element {
                NodeOrToken::Node(node) => node.text().contains_char('\n'),
                NodeOrToken::Token(token) => token.text().contains('\n'),
            };
            if is_multiline {
                res.push(Fold { range: element.text_range(), kind });
                continue;
            }
        }

        match element {
            NodeOrToken::Token(token) => {
                // Fold groups of comments
                if let Some(comment) = ast::Comment::cast(token) {
                    if !visited_comments.contains(&comment) {
                        // regions are not real comments
                        if comment.text().trim().starts_with("// region:") {
                            regions_starts.push(comment.syntax().text_range().start());
                        } else if comment.text().trim().starts_with("// endregion") {
                            if let Some(region) = regions_starts.pop() {
                                res.push(Fold {
                                    range: TextRange::new(
                                        region,
                                        comment.syntax().text_range().end(),
                                    ),
                                    kind: FoldKind::Region,
                                })
                            }
                        } else {
                            if let Some(range) =
                                contiguous_range_for_comment(comment, &mut visited_comments)
                            {
                                res.push(Fold { range, kind: FoldKind::Comment })
                            }
                        }
                    }
                }
            }
            NodeOrToken::Node(node) => {
                // Fold groups of imports
                if node.kind() == USE && !visited_imports.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
                        res.push(Fold { range, kind: FoldKind::Imports })
                    }
                }

                // Fold groups of mods
                if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node)
                {
                    if let Some(range) =
                        contiguous_range_for_group_unless(&node, has_visibility, &mut visited_mods)
                    {
                        res.push(Fold { range, kind: FoldKind::Mods })
                    }
                }
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
        ASSOC_ITEM_LIST
        | RECORD_FIELD_LIST
        | RECORD_PAT_FIELD_LIST
        | RECORD_EXPR_FIELD_LIST
        | ITEM_LIST
        | EXTERN_ITEM_LIST
        | USE_TREE_LIST
        | BLOCK_EXPR
        | MATCH_ARM_LIST
        | VARIANT_LIST
        | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_visibility(node: &SyntaxNode) -> bool {
    ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
}

fn contiguous_range_for_group(
    first: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    contiguous_range_for_group_unless(first, |_| false, visited)
}

fn contiguous_range_for_group_unless(
    first: &SyntaxNode,
    unless: impl Fn(&SyntaxNode) -> bool,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    let mut last = first.clone();
    for element in first.siblings_with_tokens(Direction::Next) {
        let node = match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                // There is a blank line or another token, which means that the
                // group ends here
                break;
            }
            NodeOrToken::Node(node) => node,
        };

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() || unless(&node) {
            break;
        }

        visited.insert(node.clone());
        last = node;
    }

    if first != &last {
        Some(TextRange::new(first.text_range().start(), last.text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment(
    first: ast::Comment,
    visited: &mut FxHashSet<ast::Comment>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    // Only fold comments of the same flavor
    let group_kind = first.kind();
    if !group_kind.shape.is_line() {
        return None;
    }

    let mut last = first.clone();
    for element in first.syntax().siblings_with_tokens(Direction::Next) {
        match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token.clone()) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                if let Some(c) = ast::Comment::cast(token) {
                    if c.kind() == group_kind {
                        // regions are not real comments
                        if c.text().trim().starts_with("// region:")
                            || c.text().trim().starts_with("// endregion")
                        {
                            break;
                        } else {
                            visited.insert(c.clone());
                            last = c;
                            continue;
                        }
                    }
                }
                // The comment group ends because either:
                // * An element of a different kind was reached
                // * A comment of a different flavor was reached
                break;
            }
            NodeOrToken::Node(_) => break,
        };
    }

    if first != last {
        Some(TextRange::new(first.syntax().text_range().start(), last.syntax().text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

#[cfg(test)]
mod tests {
    use test_utils::extract_tags;

    use super::*;

    fn check(ra_fixture: &str) {
        let (ranges, text) = extract_tags(ra_fixture, "fold");

        let parse = SourceFile::parse(&text);
        let folds = folding_ranges(&parse.tree());
        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );

        for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());

            let kind = match fold.kind {
                FoldKind::Comment => "comment",
                FoldKind::Imports => "imports",
                FoldKind::Mods => "mods",
                FoldKind::Block => "block",
                FoldKind::ArgList => "arglist",
                FoldKind::Region => "region",
            };
            assert_eq!(kind, &attr.unwrap());
        }
    }

    #[test]
    fn test_fold_comments() {
        check(
            r#"
<fold comment>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold block>{
    <fold comment>// We should
    // also
    // fold
    // this one.</fold>
    <fold comment>//! But this one is different
    //! because it has another flavor</fold>
    <fold comment>/* As does this
    multiline comment */</fold>
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_imports() {
        check(
            r#"
use std::<fold block>{
    str,
    vec,
    io as iop
}</fold>;

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_mods() {
        check(
            r#"

pub mod foo;
<fold mods>mod after_pub;
mod after_pub_next;</fold>

<fold mods>mod before_pub;
mod before_pub_next;</fold>
pub mod bar;

mod not_folding_single;
pub mod foobar;
pub not_folding_single_next;

<fold mods>#[cfg(test)]
mod with_attribute;
mod with_attribute_next;</fold>

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_import_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

<fold imports>use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;</fold>

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_import_and_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

use std::collections::<fold block>{
    HashMap,
    VecDeque,
}</fold>;
// Some random comment

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_folds_structs() {
        check(
            r#"
struct Foo <fold block>{
}</fold>
"#,
        );
    }

    #[test]
    fn test_folds_traits() {
        check(
            r#"
trait Foo <fold block>{
}</fold>
"#,
        );
    }

    #[test]
    fn test_folds_macros() {
        check(
            r#"
macro_rules! foo <fold block>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_match_arms() {
        check(
            r#"
fn main() <fold block>{
    match 0 <fold block>{
        0 => 0,
        _ => 1,
    }</fold>
}</fold>
"#,
        );
    }

    #[test]
    fn fold_big_calls() {
        check(
            r#"
fn main() <fold block>{
    frobnicate<fold arglist>(
        1,
        2,
        3,
    )</fold>
}</fold>
"#,
        )
    }

    #[test]
    fn fold_record_literals() {
        check(
            r#"
const _: S = S <fold block>{

}</fold>;
"#,
        )
    }

    #[test]
    fn fold_multiline_params() {
        check(
            r#"
fn foo<fold arglist>(
    x: i32,
    y: String,
)</fold> {}
"#,
        )
    }

    #[test]
    fn fold_region() {
        log_init_for_test_debug();
        // only error level log is printed on the terminal
        log::error!("test fold_region");
        check(
            r#"
// 1. some normal comment
<fold region>// region: test
// 2. some normal comment
calling_function(x,y);
// endregion: test</fold>
"#,
        )
    }

    fn log_init_for_test_debug() {
        let _ = env_logger::builder().is_test(true).try_init();
    }
}
<|endoftext|>


<commit_before>//! Finds a corresponding hir data structure for a syntax node in a specific
//! file.

use hir_def::{
    child_by_source::ChildBySource, dyn_map::DynMap, keys, keys::Key, nameres::ModuleSource,
    ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
    StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
};
use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
use ra_db::FileId;
use ra_prof::profile;
use ra_syntax::{
    ast::{self, AstNode, NameOwner},
    match_ast, SyntaxNode,
};

use crate::{
    db::{DefDatabase, HirDatabase},
    Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
    MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union,
};

pub(crate) trait FromSource: Sized {
    type Ast;
    fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self>;
}

pub trait FromSourceByContainer: Sized {
    type Ast: AstNode + 'static;
    type Id: Copy + 'static;
    const KEY: Key<Self::Ast, Self::Id>;
}

impl<T: FromSourceByContainer> FromSource for T
where
    T: From<<T as FromSourceByContainer>::Id>,
{
    type Ast = <T as FromSourceByContainer>::Ast;
    fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
        analyze_container(db, src.as_ref().map(|it| it.syntax()))[T::KEY]
            .get(&src)
            .copied()
            .map(Self::from)
    }
}

macro_rules! from_source_by_container_impls {
    ($(($hir:ident, $id:ident, $ast:path, $key:path)),* ,) => {$(
        impl FromSourceByContainer for $hir {
            type Ast = $ast;
            type Id = $id;
            const KEY: Key<Self::Ast, Self::Id> = $key;
        }
    )*}
}

from_source_by_container_impls![
    (Struct, StructId, ast::StructDef, keys::STRUCT),
    (Union, UnionId, ast::UnionDef, keys::UNION),
    (Enum, EnumId, ast::EnumDef, keys::ENUM),
    (Trait, TraitId, ast::TraitDef, keys::TRAIT),
    (Function, FunctionId, ast::FnDef, keys::FUNCTION),
    (Static, StaticId, ast::StaticDef, keys::STATIC),
    (Const, ConstId, ast::ConstDef, keys::CONST),
    (TypeAlias, TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS),
    (ImplBlock, ImplId, ast::ImplBlock, keys::IMPL),
];

impl FromSource for MacroDef {
    type Ast = ast::MacroCall;
    fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
        let kind = MacroDefKind::Declarative;

        let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
        let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
        let krate = Some(module.krate().id);

        let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));

        let id: MacroDefId = MacroDefId { krate, ast_id, kind };
        Some(MacroDef { id })
    }
}

impl FromSource for EnumVariant {
    type Ast = ast::EnumVariant;
    fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
        let parent_enum = src.value.parent_enum();
        let src_enum = InFile { file_id: src.file_id, value: parent_enum };
        let parent_enum = Enum::from_source(db, src_enum)?;
        parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT]
            .get(&src)
            .copied()
            .map(EnumVariant::from)
    }
}

impl FromSource for StructField {
    type Ast = FieldSource;
    fn from_source(db: &impl DefDatabase, src: InFile<Self::Ast>) -> Option<Self> {
        let src = src.as_ref();

        // FIXME this is buggy
        let variant_id: VariantId = match src.value {
            FieldSource::Named(field) => {
                let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = Struct::from_source(db, src)?;
                def.id.into()
            }
            FieldSource::Pos(field) => {
                let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
                let src = InFile { file_id: src.file_id, value };
                let def = EnumVariant::from_source(db, src)?;
                EnumVariantId::from(def).into()
            }
        };

        let dyn_map = variant_id.child_by_source(db);
        match src.value {
            FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
            FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
        }
        .copied()
        .map(StructField::from)
    }
}

impl Local {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
        let file_id = src.file_id;
        let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() },
                    ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() },
                    ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let (_body, source_map) = db.body_with_source_map(parent.into());
        let src = src.map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        Some(Local { parent, pat_id })
    }
}

impl TypeParam {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> {
        let file_id = src.file_id;
        let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::StructDef(value) => { Struct::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::EnumDef(value) => { Enum::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::TraitDef(value) => { Trait::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::TypeAliasDef(value) => { TypeAlias::from_source(db, InFile { value, file_id})?.id.into() },
                    ast::ImplBlock(value) => { ImplBlock::from_source(db, InFile { value, file_id})?.id.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?;
        Some(TypeParam { id })
    }
}

impl Module {
    pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
        let _p = profile("Module::from_declaration");
        let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);

        let parent_module = match parent_declaration {
            Some(parent_declaration) => {
                let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
                Module::from_declaration(db, src_parent)
            }
            None => {
                let source_file = db.parse(src.file_id.original_file(db)).tree();
                let src_parent =
                    InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
                Module::from_definition(db, src_parent)
            }
        }?;

        let child_name = src.value.name()?.as_name();
        let def_map = db.crate_def_map(parent_module.id.krate);
        let child_id = def_map[parent_module.id.local_id].children.get(&child_name)?;
        Some(parent_module.with_module_id(*child_id))
    }

    pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
        let _p = profile("Module::from_definition");
        match src.value {
            ModuleSource::Module(ref module) => {
                assert!(!module.has_semi());
                return Module::from_declaration(
                    db,
                    InFile { file_id: src.file_id, value: module.clone() },
                );
            }
            ModuleSource::SourceFile(_) => (),
        };

        let original_file = src.file_id.original_file(db);
        Module::from_file(db, original_file)
    }

    fn from_file(db: &impl DefDatabase, file: FileId) -> Option<Self> {
        let _p = profile("Module::from_file");
        let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| {
            let crate_def_map = db.crate_def_map(crate_id);
            let local_id = crate_def_map.modules_for_file(file).next()?;
            Some((crate_id, local_id))
        })?;
        Some(Module { id: ModuleId { krate, local_id } })
    }
}

fn analyze_container(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> DynMap {
    let _p = profile("analyze_container");
    return child_by_source(db, src).unwrap_or_default();

    fn child_by_source(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<DynMap> {
        for container in src.value.ancestors().skip(1) {
            let res = match_ast! {
                match container {
                    ast::TraitDef(it) => {
                        let def = Trait::from_source(db, src.with_value(it))?;
                        def.id.child_by_source(db)
                    },
                    ast::ImplBlock(it) => {
                        let def = ImplBlock::from_source(db, src.with_value(it))?;
                        def.id.child_by_source(db)
                    },
                    ast::FnDef(it) => {
                        let def = Function::from_source(db, src.with_value(it))?;
                        DefWithBodyId::from(def.id)
                            .child_by_source(db)
                    },
                    ast::StaticDef(it) => {
                        let def = Static::from_source(db, src.with_value(it))?;
                        DefWithBodyId::from(def.id)
                            .child_by_source(db)
                    },
                    ast::ConstDef(it) => {
                        let def = Const::from_source(db, src.with_value(it))?;
                        DefWithBodyId::from(def.id)
                            .child_by_source(db)
                    },
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let module_source = ModuleSource::from_child_node(db, src);
        let c = Module::from_definition(db, src.with_value(module_source))?;
        Some(c.id.child_by_source(db))
    }
}
<commit_msg>Removed FromSource<commit_after>//! Finds a corresponding hir data structure for a syntax node in a specific
//! file.

use hir_def::{
    child_by_source::ChildBySource, keys, nameres::ModuleSource, GenericDefId, ModuleId,
};
use hir_expand::name::AsName;
use ra_db::FileId;
use ra_prof::profile;
use ra_syntax::{
    ast::{self, AstNode, NameOwner},
    match_ast,
};

use crate::{
    db::{DefDatabase, HirDatabase},
    Const, DefWithBody, Enum, Function, ImplBlock, InFile, Local, Module, SourceBinder, Static,
    Struct, Trait, TypeAlias, TypeParam,
};

impl Local {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
        let mut sb = SourceBinder::new(db);
        let file_id = src.file_id;
        let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::ConstDef(value) => { sb.to_def::<Const, _>(InFile { value, file_id})?.into() },
                    ast::StaticDef(value) => { sb.to_def::<Static, _>(InFile { value, file_id})?.into() },
                    ast::FnDef(value) => { sb.to_def::<Function, _>(InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let (_body, source_map) = db.body_with_source_map(parent.into());
        let src = src.map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        Some(Local { parent, pat_id })
    }
}

impl TypeParam {
    pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> {
        let mut sb = SourceBinder::new(db);
        let file_id = src.file_id;
        let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::FnDef(value) => { sb.to_def::<Function, _>(InFile { value, file_id})?.id.into() },
                    ast::StructDef(value) => { sb.to_def::<Struct, _>(InFile { value, file_id})?.id.into() },
                    ast::EnumDef(value) => { sb.to_def::<Enum, _>(InFile { value, file_id})?.id.into() },
                    ast::TraitDef(value) => { sb.to_def::<Trait, _>(InFile { value, file_id})?.id.into() },
                    ast::TypeAliasDef(value) => { sb.to_def::<TypeAlias, _>(InFile { value, file_id})?.id.into() },
                    ast::ImplBlock(value) => { sb.to_def::<ImplBlock, _>(InFile { value, file_id})?.id.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?;
        Some(TypeParam { id })
    }
}

impl Module {
    pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
        let _p = profile("Module::from_declaration");
        let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);

        let parent_module = match parent_declaration {
            Some(parent_declaration) => {
                let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
                Module::from_declaration(db, src_parent)
            }
            None => {
                let source_file = db.parse(src.file_id.original_file(db)).tree();
                let src_parent =
                    InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
                Module::from_definition(db, src_parent)
            }
        }?;

        let child_name = src.value.name()?.as_name();
        let def_map = db.crate_def_map(parent_module.id.krate);
        let child_id = def_map[parent_module.id.local_id].children.get(&child_name)?;
        Some(parent_module.with_module_id(*child_id))
    }

    pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
        let _p = profile("Module::from_definition");
        match src.value {
            ModuleSource::Module(ref module) => {
                assert!(!module.has_semi());
                return Module::from_declaration(
                    db,
                    InFile { file_id: src.file_id, value: module.clone() },
                );
            }
            ModuleSource::SourceFile(_) => (),
        };

        let original_file = src.file_id.original_file(db);
        Module::from_file(db, original_file)
    }

    fn from_file(db: &impl DefDatabase, file: FileId) -> Option<Self> {
        let _p = profile("Module::from_file");
        let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| {
            let crate_def_map = db.crate_def_map(crate_id);
            let local_id = crate_def_map.modules_for_file(file).next()?;
            Some((crate_id, local_id))
        })?;
        Some(Module { id: ModuleId { krate, local_id } })
    }
}
<|endoftext|>


<commit_before>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) Ben Segall <talchas@gmail.com>
// Copyright (c) Nathan Zadoks <nathan@nathan7.eu>
// See the LICENSE file included in this distribution.
#![cfg(target_os = "linux")]
#![feature(test)]
#![feature(thread_local)]
#![feature(asm)]
extern crate fringe;
extern crate test;
use fringe::Context;
use test::black_box;

#[thread_local]
static mut ctx_slot: *mut Context<fringe::OsStack> = 0 as *mut Context<_>;

const FE_DIVBYZERO: i32 = 0x4;
extern {
  fn feenableexcept(except: i32) -> i32;
}

#[test]
#[ignore]
fn fpe() {
  unsafe extern "C" fn universe_destroyer(_arg: usize) -> ! {
    loop {
        println!("{:?}", 1.0/black_box(0.0));
        Context::swap(ctx_slot, ctx_slot, 0);
    }
  }

  unsafe {
    let stack = fringe::OsStack::new(4 << 20).unwrap();
    let mut ctx = Context::new(stack, universe_destroyer);
    ctx_slot = &mut ctx;

    Context::swap(ctx_slot, ctx_slot, 0);
    feenableexcept(FE_DIVBYZERO);
    Context::swap(ctx_slot, ctx_slot, 0);
  }
}
<commit_msg>Rewrite the FPE test to not use Context directly<commit_after>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) Ben Segall <talchas@gmail.com>
// Copyright (c) Nathan Zadoks <nathan@nathan7.eu>
// See the LICENSE file included in this distribution.
#![cfg(target_os = "linux")]
#![feature(test)]
#![feature(thread_local)]
#![feature(asm)]
extern crate fringe;
extern crate test;
use fringe::{OsStack, Generator};
use test::black_box;

const FE_DIVBYZERO: i32 = 0x4;
extern {
  fn feenableexcept(except: i32) -> i32;
}

#[test]
#[ignore]
fn fpe() {
  let stack = OsStack::new(0).unwrap();
  let mut gen = Generator::new(stack, move |yielder| {
    yielder.generate(1.0 / black_box(0.0));
  });

  unsafe { feenableexcept(FE_DIVBYZERO); }
  println!("{:?}", gen.next());
}
<|endoftext|>


<commit_before>extern crate pinyin;

struct TestCase {
    hans: String,
    args: pinyin::Args,
    result: Vec<Vec<String>>,
    lazy_result: Vec<String>,
}

impl TestCase {
    pub fn new(
        hans: String,
        args: pinyin::Args,
        result: Vec<Vec<String>>,
        lazy_result: Vec<String>,
    ) -> TestCase {
        TestCase {
            hans,
            args,
            result,
            lazy_result,
        }
    }
}

#[test]
fn test_pinyin() {
    let test_data = vec![
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args::new(),
            vec![
                vec!["zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Normal,
                heteronym: false,
            },
            vec![
                vec!["zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Tone,
                heteronym: false,
            },
            vec![
                vec!["zhōng".to_string()],
                vec!["guó".to_string()],
                vec!["rén".to_string()],
            ],
            vec!["zhōng".to_string(), "guó".to_string(), "rén".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Tone2,
                heteronym: false,
            },
            vec![
                vec!["zho1ng".to_string()],
                vec!["guo2".to_string()],
                vec!["re2n".to_string()],
            ],
            vec!["zho1ng".to_string(), "guo2".to_string(), "re2n".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Initials,
                heteronym: false,
            },
            vec![
                vec!["zh".to_string()],
                vec!["g".to_string()],
                vec!["r".to_string()],
            ],
            vec!["zh".to_string(), "g".to_string(), "r".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FirstLetter,
                heteronym: false,
            },
            vec![
                vec!["z".to_string()],
                vec!["g".to_string()],
                vec!["r".to_string()],
            ],
            vec!["z".to_string(), "g".to_string(), "r".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Finals,
                heteronym: false,
            },
            vec![
                vec!["ong".to_string()],
                vec!["uo".to_string()],
                vec!["en".to_string()],
            ],
            vec!["ong".to_string(), "uo".to_string(), "en".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FinalsTone,
                heteronym: false,
            },
            vec![
                vec!["ōng".to_string()],
                vec!["uó".to_string()],
                vec!["én".to_string()],
            ],
            vec!["ōng".to_string(), "uó".to_string(), "én".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FinalsTone2,
                heteronym: false,
            },
            vec![
                vec!["o1ng".to_string()],
                vec!["uo2".to_string()],
                vec!["e2n".to_string()],
            ],
            vec!["o1ng".to_string(), "uo2".to_string(), "e2n".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Normal,
                heteronym: true,
            },
            vec![
                vec!["zhong".to_string(), "zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
    ];
    for data in &test_data {
        assert_eq!(data.result, pinyin::pinyin(&data.hans, &data.args));
        assert_eq!(
            data.lazy_result,
            pinyin::lazy_pinyin(&data.hans, &data.args)
        );
    }
}

#[test]
fn test_non_chinese_pinyin() {
    let hans = "中国人abc你好";
    let expect = vec![
        vec!["zhong".to_string()],
        vec!["guo".to_string()],
        vec!["ren".to_string()],
        vec![],
        vec![],
        vec![],
        vec!["ni".to_string()],
        vec!["hao".to_string()],
    ];
    let result = pinyin::pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_non_chinese_lazy_pinyin() {
    let hans = "中国人abc你好";
    let expect = vec![
        "zhong".to_string(),
        "guo".to_string(),
        "ren".to_string(),
        "ni".to_string(),
        "hao".to_string(),
    ];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_new_args() {
    let args = pinyin::Args::new();
    assert_eq!(pinyin::Style::Normal, args.style);
    assert_eq!(false, args.heteronym);
}

#[test]
fn test_default_args() {
    let args = pinyin::Args::default();
    assert_eq!(pinyin::Style::Normal, args.style);
    assert_eq!(false, args.heteronym);
}

#[test]
fn test_no_initial() {
    let hans = "安";
    let expect = vec!["an".to_string()];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_no_phonetic_symbol() {
    let hans = "啊";
    let expect = vec!["a".to_string()];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}
<commit_msg>add more tests<commit_after>extern crate pinyin;

struct TestCase {
    hans: String,
    args: pinyin::Args,
    result: Vec<Vec<String>>,
    lazy_result: Vec<String>,
}

impl TestCase {
    pub fn new(
        hans: String,
        args: pinyin::Args,
        result: Vec<Vec<String>>,
        lazy_result: Vec<String>,
    ) -> TestCase {
        TestCase {
            hans,
            args,
            result,
            lazy_result,
        }
    }
}

#[test]
fn test_pinyin() {
    let test_data = vec![
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args::new(),
            vec![
                vec!["zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Normal,
                heteronym: false,
            },
            vec![
                vec!["zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Tone,
                heteronym: false,
            },
            vec![
                vec!["zhōng".to_string()],
                vec!["guó".to_string()],
                vec!["rén".to_string()],
            ],
            vec!["zhōng".to_string(), "guó".to_string(), "rén".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Tone2,
                heteronym: false,
            },
            vec![
                vec!["zho1ng".to_string()],
                vec!["guo2".to_string()],
                vec!["re2n".to_string()],
            ],
            vec!["zho1ng".to_string(), "guo2".to_string(), "re2n".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Initials,
                heteronym: false,
            },
            vec![
                vec!["zh".to_string()],
                vec!["g".to_string()],
                vec!["r".to_string()],
            ],
            vec!["zh".to_string(), "g".to_string(), "r".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FirstLetter,
                heteronym: false,
            },
            vec![
                vec!["z".to_string()],
                vec!["g".to_string()],
                vec!["r".to_string()],
            ],
            vec!["z".to_string(), "g".to_string(), "r".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Finals,
                heteronym: false,
            },
            vec![
                vec!["ong".to_string()],
                vec!["uo".to_string()],
                vec!["en".to_string()],
            ],
            vec!["ong".to_string(), "uo".to_string(), "en".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FinalsTone,
                heteronym: false,
            },
            vec![
                vec!["ōng".to_string()],
                vec!["uó".to_string()],
                vec!["én".to_string()],
            ],
            vec!["ōng".to_string(), "uó".to_string(), "én".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::FinalsTone2,
                heteronym: false,
            },
            vec![
                vec!["o1ng".to_string()],
                vec!["uo2".to_string()],
                vec!["e2n".to_string()],
            ],
            vec!["o1ng".to_string(), "uo2".to_string(), "e2n".to_string()],
        ),
        TestCase::new(
            "中国人".to_string(),
            pinyin::Args {
                style: pinyin::Style::Normal,
                heteronym: true,
            },
            vec![
                vec!["zhong".to_string(), "zhong".to_string()],
                vec!["guo".to_string()],
                vec!["ren".to_string()],
            ],
            vec!["zhong".to_string(), "guo".to_string(), "ren".to_string()],
        ),
    ];
    for data in &test_data {
        assert_eq!(data.result, pinyin::pinyin(&data.hans, &data.args));
        assert_eq!(
            data.lazy_result,
            pinyin::lazy_pinyin(&data.hans, &data.args)
        );
    }
}

#[test]
fn test_non_chinese_pinyin() {
    let hans = "中国人abc你好";
    let expect = vec![
        vec!["zhong".to_string()],
        vec!["guo".to_string()],
        vec!["ren".to_string()],
        vec![],
        vec![],
        vec![],
        vec!["ni".to_string()],
        vec!["hao".to_string()],
    ];
    let result = pinyin::pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_non_chinese_lazy_pinyin() {
    let hans = "中国人abc你好";
    let expect = vec![
        "zhong".to_string(),
        "guo".to_string(),
        "ren".to_string(),
        "ni".to_string(),
        "hao".to_string(),
    ];
    let result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);
}

#[test]
fn test_new_args() {
    let args = pinyin::Args::new();
    assert_eq!(pinyin::Style::Normal, args.style);
    assert_eq!(false, args.heteronym);

    let expected = pinyin::Args {
        style: pinyin::Style::Normal,
        heteronym: false,
    };
    assert_eq!(expected, args);
}

#[test]
fn test_default_args() {
    let args = pinyin::Args::default();
    assert_eq!(pinyin::Style::Normal, args.style);
    assert_eq!(false, args.heteronym);

    let expected = pinyin::Args {
        style: pinyin::Style::Normal,
        heteronym: false,
    };
    assert_eq!(expected, args);
}

#[test]
fn test_no_initial() {
    let hans = "安";
    let mut expect = vec!["an".to_string()];
    let mut result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);

    expect = vec!["an".to_string()];
    result = pinyin::lazy_pinyin(
        hans,
        &pinyin::Args {
            style: pinyin::Style::Finals,
            heteronym: false,
        },
    );
    assert_eq!(expect, result);
}

#[test]
fn test_no_phonetic_symbol() {
    let hans = "啊";
    let mut expect = vec!["a".to_string()];
    let mut result = pinyin::lazy_pinyin(hans, &pinyin::Args::new());
    assert_eq!(expect, result);

    expect = vec!["a".to_string()];
    result = pinyin::lazy_pinyin(
        hans,
        &pinyin::Args {
            style: pinyin::Style::Finals,
            heteronym: false,
        },
    );
    assert_eq!(expect, result);
}
<|endoftext|>


<commit_before>use ndarray::*;
use ndarray_linalg::*;
use std::f64::consts::PI;

use eom::pde::*;

#[test]
fn pair_r2c2r() {
    let n = 128;
    let a: Array1<f64> = random(n);
    let mut p = Pair::new(n);
    p.r.copy_from_slice(&a.as_slice().unwrap());
    p.r2c();
    p.c2r();
    let b: Array1<f64> = Array::from_iter(p.r.iter().cloned());
    assert_close_l2!(&a, &b, 1e-7);
}

#[test]
fn pair_c2r() {
    let n = 128;
    let k0 = 2.0 * PI / n as f64;
    let a = Array::from_shape_fn(n, |i| 2.0 * (i as f64 * k0).cos());
    let mut p = Pair::new(n);
    p.c[1] = c64::new(1.0, 0.0);
    p.c2r();
    let b = Array::from_iter(p.r.iter().cloned());
    assert_close_l2!(&a, &b, 1e-7);
}
<commit_msg>add FromIterator to tests/pde.rs<commit_after>use ndarray::*;
use ndarray_linalg::*;
use std::f64::consts::PI;
use std::iter::FromIterator;

use eom::pde::*;

#[test]
fn pair_r2c2r() {
    let n = 128;
    let a: Array1<f64> = random(n);
    let mut p = Pair::new(n);
    p.r.copy_from_slice(&a.as_slice().unwrap());
    p.r2c();
    p.c2r();
    let b: Array1<f64> = Array::from_iter(p.r.iter().cloned());
    assert_close_l2!(&a, &b, 1e-7);
}

#[test]
fn pair_c2r() {
    let n = 128;
    let k0 = 2.0 * PI / n as f64;
    let a = Array::from_shape_fn(n, |i| 2.0 * (i as f64 * k0).cos());
    let mut p = Pair::new(n);
    p.c[1] = c64::new(1.0, 0.0);
    p.c2r();
    let b = Array::from_iter(p.r.iter().cloned());
    assert_close_l2!(&a, &b, 1e-7);
}
<|endoftext|>


<commit_before>use hyper;

extern crate gluon;

use std::{fs, path::PathBuf, process::Command};

use crate::gluon::{
    new_vm,
    vm::api::{Hole, OpaqueValue},
    Compiler, RootedThread,
};

use futures::{future, Future};
use tokio::runtime::current_thread::Runtime;
use tokio_retry::{strategy::FixedInterval, Retry};

#[test]
fn test() {
    test_pages(&[
        ("", hyper::StatusCode::OK),
        ("/not_existing.html", hyper::StatusCode::NOT_FOUND),
        ("/404.html", hyper::StatusCode::OK),
    ]);
}

fn test_pages(pages: &[(&str, hyper::StatusCode)]) {
    let path = PathBuf::from(::std::env::args().next().unwrap());
    let exe = path
        .parent()
        .and_then(|path| path.parent())
        .expect("server executable")
        .join(env!("CARGO_PKG_NAME"));
    let mut child = Command::new(exe).args(&["--port", "4567"]).spawn().unwrap();
    {
        let mut runtime = Runtime::new().unwrap();

        for (page, expected_status) in pages {
            runtime
                .block_on(future::lazy(move || {
                    let client = hyper::Client::new();
                    let strategy = FixedInterval::from_millis(500).take(20);
                    let url = format!("http://localhost:4567{}", page);
                    Retry::spawn(strategy, {
                        let url = url.clone();
                        move || client.get(url.parse().unwrap())
                    })
                    .map(move |response| {
                        assert_eq!(
                            response.status(),
                            *expected_status,
                            "Unexpected status for {}",
                            url
                        )
                    })
                }))
                .unwrap();
        }
    }
    child.kill().unwrap();
}

#[test]
fn test_examples() {
    let thread = new_vm();
    let mut compiler = Compiler::new();

    for example_path in fs::read_dir("public/examples").unwrap() {
        let example_path = &example_path.as_ref().unwrap().path();
        eprintln!("{}", example_path.display());
        let contents = fs::read_to_string(example_path).unwrap();
        compiler
            .run_expr::<OpaqueValue<RootedThread, Hole>>(
                &thread,
                &example_path.display().to_string(),
                &contents,
            )
            .unwrap_or_else(|err| panic!("{}", err));
    }
}
<commit_msg>Cleanup after the test_pages test fails<commit_after>use hyper;

extern crate gluon;

use std::{
    fs,
    path::PathBuf,
    process::{Child, Command},
};

use crate::gluon::{
    new_vm,
    vm::api::{Hole, OpaqueValue},
    Compiler, RootedThread,
};

use futures::{future, Future};
use tokio::runtime::current_thread::Runtime;
use tokio_retry::{strategy::FixedInterval, Retry};

struct DropKill(Child);

impl Drop for DropKill {
    fn drop(&mut self) {
        self.0.kill().unwrap();
    }
}

#[test]
fn test_pages() {
    let pages = &[
        ("", hyper::StatusCode::OK),
        ("/not_existing.html", hyper::StatusCode::NOT_FOUND),
        ("/404.html", hyper::StatusCode::OK),
    ];

    let path = PathBuf::from(::std::env::args().next().unwrap());
    let exe = path
        .parent()
        .and_then(|path| path.parent())
        .expect("server executable")
        .join(env!("CARGO_PKG_NAME"));
    let _child = DropKill(Command::new(exe).args(&["--port", "4567"]).spawn().unwrap());
    let mut runtime = Runtime::new().unwrap();

    for (page, expected_status) in pages {
        runtime
            .block_on(future::lazy(move || {
                let client = hyper::Client::new();
                let strategy = FixedInterval::from_millis(500).take(20);
                let url = format!("http://localhost:4567{}", page);
                Retry::spawn(strategy, {
                    let url = url.clone();
                    move || client.get(url.parse().unwrap())
                })
                .map(move |response| {
                    assert_eq!(
                        response.status(),
                        *expected_status,
                        "Unexpected status for {}",
                        url
                    )
                })
            }))
            .unwrap();
    }
}

#[test]
fn test_examples() {
    let thread = new_vm();
    let mut compiler = Compiler::new();

    for example_path in fs::read_dir("public/examples").unwrap() {
        let example_path = &example_path.as_ref().unwrap().path();
        eprintln!("{}", example_path.display());
        let contents = fs::read_to_string(example_path).unwrap();
        compiler
            .run_expr::<OpaqueValue<RootedThread, Hole>>(
                &thread,
                &example_path.display().to_string(),
                &contents,
            )
            .unwrap_or_else(|err| panic!("{}", err));
    }
}
<|endoftext|>