<commit_before>use Parameter;
use fa::VFunction;
use agents::PredictionAgent;
use geometry::Space;
use std::marker::PhantomData;

use ndarray::ArrayBase;

pub struct EveryVisitMC<S: Space, V: VFunction<S>>
{
    v_func: V,
    observations: Vec<(S::Repr, f64)>,

    alpha: Parameter,
    gamma: Parameter,

    phantom: PhantomData<S>,
}

impl<S: Space, V: VFunction<S>> EveryVisitMC<S, V>
{
    pub fn new<T1, T2>(v_func: V, alpha: T1, gamma: T2) -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>
    {
        EveryVisitMC {
            v_func: v_func,
            observations: vec![],

            alpha: alpha.into(),
            gamma: gamma.into(),

            phantom: PhantomData,
        }
    }
}

impl<S: Space, V: VFunction<S>> PredictionAgent<S> for EveryVisitMC<S, V>
{
    fn handle_transition(&mut self, s: &S::Repr, _: &S::Repr, r: f64) -> Option<f64> {
        self.observations.push((s.clone(), r));

        None
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        let mut sum = 0.0;

        for (s, r) in self.observations.drain(0..).rev() {
            sum = r + self.gamma*sum;

            let v_est = self.v_func.evaluate(&s);
            self.v_func.update(&s, self.alpha*(sum - v_est));
        }

        self.alpha = self.alpha.step();
        self.gamma = self.gamma.step();
    }
}
<commit_msg>Split logic in EveryVisitMC into propagate method<commit_after>use Parameter;
use fa::VFunction;
use agents::PredictionAgent;
use geometry::Space;
use std::marker::PhantomData;

use ndarray::ArrayBase;

pub struct EveryVisitMC<S: Space, V: VFunction<S>>
{
    v_func: V,
    observations: Vec<(S::Repr, f64)>,

    alpha: Parameter,
    gamma: Parameter,

    phantom: PhantomData<S>,
}

impl<S: Space, V: VFunction<S>> EveryVisitMC<S, V>
{
    pub fn new<T1, T2>(v_func: V, alpha: T1, gamma: T2) -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>
    {
        EveryVisitMC {
            v_func: v_func,
            observations: vec![],

            alpha: alpha.into(),
            gamma: gamma.into(),

            phantom: PhantomData,
        }
    }

    pub fn propagate(&mut self) {
        let mut sum = 0.0;

        for (s, r) in self.observations.drain(0..).rev() {
            sum = r + self.gamma*sum;

            let v_est = self.v_func.evaluate(&s);
            self.v_func.update(&s, self.alpha*(sum - v_est));
        }
    }
}

impl<S: Space, V: VFunction<S>> PredictionAgent<S> for EveryVisitMC<S, V>
{
    fn handle_transition(&mut self, s: &S::Repr, _: &S::Repr, r: f64) -> Option<f64> {
        self.observations.push((s.clone(), r));

        None
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.propagate();

        self.alpha = self.alpha.step();
        self.gamma = self.gamma.step();
    }
}
<|endoftext|>


<commit_before>
mod api {

	pub type Target = u32;
	pub type Number = f64;
	pub type Category = u32;

	pub type DataSet<T> = Vec<(T, Target)>;

	pub enum FeatureType {
		Boolean,
		Category,
		Number
	}

	pub trait RecordMeta {

		fn feature_count(&self) -> usize;

		fn feature_name(&self, feature: usize) -> String;
		fn feature_type(&self, feature: usize) -> FeatureType;

		fn category_count(&self, feature: usize) -> usize;

	}

	pub trait DataSetMeta {

		fn target_count(&self) -> usize;

	}

}

mod data {

	use ::api;

	pub struct Record {
		pub category: api::Category,
    	pub number: api::Number,
    	pub boolean: bool
	}

	impl api::RecordMeta for Record {

		fn feature_count(&self) -> usize {
			3
		}

		fn feature_name(&self, feature: usize) -> String {
			match feature {
			    0 => "category".to_string(),
			    1 => "number".to_string(),
			    2 => "boolean".to_string(),
			    _ => panic!("Unknown feature")
			}
		}

		fn feature_type(&self, feature: usize) -> api::FeatureType {
			match feature {
			    0 => api::FeatureType::Category,
			    1 => api::FeatureType::Number,
			    2 => api::FeatureType::Boolean,
			    _ => panic!("Unknown feature")
			}
		}

		fn category_count(&self, feature: usize) -> usize {
			match feature {
			    0 => 3,
			    _ => panic!("Unknown feature")
			}

		}

	}

	impl api::DataSetMeta for api::DataSet<Record> {

		fn target_count(&self) -> usize {
			2
		}

	}

	pub fn read_data() -> api::DataSet<Record> {
		let result = vec![
			(Record{category: 0, number: 70.0, boolean: true }, 0),
			(Record{category: 0, number: 90.0, boolean: true }, 1),
			(Record{category: 0, number: 85.0, boolean: false}, 1),
			(Record{category: 0, number: 95.0, boolean: false}, 1),
			(Record{category: 0, number: 70.0, boolean: false}, 0),
			(Record{category: 1, number: 90.0, boolean: true }, 0),
			(Record{category: 1, number: 78.0, boolean: false}, 0),
			(Record{category: 1, number: 65.0, boolean: true }, 0),
			(Record{category: 1, number: 75.0, boolean: false}, 0),
			(Record{category: 2, number: 80.0, boolean: true }, 1),
			(Record{category: 2, number: 70.0, boolean: true }, 1),
			(Record{category: 2, number: 80.0, boolean: false}, 0),
			(Record{category: 2, number: 80.0, boolean: false}, 0),
			(Record{category: 2, number: 96.0, boolean: false}, 0)
		];
		result
	}

}

mod solve_func {

	use ::api;

	pub fn information_gain<T: api::RecordMeta, F>(data: &api::DataSet<T>, criterion: &F, target_count: usize) -> f64 where F: Fn(&T) -> bool {
		let e = target_entropy(data, target_count);
		let fe = entropy(data, criterion, target_count);
		e - fe
	}

	fn entropy_helper<T: api::RecordMeta, F>(data: &api::DataSet<T>, value: bool, criterion: &F, target_count: usize) -> f64 where F: Fn(&T) -> bool {
		let mut total = 0;
		let mut classes = vec![0; target_count];
		for record in data.iter() {
			if criterion(&record.0) == value {
				classes[record.1 as usize] += 1;
				total += 1;
			}
		}
		let mut result = 0f64;
		for class in classes.iter() {
			if *class != 0 {
				let p = (*class as f64) / (total as f64);
				result += p * p.log2()
			}
		}
		-result
	}

	fn entropy<T: api::RecordMeta, F>(data: &api::DataSet<T>, criterion: &F, target_count: usize) -> f64 where F: Fn(&T) -> bool {
		let total = data.len();
		let mut ccs = vec![0u8, 0u8];
		let values = vec![false, true];
		for record in data.iter() {
			let i = match criterion(&record.0) {
					false => 0,
					true => 1,
			};
			ccs[i] += 1;
		}
		let mut result = 0f64;
		for i in 0..2 {
			let p = (ccs[i] as f64) / (total as f64);
			result += p * entropy_helper(data, values[i], criterion, target_count);
		}
		result
	}

	fn target_entropy<T: api::RecordMeta>(data: &api::DataSet<T>, target_count: usize) -> f64 {
		let total = data.len();
		let mut classes = vec![0; target_count];
		for record in data.iter() {
			classes[record.1 as usize] += 1;
		}
		for class in classes.iter() {
			if *class == 0 {
				return 0f64
			}
		}
		let mut result = 0f64;
		for class in classes.iter() {
			let p = (*class as f64) / (total as f64);
			result += p * p.log2()
		}
		-result
	}

}

fn main() {
	use api::DataSetMeta;

    println!("Hello, ID3!");
    let data = data::read_data();
    let entropy = solve_func::information_gain(&data, &|rec| rec.boolean, data.target_count());
    println!("{}", entropy);
}
<commit_msg>remove DataSetMeta<commit_after>
mod api {

	pub type Target = u32;
	pub type Number = f64;
	pub type Category = u32;

	pub struct DataSet<T> {
		pub records: Vec<(T, Target)>,
		pub target_count: usize
	}

	pub enum FeatureType {
		Boolean,
		Category,
		Number
	}

	pub trait RecordMeta {

		fn feature_count(&self) -> usize;

		fn feature_name(&self, feature: usize) -> String;
		fn feature_type(&self, feature: usize) -> FeatureType;

		fn category_count(&self, feature: usize) -> usize;

	}

}

mod data {

	use ::api;

	pub struct Record {
		pub category: api::Category,
		pub number: api::Number,
		pub boolean: bool
	}

	impl api::RecordMeta for Record {

		fn feature_count(&self) -> usize {
			3
		}

		fn feature_name(&self, feature: usize) -> String {
			match feature {
				0 => "category".to_string(),
				1 => "number".to_string(),
				2 => "boolean".to_string(),
				_ => panic!("Unknown feature")
			}
		}

		fn feature_type(&self, feature: usize) -> api::FeatureType {
			match feature {
				0 => api::FeatureType::Category,
				1 => api::FeatureType::Number,
				2 => api::FeatureType::Boolean,
				_ => panic!("Unknown feature")
			}
		}

		fn category_count(&self, feature: usize) -> usize {
			match feature {
				0 => 3,
				_ => panic!("Unknown feature")
			}

		}

	}

	pub fn read_data() -> api::DataSet<Record> {
		let result = api::DataSet{
			records: vec![
				(Record{category: 0, number: 70.0, boolean: true }, 0),
				(Record{category: 0, number: 90.0, boolean: true }, 1),
				(Record{category: 0, number: 85.0, boolean: false}, 1),
				(Record{category: 0, number: 95.0, boolean: false}, 1),
				(Record{category: 0, number: 70.0, boolean: false}, 0),
				(Record{category: 1, number: 90.0, boolean: true }, 0),
				(Record{category: 1, number: 78.0, boolean: false}, 0),
				(Record{category: 1, number: 65.0, boolean: true }, 0),
				(Record{category: 1, number: 75.0, boolean: false}, 0),
				(Record{category: 2, number: 80.0, boolean: true }, 1),
				(Record{category: 2, number: 70.0, boolean: true }, 1),
				(Record{category: 2, number: 80.0, boolean: false}, 0),
				(Record{category: 2, number: 80.0, boolean: false}, 0),
				(Record{category: 2, number: 96.0, boolean: false}, 0)
			],
			target_count: 2
		};
		result
	}

}

mod solve_func {

	use ::api;

	pub fn information_gain<T: api::RecordMeta, F>(data: &api::DataSet<T>, criterion: &F) -> f64 where F: Fn(&T) -> bool {
		let e = target_entropy(data);
		let fe = entropy(data, criterion);
		e - fe
	}

	fn entropy_helper<T: api::RecordMeta, F>(data: &api::DataSet<T>, value: bool, criterion: &F) -> f64 where F: Fn(&T) -> bool {
		let mut total = 0;
		let mut classes = vec![0; data.target_count];
		for record in data.records.iter() {
			if criterion(&record.0) == value {
				classes[record.1 as usize] += 1;
				total += 1;
			}
		}
		let mut result = 0f64;
		for class in classes.iter() {
			if *class != 0 {
				let p = (*class as f64) / (total as f64);
				result += p * p.log2()
			}
		}
		-result
	}

	fn entropy<T: api::RecordMeta, F>(data: &api::DataSet<T>, criterion: &F) -> f64 where F: Fn(&T) -> bool {
		let total = data.records.len();
		let mut ccs = vec![0u8, 0u8];
		let values = vec![false, true];
		for record in data.records.iter() {
			let i = match criterion(&record.0) {
					false => 0,
					true => 1,
			};
			ccs[i] += 1;
		}
		let mut result = 0f64;
		for i in 0..2 {
			let p = (ccs[i] as f64) / (total as f64);
			result += p * entropy_helper(data, values[i], criterion);
		}
		result
	}

	fn target_entropy<T: api::RecordMeta>(data: &api::DataSet<T>) -> f64 {
		let total = data.records.len();
		let mut classes = vec![0; data.target_count];
		for record in data.records.iter() {
			classes[record.1 as usize] += 1;
		}
		for class in classes.iter() {
			if *class == 0 {
				return 0f64
			}
		}
		let mut result = 0f64;
		for class in classes.iter() {
			let p = (*class as f64) / (total as f64);
			result += p * p.log2()
		}
		-result
	}

}

fn main() {
	println!("Hello, ID3!");
	let data = data::read_data();
	let entropy = solve_func::information_gain(&data, &|rec| rec.boolean);
	println!("{}", entropy);
}
<|endoftext|>


<commit_before>
extern crate airspy;
use airspy::Airspy;
use airspy::AirspySamplerate;

#[macro_use]
extern crate log;
extern crate fern;
extern crate time;
extern crate num;
use num::complex::Complex;

use std::io;
use std::io::Write;
use std::env;

fn main() {
    // setup fern logger
    let logger_config = fern::DispatchConfig {
        format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
            let t = time::now();
            let ms = t.tm_nsec/1000_000;
            format!("{}.{:3} [{}] {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, msg)
        }),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) {
        panic!("Failed to initialize global logger: {}", e);
    }

    // check args
    let mut args = env::args();
    let mut frequency_hz = 103_400_000;
    if args.len() == 1 {
        info!("use default frequency: {:.3} MHz", frequency_hz as f64/1000_000f64);
    }
    else {
        frequency_hz = (args.nth(1).unwrap().parse::<f32>().unwrap() as f64 * 1000_000f64) as u32;
        info!("frequency: {:.3} MHz", frequency_hz as f64/1000_000f64);
    }

    match Airspy::<Complex<i16>>::new() {
        Ok(mut device) => {
            info!("device opened sucessfully");
            device.set_samplerate(AirspySamplerate::Sps2500000).unwrap();

            device.set_vga_gain(6).unwrap();
            device.set_mixer_gain(5).unwrap();
            device.set_lna_gain(7).unwrap();

            device.set_frequency(frequency_hz).unwrap();
            device.start_rx().unwrap();

            let mut stdout = io::stdout();

            loop {
                // actually iterator blocks here
                for s in device.samples() {
                    let sliceu8 = Airspy::convert_samples_to_bytes(&s);

                    stdout.write(sliceu8).unwrap();
                    stdout.flush().unwrap();
                }

                info!("sleep");
            }
        }
        Err(e) => {
            error!("error {:?}", e);
        }
    }
}
<commit_msg>use f64 to calculate frequency_hz in example code<commit_after>
extern crate airspy;
use airspy::Airspy;
use airspy::AirspySamplerate;

#[macro_use]
extern crate log;
extern crate fern;
extern crate time;
extern crate num;
use num::complex::Complex;

use std::io;
use std::io::Write;
use std::env;

fn main() {
    // setup fern logger
    let logger_config = fern::DispatchConfig {
        format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
            let t = time::now();
            let ms = t.tm_nsec/1000_000;
            format!("{}.{:3} [{}] {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, msg)
        }),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) {
        panic!("Failed to initialize global logger: {}", e);
    }

    // check args
    let mut args = env::args();
    let mut frequency_hz = 103_400_000;
    if args.len() == 1 {
        info!("use default frequency: {:.3} MHz", frequency_hz as f64/1000_000f64);
    }
    else {
        frequency_hz = (args.nth(1).unwrap().parse::<f64>().unwrap() * 1000_000f64) as u32;
        info!("frequency: {:.3} MHz", frequency_hz as f64/1000_000f64);
    }

    match Airspy::<Complex<i16>>::new() {
        Ok(mut device) => {
            info!("device opened sucessfully");
            device.set_samplerate(AirspySamplerate::Sps2500000).unwrap();

            device.set_vga_gain(6).unwrap();
            device.set_mixer_gain(5).unwrap();
            device.set_lna_gain(7).unwrap();

            device.set_frequency(frequency_hz).unwrap();
            device.start_rx().unwrap();

            let mut stdout = io::stdout();

            loop {
                // actually iterator blocks here
                for s in device.samples() {
                    let sliceu8 = Airspy::convert_samples_to_bytes(&s);

                    stdout.write(sliceu8).unwrap();
                    stdout.flush().unwrap();
                }

                info!("sleep");
            }
        }
        Err(e) => {
            error!("error {:?}", e);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern mod glfw;

use std::libc;

fn error_callback(_: libc::c_int, description: ~str) {
    println(fmt!("GLFW Error: %s", description));
}

fn main() {
    glfw::set_error_callback(error_callback);

    do glfw::spawn {

        glfw::window_hint::visible(true);

        let window = glfw::Window::create(640, 480, "Defaults", glfw::Windowed).unwrap();

        window.make_context_current();

        let (width, height) = window.get_size();
        println(fmt!("window size: %? x %?", width, height));

        println(fmt!("Context version: %s",           window.get_context_version().to_str()));
        println(fmt!("OpenGL forward compatible: %?", window.is_opengl_forward_compat()));
        println(fmt!("OpenGL debug context: %?",      window.is_opengl_debug_context()));
        println(fmt!("OpenGL profile: %?",            window.get_opengl_profile()));

        let gl_params = [
            (gl::RED_BITS,          None,   "red bits"          ),
            (gl::GREEN_BITS,        None,   "green bits"        ),
            (gl::BLUE_BITS,         None,   "blue bits"         ),
            (gl::ALPHA_BITS,        None,   "alpha bits"        ),
            (gl::DEPTH_BITS,        None,   "depth bits"        ),
            (gl::STENCIL_BITS,      None,   "stencil bits"      ),
            (gl::ACCUM_RED_BITS,    None,   "accum red bits"    ),
            (gl::ACCUM_GREEN_BITS,  None,   "accum green bits"  ),
            (gl::ACCUM_BLUE_BITS,   None,   "accum blue bits"   ),
            (gl::ACCUM_ALPHA_BITS,  None,   "accum alpha bits"  ),
            (gl::STEREO,            None,   "stereo"            ),
            (gl::SAMPLES_ARB,       Some("GL_ARB_multisample"), "FSAA samples" ),
        ];

        for gl_params.iter().advance |&(param, ext, name)| {
            if do ext.map_default(true) |&s| {
                glfw::extension_supported(s)
            } {
                unsafe {
                    let value = 0;
                    gl::GetIntegerv(param, &value);
                    println(fmt!("OpenGL %s: %?", name, value));
                }
            };
        }
    }
}

mod gl {
    use std::libc;

    #[nolink]
    #[link_args="-framework OpenGL"]
    #[cfg(target_os = "macos")]
    pub extern { }

    #[nolink]
    #[link_args="-lGL"]
    #[cfg(target_os = "linux")]
    pub extern { }

    pub type GLenum = libc::c_uint;
    pub type GLint  = libc::c_int;

    pub static RED_BITS              : GLenum = 0x0D52;
    pub static GREEN_BITS            : GLenum = 0x0D53;
    pub static BLUE_BITS             : GLenum = 0x0D54;
    pub static ALPHA_BITS            : GLenum = 0x0D55;
    pub static DEPTH_BITS            : GLenum = 0x0D56;
    pub static STENCIL_BITS          : GLenum = 0x0D57;
    pub static ACCUM_RED_BITS        : GLenum = 0x0D58;
    pub static ACCUM_GREEN_BITS      : GLenum = 0x0D59;
    pub static ACCUM_BLUE_BITS       : GLenum = 0x0D5A;
    pub static ACCUM_ALPHA_BITS      : GLenum = 0x0D5B;
    pub static STEREO                : GLenum = 0x0C33;
    pub static SAMPLES_ARB           : GLenum = 0x80A9;

    pub extern "C" {
        #[link_name="glGetIntegerv"]
        pub fn GetIntegerv(pname: GLenum, params: *GLint);
    }
}
<commit_msg>Remove obsolete `pub` modifier on `extern` blocks<commit_after>// Copyright 2013 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern mod glfw;

use std::libc;

fn error_callback(_: libc::c_int, description: ~str) {
    println(fmt!("GLFW Error: %s", description));
}

fn main() {
    glfw::set_error_callback(error_callback);

    do glfw::spawn {

        glfw::window_hint::visible(true);

        let window = glfw::Window::create(640, 480, "Defaults", glfw::Windowed).unwrap();

        window.make_context_current();

        let (width, height) = window.get_size();
        println(fmt!("window size: %? x %?", width, height));

        println(fmt!("Context version: %s",           window.get_context_version().to_str()));
        println(fmt!("OpenGL forward compatible: %?", window.is_opengl_forward_compat()));
        println(fmt!("OpenGL debug context: %?",      window.is_opengl_debug_context()));
        println(fmt!("OpenGL profile: %?",            window.get_opengl_profile()));

        let gl_params = [
            (gl::RED_BITS,          None,   "red bits"          ),
            (gl::GREEN_BITS,        None,   "green bits"        ),
            (gl::BLUE_BITS,         None,   "blue bits"         ),
            (gl::ALPHA_BITS,        None,   "alpha bits"        ),
            (gl::DEPTH_BITS,        None,   "depth bits"        ),
            (gl::STENCIL_BITS,      None,   "stencil bits"      ),
            (gl::ACCUM_RED_BITS,    None,   "accum red bits"    ),
            (gl::ACCUM_GREEN_BITS,  None,   "accum green bits"  ),
            (gl::ACCUM_BLUE_BITS,   None,   "accum blue bits"   ),
            (gl::ACCUM_ALPHA_BITS,  None,   "accum alpha bits"  ),
            (gl::STEREO,            None,   "stereo"            ),
            (gl::SAMPLES_ARB,       Some("GL_ARB_multisample"), "FSAA samples" ),
        ];

        for gl_params.iter().advance |&(param, ext, name)| {
            if do ext.map_default(true) |&s| {
                glfw::extension_supported(s)
            } {
                unsafe {
                    let value = 0;
                    gl::GetIntegerv(param, &value);
                    println(fmt!("OpenGL %s: %?", name, value));
                }
            };
        }
    }
}

mod gl {
    use std::libc;

    #[nolink]
    #[link_args="-framework OpenGL"]
    #[cfg(target_os = "macos")]
    extern { }

    #[nolink]
    #[link_args="-lGL"]
    #[cfg(target_os = "linux")]
    extern { }

    pub type GLenum = libc::c_uint;
    pub type GLint  = libc::c_int;

    pub static RED_BITS              : GLenum = 0x0D52;
    pub static GREEN_BITS            : GLenum = 0x0D53;
    pub static BLUE_BITS             : GLenum = 0x0D54;
    pub static ALPHA_BITS            : GLenum = 0x0D55;
    pub static DEPTH_BITS            : GLenum = 0x0D56;
    pub static STENCIL_BITS          : GLenum = 0x0D57;
    pub static ACCUM_RED_BITS        : GLenum = 0x0D58;
    pub static ACCUM_GREEN_BITS      : GLenum = 0x0D59;
    pub static ACCUM_BLUE_BITS       : GLenum = 0x0D5A;
    pub static ACCUM_ALPHA_BITS      : GLenum = 0x0D5B;
    pub static STEREO                : GLenum = 0x0C33;
    pub static SAMPLES_ARB           : GLenum = 0x80A9;

    extern "C" {
        #[link_name="glGetIntegerv"]
        pub fn GetIntegerv(pname: GLenum, params: *GLint);
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::{AtomicBool, Ordering};
use std::cmp::max;

use game::{Position, Move};
use types::{NumPlies, Score, ScoreUnit, Data};

// TODO put more parameters here
#[derive(Clone)]
pub struct Param {
    pub draw_val: ScoreUnit,
    pub depth: NumPlies,
}

pub fn negamax(pos: &mut Position,
               alpha: Option<Score>,
               beta: Option<Score>,
               param: Param,
               is_killed: &AtomicBool) -> (Score, Data) {
    negamax_generic(pos, alpha, beta, param, is_killed,
                    &mut |x| Box::new(x.legal_iter()),
                    &mut |x, draw_val| {
                        let quiescence_param = Param {
                            draw_val: draw_val,
                            depth: NumPlies(20)
                        };
                        quiescence(x, alpha, beta, quiescence_param, is_killed)
                    })
}

fn quiescence(pos: &mut Position,
              alpha: Option<Score>,
              beta: Option<Score>,
              param: Param,
              is_killed: &AtomicBool) -> (Score, Data) {
    negamax_generic(pos, alpha, beta, param, is_killed,
                    &mut |x| Box::new(x.noisy_iter()),
                    &mut |x, draw_val| (x.eval(draw_val), Data::one_node()))
}

// TODO somehow eliminate the Box
fn negamax_generic<F, G>(pos: &mut Position,
                         alpha: Option<Score>,
                         beta: Option<Score>,
                         param: Param,
                         is_killed: &AtomicBool,
                         move_gen_fn: &mut F,
                         eval_fn: &mut G) -> (Score, Data) where
for<'a> F: FnMut(&'a Position) -> Box<Iterator<Item = Move> + 'a>,
for<'b> G: FnMut(&'b mut Position, ScoreUnit) -> (Score, Data) {
    if is_killed.load(Ordering::Relaxed) {
        return (Score::Value(ScoreUnit(0)), Data::one_node());
    }
    if param.depth == NumPlies(0) {
        return eval_fn(pos, param.draw_val);
    }

    let (has_legal, score_opt, data): (bool, Option<Score>, Data) = {
        let temp = pos.clone();
        let move_iter = move_gen_fn(&temp);

        let mut has_legal = false;
        let mut prev_score_opt: Option<Score> = alpha;
        let mut prev_data = Data::one_node();

        for curr_move in move_iter {

            let new_alpha = beta.map(|x| x.decrement());
            let new_beta = prev_score_opt.map(|x| x.decrement());
            let new_param = Param {
                draw_val: -param.draw_val,
                depth: NumPlies(param.depth.0 - 1)
            };
            let (temp_score, temp_data) = pos.with_move(&curr_move, |new_pos|
                negamax_generic(new_pos,
                                new_alpha,
                                new_beta,
                                new_param,
                                is_killed,
                                move_gen_fn,
                                eval_fn));
            let curr_score = temp_score.increment();
            let curr_data = temp_data.increment();

            let new_score = match prev_score_opt {
                None => curr_score,
                Some(prev_score) => max(prev_score, curr_score),
            };
            let new_data = prev_data.combine(curr_data);

            has_legal = true;
            prev_score_opt = Some(new_score);
            prev_data = new_data;

            if let Some(beta_val) = beta {
                if new_score >= beta_val {
                    prev_score_opt = beta;
                    break;
                }
            }
        }

        (has_legal, prev_score_opt, prev_data)
    };

    if has_legal {
        (score_opt.unwrap(), data)
    } else {
        eval_fn(pos, param.draw_val)
    }
}
<commit_msg>Properly implement standing pat for quiescence search<commit_after>use std::sync::atomic::{AtomicBool, Ordering};
use std::cmp::max;

use game::{Position, Move};
use types::{NumPlies, Score, ScoreUnit, Data};

// TODO put more parameters here
#[derive(Clone)]
pub struct Param {
    pub draw_val: ScoreUnit,
    pub depth: NumPlies,
}

pub fn negamax(pos: &mut Position,
               alpha: Option<Score>,
               beta: Option<Score>,
               param: Param,
               is_killed: &AtomicBool) -> (Score, Data) {
    negamax_generic(pos, alpha, beta, param, is_killed,
                    &mut |x| Box::new(x.legal_iter()),
                    &mut |x, draw_val| {
                        let quiescence_param = Param {
                            draw_val: draw_val,
                            depth: NumPlies(20)
                        };
                        quiescence(x, alpha, beta, quiescence_param, is_killed)
                    },
                    &mut |_, _| None)
}

fn quiescence(pos: &mut Position,
              alpha: Option<Score>,
              beta: Option<Score>,
              param: Param,
              is_killed: &AtomicBool) -> (Score, Data) {
    negamax_generic(pos, alpha, beta, param, is_killed,
                    &mut |x| Box::new(x.noisy_iter()),
                    &mut |x, draw_val| (x.eval(draw_val), Data::one_node()),
                    &mut |x, draw_val| Some(x.eval(draw_val)))
}

// TODO somehow eliminate the Box
fn negamax_generic<F, G, H>(pos: &mut Position,
                            alpha: Option<Score>,
                            beta: Option<Score>,
                            param: Param,
                            is_killed: &AtomicBool,
                            move_gen_fn: &mut F,
                            eval_fn: &mut G,
                            stand_pat_fn: &mut H) -> (Score, Data) where
for<'a> F: FnMut(&'a Position) -> Box<Iterator<Item = Move> + 'a>,
for<'b> G: FnMut(&'b mut Position, ScoreUnit) -> (Score, Data),
for<'c> H: FnMut(&'c mut Position, ScoreUnit) -> Option<Score> {
    if is_killed.load(Ordering::Relaxed) {
        return (Score::Value(ScoreUnit(0)), Data::one_node());
    }
    if param.depth == NumPlies(0) {
        return eval_fn(pos, param.draw_val);
    }

    let (has_legal, score_opt, data): (bool, Option<Score>, Data) = (|| {
        let temp = pos.clone();
        let move_iter = move_gen_fn(&temp);

        let mut has_legal = false;
        let mut prev_score_opt: Option<Score> = alpha;

        if let Some(stand_pat_score) = stand_pat_fn(pos, param.draw_val) {
            let new_score = match prev_score_opt {
                None => stand_pat_score,
                Some(prev_score) => max(prev_score, stand_pat_score),
            };

            has_legal = true;
            prev_score_opt = Some(new_score);

            if let Some(beta_val) = beta {
                if new_score >= beta_val {
                    return (true, beta, Data::one_node());
                }
            }
        }

        let mut prev_data = Data::one_node();

        for curr_move in move_iter {

            let new_alpha = beta.map(|x| x.decrement());
            let new_beta = prev_score_opt.map(|x| x.decrement());
            let new_param = Param {
                draw_val: -param.draw_val,
                depth: NumPlies(param.depth.0 - 1)
            };
            let (temp_score, temp_data) = pos.with_move(&curr_move, |new_pos|
                negamax_generic(new_pos,
                                new_alpha,
                                new_beta,
                                new_param,
                                is_killed,
                                move_gen_fn,
                                eval_fn,
                                stand_pat_fn));
            let curr_score = temp_score.increment();
            let curr_data = temp_data.increment();

            let new_score = match prev_score_opt {
                None => curr_score,
                Some(prev_score) => max(prev_score, curr_score),
            };
            let new_data = prev_data.combine(curr_data);

            has_legal = true;
            prev_score_opt = Some(new_score);
            prev_data = new_data;

            if let Some(beta_val) = beta {
                if new_score >= beta_val {
                    prev_score_opt = beta;
                    break;
                }
            }
        }

        (has_legal, prev_score_opt, prev_data)
    })();

    if has_legal {
        (score_opt.unwrap(), data)
    } else {
        eval_fn(pos, param.draw_val)
    }
}
<|endoftext|>


<commit_before>//! Module containing the Library trait, useful to get started to implement
//! a plug-in for an audio player.
use crate::Song;
use ndarray::{arr1, Array, Array1};
use ndarray_stats::QuantileExt;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;

/// Library trait to make creating plug-ins for existing audio players easier.
pub trait Library {
    /// Return the absolute path of all the songs in an
    /// audio player's music library.
    fn get_songs_paths(&self) -> Vec<String>;
    /// Store an analyzed Song object in some (cold) storage, e.g.
    /// a database, a file...
    fn store_song(&mut self, song: Song);
    /// Log and / or store that an error happened while trying to decode and
    /// analyze a song.
    fn store_error_song(&mut self, song_path: String, error: String);
    /// Retrieve a list of all the stored Songs.
    ///
    /// This should work only after having run `analyze_library` at least
    /// once.
    fn get_stored_songs(&self) -> Vec<Song>;

    /// Return a list of songs that are similar to ``first_song``.
    ///
    /// # Arguments
    ///
    /// * `first_song` - The song the playlist will be built from.
    /// * `playlist_length` - The playlist length. If there are not enough
    /// songs in the library, it will be truncated to the size of the library.
    ///
    /// # Returns
    ///
    /// A vector of `playlist_length` Songs, including `first_song`, that you
    /// most likely want to plug in your audio player by using something like
    /// `ret.map(|song| song.path.to_owned()).collect::<Vec<String>>()`.
    fn playlist_from_song(&self, first_song: Song, playlist_length: usize) -> Vec<Song> {
        let analysis_current_song = arr1(&first_song.analysis.to_vec());
        let mut songs = self.get_stored_songs();
        // Get rid of the song we want to build a playlist from
        songs.retain(|x| *x != first_song);
        let mut array: Array1<f32> = Array1::zeros(songs.len());
        let m = Array::eye(first_song.analysis.len());
        // TODO make that better
        for (i, song) in songs.iter().enumerate() {
            array[[i]] = (arr1(&song.analysis) - &analysis_current_song)
                .dot(&m)
                .dot(&(arr1(&song.analysis) - &analysis_current_song));
        }

        let mut playlist: Vec<Song> = vec![first_song];
        let playlist_length = (playlist_length - 1).min(array.len());
        // TODO also make that better
        for _ in 0..playlist_length {
            let min_index = array.argmin().unwrap();
            let mut vec = array.to_vec();
            vec.remove(min_index);
            array = arr1(&vec);
            playlist.push(songs[min_index].to_owned());
            songs.remove(min_index);
        }
        playlist
    }

    /// Analyze and store songs in `paths`, using `store_song` and
    /// `store_error_song` implementations.
    ///
    /// Note: this is mostly useful for updating a song library. For the first
    /// run, you probably want to use `analyze_library`.
    fn analyze_paths(&mut self, paths: Vec<String>) -> Result<(), String> {
        if paths.is_empty() {
            return Ok(());
        }
        let num_cpus = num_cpus::get();

        let (tx, rx): (
            Sender<(String, Result<Song, String>)>,
            Receiver<(String, Result<Song, String>)>,
        ) = mpsc::channel();
        let mut handles = Vec::new();
        let mut chunk_length = paths.len() / num_cpus;
        if chunk_length == 0 {
            chunk_length = paths.len();
        }

        for chunk in paths.chunks(chunk_length) {
            let tx_thread = tx.clone();
            let owned_chunk = chunk.to_owned();
            let child = thread::spawn(move || {
                for path in owned_chunk {
                    println!("Analyzing path {}", path);
                    let song = Song::new(&path);
                    tx_thread.send((path.to_string(), song)).unwrap();
                }
                drop(tx_thread);
            });
            handles.push(child);
        }
        drop(tx);

        for (path, song) in rx.iter() {
            match song {
                Ok(song) => self.store_song(song),
                Err(e) => self.store_error_song(path.to_string(), e),
            }
        }

        for child in handles {
            child.join().unwrap();
        }
        Ok(())
    }

    /// Analyzes a song library, using `get_songs_paths`, `store_song` and
    /// `store_error_song` implementations.
    fn analyze_library(&mut self) -> Result<(), String> {
        let paths = self.get_songs_paths();
        self.analyze_paths(paths)
    }
}

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

    #[derive(Default)]
    struct TestLibrary {
        internal_storage: Vec<Song>,
        failed_files: Vec<(String, String)>,
    }

    impl Library for TestLibrary {
        fn get_songs_paths(&self) -> Vec<String> {
            vec![
                String::from("./data/white_noise.flac"),
                String::from("./data/s16_mono_22_5kHz.flac"),
                String::from("not-existing.foo"),
                String::from("definitely-not-existing.foo"),
            ]
        }

        fn store_song(&mut self, song: Song) {
            self.internal_storage.push(song);
        }

        fn store_error_song(&mut self, song_path: String, error: String) {
            self.failed_files.push((song_path, error));
        }

        fn get_stored_songs(&self) -> Vec<Song> {
            self.internal_storage.to_owned()
        }
    }

    #[test]
    fn test_analyze_library() {
        let mut test_library = TestLibrary {
            internal_storage: vec![],
            failed_files: vec![],
        };
        test_library.analyze_library().unwrap();

        let mut failed_files = test_library
            .failed_files
            .iter()
            .map(|x| x.0.to_owned())
            .collect::<Vec<String>>();
        failed_files.sort();

        assert_eq!(
            failed_files,
            vec![
                String::from("definitely-not-existing.foo"),
                String::from("not-existing.foo"),
            ],
        );

        let mut songs = test_library
            .internal_storage
            .iter()
            .map(|x| x.path.to_owned())
            .collect::<Vec<String>>();
        songs.sort();

        assert_eq!(
            songs,
            vec![
                String::from("./data/s16_mono_22_5kHz.flac"),
                String::from("./data/white_noise.flac"),
            ],
        );

        test_library
            .internal_storage
            .iter()
            .for_each(|x| assert!(x.analysis.len() > 0));
    }

    #[test]
    fn test_playlist_from_song() {
        let mut test_library = TestLibrary::default();
        let first_song = Song {
            path: String::from("path-to-first"),
            analysis: vec![0., 0., 0.],
            ..Default::default()
        };

        let second_song = Song {
            path: String::from("path-to-second"),
            analysis: vec![0.1, 0., 0.],
            ..Default::default()
        };

        let third_song = Song {
            path: String::from("path-to-third"),
            analysis: vec![10., 11., 10.],
            ..Default::default()
        };

        test_library.internal_storage = vec![
            first_song.to_owned(),
            second_song.to_owned(),
            third_song.to_owned(),
        ];
        assert_eq!(
            vec![first_song.to_owned(), second_song],
            test_library.playlist_from_song(first_song, 2)
        );
    }

    #[test]
    fn test_playlist_from_song_too_little_songs() {
        let mut test_library = TestLibrary::default();
        let first_song = Song {
            path: String::from("path-to-first"),
            analysis: vec![0., 0., 0.],
            ..Default::default()
        };

        let second_song = Song {
            path: String::from("path-to-second"),
            analysis: vec![0.1, 0., 0.],
            ..Default::default()
        };

        let third_song = Song {
            path: String::from("path-to-third"),
            analysis: vec![10., 11., 10.],
            ..Default::default()
        };

        test_library.internal_storage = vec![
            first_song.to_owned(),
            second_song.to_owned(),
            third_song.to_owned(),
        ];
        assert_eq!(
            vec![first_song.to_owned(), second_song, third_song],
            test_library.playlist_from_song(first_song, 200)
        );
    }

    #[test]
    fn test_analyze_empty_path() {
        let mut test_library = TestLibrary::default();
        assert!(test_library.analyze_paths(vec![]).is_ok());
    }
}
<commit_msg>Fix TODOs in library<commit_after>//! Module containing the Library trait, useful to get started to implement
//! a plug-in for an audio player.
use crate::Song;
use ndarray::{arr1, Array};
use noisy_float::prelude::*;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;

/// Library trait to make creating plug-ins for existing audio players easier.
pub trait Library {
    /// Return the absolute path of all the songs in an
    /// audio player's music library.
    fn get_songs_paths(&self) -> Vec<String>;
    /// Store an analyzed Song object in some (cold) storage, e.g.
    /// a database, a file...
    fn store_song(&mut self, song: Song);
    /// Log and / or store that an error happened while trying to decode and
    /// analyze a song.
    fn store_error_song(&mut self, song_path: String, error: String);
    /// Retrieve a list of all the stored Songs.
    ///
    /// This should work only after having run `analyze_library` at least
    /// once.
    fn get_stored_songs(&self) -> Vec<Song>;

    /// Return a list of songs that are similar to ``first_song``.
    ///
    /// # Arguments
    ///
    /// * `first_song` - The song the playlist will be built from.
    /// * `playlist_length` - The playlist length. If there are not enough
    /// songs in the library, it will be truncated to the size of the library.
    ///
    /// # Returns
    ///
    /// A vector of `playlist_length` Songs, including `first_song`, that you
    /// most likely want to plug in your audio player by using something like
    /// `ret.map(|song| song.path.to_owned()).collect::<Vec<String>>()`.
    fn playlist_from_song(&self, first_song: Song, playlist_length: usize) -> Vec<Song> {
        let analysis_current_song = arr1(&first_song.analysis.to_vec());
        let mut songs = self.get_stored_songs();
        let m = Array::eye(first_song.analysis.len());
        songs.sort_by_key(|song| {
            n32((arr1(&song.analysis) - &analysis_current_song)
                .dot(&m)
                .dot(&(arr1(&song.analysis) - &analysis_current_song)))
        });
        songs
            .into_iter()
            .take(playlist_length)
            .collect::<Vec<Song>>()
    }

    /// Analyze and store songs in `paths`, using `store_song` and
    /// `store_error_song` implementations.
    ///
    /// Note: this is mostly useful for updating a song library. For the first
    /// run, you probably want to use `analyze_library`.
    fn analyze_paths(&mut self, paths: Vec<String>) -> Result<(), String> {
        if paths.is_empty() {
            return Ok(());
        }
        let num_cpus = num_cpus::get();

        let (tx, rx): (
            Sender<(String, Result<Song, String>)>,
            Receiver<(String, Result<Song, String>)>,
        ) = mpsc::channel();
        let mut handles = Vec::new();
        let mut chunk_length = paths.len() / num_cpus;
        if chunk_length == 0 {
            chunk_length = paths.len();
        }

        for chunk in paths.chunks(chunk_length) {
            let tx_thread = tx.clone();
            let owned_chunk = chunk.to_owned();
            let child = thread::spawn(move || {
                for path in owned_chunk {
                    println!("Analyzing path {}", path);
                    let song = Song::new(&path);
                    tx_thread.send((path.to_string(), song)).unwrap();
                }
                drop(tx_thread);
            });
            handles.push(child);
        }
        drop(tx);

        for (path, song) in rx.iter() {
            match song {
                Ok(song) => self.store_song(song),
                Err(e) => self.store_error_song(path.to_string(), e),
            }
        }

        for child in handles {
            child.join().unwrap();
        }
        Ok(())
    }

    /// Analyzes a song library, using `get_songs_paths`, `store_song` and
    /// `store_error_song` implementations.
    fn analyze_library(&mut self) -> Result<(), String> {
        let paths = self.get_songs_paths();
        self.analyze_paths(paths)
    }
}

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

    #[derive(Default)]
    struct TestLibrary {
        internal_storage: Vec<Song>,
        failed_files: Vec<(String, String)>,
    }

    impl Library for TestLibrary {
        fn get_songs_paths(&self) -> Vec<String> {
            vec![
                String::from("./data/white_noise.flac"),
                String::from("./data/s16_mono_22_5kHz.flac"),
                String::from("not-existing.foo"),
                String::from("definitely-not-existing.foo"),
            ]
        }

        fn store_song(&mut self, song: Song) {
            self.internal_storage.push(song);
        }

        fn store_error_song(&mut self, song_path: String, error: String) {
            self.failed_files.push((song_path, error));
        }

        fn get_stored_songs(&self) -> Vec<Song> {
            self.internal_storage.to_owned()
        }
    }

    #[test]
    fn test_analyze_library() {
        let mut test_library = TestLibrary {
            internal_storage: vec![],
            failed_files: vec![],
        };
        test_library.analyze_library().unwrap();

        let mut failed_files = test_library
            .failed_files
            .iter()
            .map(|x| x.0.to_owned())
            .collect::<Vec<String>>();
        failed_files.sort();

        assert_eq!(
            failed_files,
            vec![
                String::from("definitely-not-existing.foo"),
                String::from("not-existing.foo"),
            ],
        );

        let mut songs = test_library
            .internal_storage
            .iter()
            .map(|x| x.path.to_owned())
            .collect::<Vec<String>>();
        songs.sort();

        assert_eq!(
            songs,
            vec![
                String::from("./data/s16_mono_22_5kHz.flac"),
                String::from("./data/white_noise.flac"),
            ],
        );

        test_library
            .internal_storage
            .iter()
            .for_each(|x| assert!(x.analysis.len() > 0));
    }

    #[test]
    fn test_playlist_from_song() {
        let mut test_library = TestLibrary::default();
        let first_song = Song {
            path: String::from("path-to-first"),
            analysis: vec![0., 0., 0.],
            ..Default::default()
        };

        let second_song = Song {
            path: String::from("path-to-second"),
            analysis: vec![0.1, 0., 0.],
            ..Default::default()
        };

        let third_song = Song {
            path: String::from("path-to-third"),
            analysis: vec![10., 11., 10.],
            ..Default::default()
        };

        let fourth_song = Song {
            path: String::from("path-to-fourth"),
            analysis: vec![20., 21., 20.],
            ..Default::default()
        };

        test_library.internal_storage = vec![
            first_song.to_owned(),
            fourth_song.to_owned(),
            third_song.to_owned(),
            second_song.to_owned(),
        ];
        assert_eq!(
            vec![first_song.to_owned(), second_song, third_song],
            test_library.playlist_from_song(first_song, 3)
        );
    }

    #[test]
    fn test_playlist_from_song_too_little_songs() {
        let mut test_library = TestLibrary::default();
        let first_song = Song {
            path: String::from("path-to-first"),
            analysis: vec![0., 0., 0.],
            ..Default::default()
        };

        let second_song = Song {
            path: String::from("path-to-second"),
            analysis: vec![0.1, 0., 0.],
            ..Default::default()
        };

        let third_song = Song {
            path: String::from("path-to-third"),
            analysis: vec![10., 11., 10.],
            ..Default::default()
        };

        test_library.internal_storage = vec![
            first_song.to_owned(),
            second_song.to_owned(),
            third_song.to_owned(),
        ];
        assert_eq!(
            vec![first_song.to_owned(), second_song, third_song],
            test_library.playlist_from_song(first_song, 200)
        );
    }

    #[test]
    fn test_analyze_empty_path() {
        let mut test_library = TestLibrary::default();
        assert!(test_library.analyze_paths(vec![]).is_ok());
    }
}
<|endoftext|>


<commit_before>//! Glyph caching using the RustType library.

extern crate rusttype;
use texture::{ops, CreateTexture, UpdateTexture, Format, TextureSettings};
use std::collections::HashMap;

extern crate fnv;
use self::fnv::FnvHasher;
use std::hash::BuildHasherDefault;

use std::path::Path;
use std::io::Read;
use std::fs::File;

use ImageSize;
use types::{FontSize, Scalar};
use character::{Character, CharacterCache};
use texture_packer::TexturePacker;

struct Data {
    offset: [Scalar; 2],
    advance_size: [Scalar; 2],
    atlas_offset: [Scalar; 2],
    atlas_size: [Scalar; 2],
    texture: usize,
}

/// The minimum atlas size.
pub const ATLAS_SIZE: [u32; 2] = [256; 2];

/// A struct used for caching rendered font.
pub struct GlyphCache<'a, F, T> {
    /// The font.
    pub font: rusttype::Font<'a>,
    /// The factory used to create textures.
    pub factory: F,
    /// The settings to render the font with.
    settings: TextureSettings,
    texture_packer: TexturePacker<T>,
    // Maps from fontsize and character to offset, texture offset, advance size and texture index.
    data: HashMap<(FontSize, char), Data, BuildHasherDefault<FnvHasher>>,
}

impl<'a, F, T> GlyphCache<'a, F, T>
    where T: CreateTexture<F> + UpdateTexture<F> + ImageSize
{
    /// Constructs a GlyphCache from a Font.
    pub fn from_font(font: rusttype::Font<'a>, factory: F, settings: TextureSettings) -> Self {
        let fnv = BuildHasherDefault::<FnvHasher>::default();
        GlyphCache {
            font: font,
            factory: factory,
            settings: settings,
            texture_packer: TexturePacker::new(),
            data: HashMap::with_hasher(fnv),
        }
    }

    /// Constructor for a GlyphCache.
    pub fn new<P>(font: P,
                  factory: F,
                  settings: TextureSettings)
                  -> ::std::io::Result<GlyphCache<'static, F, T>>
        where P: AsRef<Path>
    {
        let fnv = BuildHasherDefault::<FnvHasher>::default();
        let mut file = File::open(font)?;
        let mut file_buffer = Vec::new();
        file.read_to_end(&mut file_buffer)?;

        let collection = rusttype::FontCollection::from_bytes(file_buffer);
        let font = collection.unwrap().into_font().unwrap();
        Ok(GlyphCache {
            font: font,
            factory: factory,
            settings: settings,
            texture_packer: TexturePacker::new(),
            data: HashMap::with_hasher(fnv),
        })
    }

    /// Creates a GlyphCache for a font stored in memory.
    pub fn from_bytes(font: &'a [u8],
                      factory: F,
                      settings: TextureSettings)
                      -> Result<GlyphCache<'a, F, T>, ()> {
        let collection = rusttype::FontCollection::from_bytes(font).or(Err(()))?;
        let font = collection.into_font().or(Err(()))?;
        Ok(Self::from_font(font, factory, settings))
    }

    /// Load all characters in the `chars` iterator for `size`
    pub fn preload_chars<I>(
        &mut self,
        size: FontSize,
        chars: I
    ) -> Result<(), T::Error>
        where I: Iterator<Item = char>
    {
        for ch in chars {
            self.character(size, ch)?;
        }
        Ok(())
    }

    /// Load all the printable ASCII characters for `size`. Includes space.
    pub fn preload_printable_ascii(
        &mut self,
        size: FontSize
    ) -> Result<(), T::Error> {
        // [0x20, 0x7F) contains all printable ASCII characters ([' ', '~'])
        self.preload_chars(size, (0x20u8..0x7F).map(|ch| ch as char))
    }

    /// Return `ch` for `size` if it's already cached. Don't load.
    /// See the `preload_*` functions.
    pub fn opt_character(&self, size: FontSize, ch: char) -> Option<Character<T>> {
        self.data.get(&(size, ch)).map(|&Data {
            offset, advance_size, atlas_offset, atlas_size, texture
        }| {
            Character {
                offset,
                advance_size,
                atlas_offset,
                atlas_size,
                texture: &self.texture_packer.textures[texture],
            }
        })
    }
}

impl<'b, F, T: ImageSize> CharacterCache for GlyphCache<'b, F, T>
    where T: CreateTexture<F> + UpdateTexture<F>
{
    type Texture = T;
    type Error = T::Error;

    fn character<'a>(&'a mut self,
                     size: FontSize,
                     ch: char)
                     -> Result<Character<'a, T>, Self::Error> {
        use std::collections::hash_map::Entry;
        use self::rusttype as rt;

        let size = ((size as f32) * 1.333).round() as u32; // convert points to pixels

        match self.data.entry((size, ch)) {
            //returning `into_mut()' to get reference with 'a lifetime
            Entry::Occupied(v) => {
                let &mut Data {offset, advance_size, atlas_offset, atlas_size, texture} = v.into_mut();
                Ok(Character {
                    offset,
                    advance_size,
                    atlas_offset,
                    atlas_size,
                    texture: &self.texture_packer.textures[texture],
                })
            }
            Entry::Vacant(v) => {
                // this is only None for invalid GlyphIds,
                // but char is converted to a Codepoint which must result in a glyph.
                let glyph = self.font.glyph(ch);
                let scale = rt::Scale::uniform(size as f32);
                let mut glyph = glyph.scaled(scale);

                // some fonts do not contain glyph zero as fallback, instead try U+FFFD.
                if glyph.id() == rt::GlyphId(0) && glyph.shape().is_none() {
                    glyph = self.font.glyph('\u{FFFD}').scaled(scale);
                }

                let h_metrics = glyph.h_metrics();
                let bounding_box = glyph.exact_bounding_box().unwrap_or(rt::Rect {
                    min: rt::Point { x: 0.0, y: 0.0 },
                    max: rt::Point { x: 0.0, y: 0.0 },
                });
                let glyph = glyph.positioned(rt::point(0.0, 0.0));
                let pixel_bounding_box = glyph.pixel_bounding_box().unwrap_or(rt::Rect {
                    min: rt::Point { x: 0, y: 0 },
                    max: rt::Point { x: 0, y: 0 },
                });
                let size = [
                    (pixel_bounding_box.width() + 2) as u32,
                    (pixel_bounding_box.height() + 2) as u32,
                ];

                let &mut Data {
                    offset,
                    advance_size,
                    atlas_offset,
                    atlas_size,
                    texture
                } = match self.texture_packer.find_space(size) {
                    None => {
                        // Create a new texture atlas.
                        let mut image_buffer = Vec::<u8>::new();
                        let w = size[0].max(ATLAS_SIZE[0]) as u32;
                        let h = size[1].max(ATLAS_SIZE[1]) as u32;
                        image_buffer.resize((w * h) as usize, 0);
                        glyph.draw(|x, y, v| {
                            let pos = ((x + 1) + (y + 1) * w) as usize;
                            image_buffer[pos] = (255.0 * v) as u8;
                        });

                        let texture = self.texture_packer.create(size, {
                            if size[0] == 0 || size[1] == 0 {
                                empty(&mut self.factory, &self.settings)?
                            } else {
                                from_memory_alpha(&mut self.factory,
                                                  &image_buffer,
                                                  [w, h],
                                                  &self.settings)?
                            }
                        });
                        v.insert(Data {
                            offset: [bounding_box.min.x as Scalar - 1.0,
                                     -pixel_bounding_box.min.y as Scalar + 1.0],
                            advance_size: [h_metrics.advance_width as Scalar, 0.0],
                            atlas_offset: [0.0; 2],
                            atlas_size: [size[0] as Scalar, size[1] as Scalar],
                            texture
                        })
                    }
                    Some(ind) => {
                        // Use existing texture atlas.
                        let mut image_buffer = Vec::<u8>::new();
                        image_buffer.resize((size[0] * size[1]) as usize, 0);
                        glyph.draw(|x, y, v| {
                            let pos = ((x + 1) + (y + 1) * size[0]) as usize;
                            image_buffer[pos] = (255.0 * v) as u8;
                        });

                        let (texture, offset) = self.texture_packer.update(ind, size);

                        update_memory_alpha(
                            &mut self.texture_packer.textures[texture],
                            &mut self.factory,
                            &image_buffer,
                            offset,
                            size,
                        )?;
                        v.insert(Data {
                            offset: [bounding_box.min.x as Scalar - 1.0,
                                     -pixel_bounding_box.min.y as Scalar + 1.0],
                            advance_size: [h_metrics.advance_width as Scalar, 0.0],
                            atlas_offset: [offset[0] as Scalar, offset[1] as Scalar],
                            atlas_size: [size[0] as Scalar, size[1] as Scalar],
                            texture
                        })
                    }
                };
                Ok(Character {
                    offset,
                    advance_size,
                    atlas_offset,
                    atlas_size,
                    texture: &self.texture_packer.textures[texture],
                })
            }
        }
    }
}

fn empty<F, T: CreateTexture<F>>(factory: &mut F,
                                 settings: &TextureSettings)
                                 -> Result<T, T::Error> {
    CreateTexture::create(factory, Format::Rgba8, &[0u8; 4], [1, 1], settings)
}

fn from_memory_alpha<F, T: CreateTexture<F>>(factory: &mut F,
                                             buf: &[u8],
                                             size: [u32; 2],
                                             settings: &TextureSettings)
                                             -> Result<T, T::Error> {
    let buffer: Vec<u8> = ops::alpha_to_rgba8(buf, size);
    CreateTexture::create(factory, Format::Rgba8, &buffer, size, settings)
}

fn update_memory_alpha<F, T: UpdateTexture<F>>(
                                            texture: &mut T,
                                            factory: &mut F,
                                             buf: &[u8],
                                             offset: [u32; 2],
                                             size: [u32; 2])
                                             -> Result<(), T::Error> {
    let buffer: Vec<u8> = ops::alpha_to_rgba8(buf, size);
    texture.update(factory, Format::Rgba8, &buffer, offset, size)
}
<commit_msg>Fix compile errors<commit_after>//! Glyph caching using the RustType library.

extern crate rusttype;
use texture::{ops, CreateTexture, UpdateTexture, Format, TextureSettings};
use std::collections::HashMap;

extern crate fnv;
use self::fnv::FnvHasher;
use std::hash::BuildHasherDefault;

use std::path::Path;
use std::io::Read;
use std::fs::File;

use ImageSize;
use types::{FontSize, Scalar};
use character::{Character, CharacterCache};
use texture_packer::TexturePacker;

struct Data {
    offset: [Scalar; 2],
    advance_size: [Scalar; 2],
    atlas_offset: [Scalar; 2],
    atlas_size: [Scalar; 2],
    texture: usize,
}

struct EmptyOutlineBuilder;

impl rusttype::OutlineBuilder for EmptyOutlineBuilder {
    fn move_to(&mut self, _x: f32, _y: f32){}
    fn line_to(&mut self, _x: f32, _y: f32){}
    fn quad_to(&mut self, _x1: f32, _y1: f32, _x: f32, _y: f32){}
    fn curve_to(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32, _x: f32, _y: f32){}
    fn close(&mut self){}
}

/// The minimum atlas size.
pub const ATLAS_SIZE: [u32; 2] = [256; 2];

/// A struct used for caching rendered font.
pub struct GlyphCache<'a, F, T> {
    /// The font.
    pub font: rusttype::Font<'a>,
    /// The factory used to create textures.
    pub factory: F,
    /// The settings to render the font with.
    settings: TextureSettings,
    texture_packer: TexturePacker<T>,
    // Maps from fontsize and character to offset, texture offset, advance size and texture index.
    data: HashMap<(FontSize, char), Data, BuildHasherDefault<FnvHasher>>,
}

impl<'a, F, T> GlyphCache<'a, F, T>
    where T: CreateTexture<F> + UpdateTexture<F> + ImageSize
{
    /// Constructs a GlyphCache from a Font.
    pub fn from_font(font: rusttype::Font<'a>, factory: F, settings: TextureSettings) -> Self {
        let fnv = BuildHasherDefault::<FnvHasher>::default();
        GlyphCache {
            font: font,
            factory: factory,
            settings: settings,
            texture_packer: TexturePacker::new(),
            data: HashMap::with_hasher(fnv),
        }
    }

    /// Constructor for a GlyphCache.
    pub fn new<P>(font: P,
                  factory: F,
                  settings: TextureSettings)
                  -> ::std::io::Result<GlyphCache<'static, F, T>>
        where P: AsRef<Path>
    {
        let fnv = BuildHasherDefault::<FnvHasher>::default();
        let mut file = File::open(font)?;
        let mut file_buffer = Vec::new();
        file.read_to_end(&mut file_buffer)?;

        let font = rusttype::Font::try_from_vec(file_buffer)
            .ok_or(std::io::Error::new(std::io::ErrorKind::Other, "invalid font"))?;
        Ok(GlyphCache {
            font: font,
            factory: factory,
            settings: settings,
            texture_packer: TexturePacker::new(),
            data: HashMap::with_hasher(fnv),
        })
    }

    /// Creates a GlyphCache for a font stored in memory.
    pub fn from_bytes(font: &'a [u8],
                      factory: F,
                      settings: TextureSettings)
                      -> Result<GlyphCache<'a, F, T>, ()> {
        let font = rusttype::Font::try_from_bytes(font).ok_or(())?;
        Ok(Self::from_font(font, factory, settings))
    }

    /// Load all characters in the `chars` iterator for `size`
    pub fn preload_chars<I>(
        &mut self,
        size: FontSize,
        chars: I
    ) -> Result<(), T::Error>
        where I: Iterator<Item = char>
    {
        for ch in chars {
            self.character(size, ch)?;
        }
        Ok(())
    }

    /// Load all the printable ASCII characters for `size`. Includes space.
    pub fn preload_printable_ascii(
        &mut self,
        size: FontSize
    ) -> Result<(), T::Error> {
        // [0x20, 0x7F) contains all printable ASCII characters ([' ', '~'])
        self.preload_chars(size, (0x20u8..0x7F).map(|ch| ch as char))
    }

    /// Return `ch` for `size` if it's already cached. Don't load.
    /// See the `preload_*` functions.
    pub fn opt_character(&self, size: FontSize, ch: char) -> Option<Character<T>> {
        self.data.get(&(size, ch)).map(|&Data {
            offset, advance_size, atlas_offset, atlas_size, texture
        }| {
            Character {
                offset,
                advance_size,
                atlas_offset,
                atlas_size,
                texture: &self.texture_packer.textures[texture],
            }
        })
    }
}

impl<'b, F, T: ImageSize> CharacterCache for GlyphCache<'b, F, T>
    where T: CreateTexture<F> + UpdateTexture<F>
{
    type Texture = T;
    type Error = T::Error;

    fn character<'a>(&'a mut self,
                     size: FontSize,
                     ch: char)
                     -> Result<Character<'a, T>, Self::Error> {
        use std::collections::hash_map::Entry;
        use self::rusttype as rt;

        let size = ((size as f32) * 1.333).round() as u32; // convert points to pixels

        match self.data.entry((size, ch)) {
            //returning `into_mut()' to get reference with 'a lifetime
            Entry::Occupied(v) => {
                let &mut Data {offset, advance_size, atlas_offset, atlas_size, texture} = v.into_mut();
                Ok(Character {
                    offset,
                    advance_size,
                    atlas_offset,
                    atlas_size,
                    texture: &self.texture_packer.textures[texture],
                })
            }
            Entry::Vacant(v) => {
                // this is only None for invalid GlyphIds,
                // but char is converted to a Codepoint which must result in a glyph.
                let glyph = self.font.glyph(ch);
                let scale = rt::Scale::uniform(size as f32);
                let mut glyph = glyph.scaled(scale);

                // some fonts do not contain glyph zero as fallback, instead try U+FFFD.
                if glyph.id() == rt::GlyphId(0) && !glyph.build_outline(&mut EmptyOutlineBuilder) {
                    glyph = self.font.glyph('\u{FFFD}').scaled(scale);
                }

                let h_metrics = glyph.h_metrics();
                let bounding_box = glyph.exact_bounding_box().unwrap_or(rt::Rect {
                    min: rt::Point { x: 0.0, y: 0.0 },
                    max: rt::Point { x: 0.0, y: 0.0 },
                });
                let glyph = glyph.positioned(rt::point(0.0, 0.0));
                let pixel_bounding_box = glyph.pixel_bounding_box().unwrap_or(rt::Rect {
                    min: rt::Point { x: 0, y: 0 },
                    max: rt::Point { x: 0, y: 0 },
                });
                let size = [
                    (pixel_bounding_box.width() + 2) as u32,
                    (pixel_bounding_box.height() + 2) as u32,
                ];

                let &mut Data {
                    offset,
                    advance_size,
                    atlas_offset,
                    atlas_size,
                    texture
                } = match self.texture_packer.find_space(size) {
                    None => {
                        // Create a new texture atlas.
                        let mut image_buffer = Vec::<u8>::new();
                        let w = size[0].max(ATLAS_SIZE[0]) as u32;
                        let h = size[1].max(ATLAS_SIZE[1]) as u32;
                        image_buffer.resize((w * h) as usize, 0);
                        glyph.draw(|x, y, v| {
                            let pos = ((x + 1) + (y + 1) * w) as usize;
                            image_buffer[pos] = (255.0 * v) as u8;
                        });

                        let texture = self.texture_packer.create(size, {
                            if size[0] == 0 || size[1] == 0 {
                                empty(&mut self.factory, &self.settings)?
                            } else {
                                from_memory_alpha(&mut self.factory,
                                                  &image_buffer,
                                                  [w, h],
                                                  &self.settings)?
                            }
                        });
                        v.insert(Data {
                            offset: [bounding_box.min.x as Scalar - 1.0,
                                     -pixel_bounding_box.min.y as Scalar + 1.0],
                            advance_size: [h_metrics.advance_width as Scalar, 0.0],
                            atlas_offset: [0.0; 2],
                            atlas_size: [size[0] as Scalar, size[1] as Scalar],
                            texture
                        })
                    }
                    Some(ind) => {
                        // Use existing texture atlas.
                        let mut image_buffer = Vec::<u8>::new();
                        image_buffer.resize((size[0] * size[1]) as usize, 0);
                        glyph.draw(|x, y, v| {
                            let pos = ((x + 1) + (y + 1) * size[0]) as usize;
                            image_buffer[pos] = (255.0 * v) as u8;
                        });

                        let (texture, offset) = self.texture_packer.update(ind, size);

                        update_memory_alpha(
                            &mut self.texture_packer.textures[texture],
                            &mut self.factory,
                            &image_buffer,
                            offset,
                            size,
                        )?;
                        v.insert(Data {
                            offset: [bounding_box.min.x as Scalar - 1.0,
                                     -pixel_bounding_box.min.y as Scalar + 1.0],
                            advance_size: [h_metrics.advance_width as Scalar, 0.0],
                            atlas_offset: [offset[0] as Scalar, offset[1] as Scalar],
                            atlas_size: [size[0] as Scalar, size[1] as Scalar],
                            texture
                        })
                    }
                };
                Ok(Character {
                    offset,
                    advance_size,
                    atlas_offset,
                    atlas_size,
                    texture: &self.texture_packer.textures[texture],
                })
            }
        }
    }
}

fn empty<F, T: CreateTexture<F>>(factory: &mut F,
                                 settings: &TextureSettings)
                                 -> Result<T, T::Error> {
    CreateTexture::create(factory, Format::Rgba8, &[0u8; 4], [1, 1], settings)
}

fn from_memory_alpha<F, T: CreateTexture<F>>(factory: &mut F,
                                             buf: &[u8],
                                             size: [u32; 2],
                                             settings: &TextureSettings)
                                             -> Result<T, T::Error> {
    let buffer: Vec<u8> = ops::alpha_to_rgba8(buf, size);
    CreateTexture::create(factory, Format::Rgba8, &buffer, size, settings)
}

fn update_memory_alpha<F, T: UpdateTexture<F>>(
                                            texture: &mut T,
                                            factory: &mut F,
                                             buf: &[u8],
                                             offset: [u32; 2],
                                             size: [u32; 2])
                                             -> Result<(), T::Error> {
    let buffer: Vec<u8> = ops::alpha_to_rgba8(buf, size);
    texture.update(factory, Format::Rgba8, &buffer, offset, size)
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here
mod expr;
mod match_check;
mod unsafe_check;

use std::any::Any;

use hir_def::DefWithBodyId;
use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
use ra_prof::profile;
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
use stdx::format_to;

use crate::db::HirDatabase;

pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};

pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
    let _p = profile("validate_body");
    let infer = db.infer(owner);
    infer.add_diagnostics(db, owner, sink);
    let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
    validator.validate_body(db);
    let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
    validator.validate_body(db);
}

#[derive(Debug)]
pub struct NoSuchField {
    pub file: HirFileId,
    pub field: AstPtr<ast::RecordField>,
}

impl Diagnostic for NoSuchField {
    fn message(&self) -> String {
        "no such field".to_string()
    }

    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.field.clone().into())
    }

    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for NoSuchField {
    type AST = ast::RecordField;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.source().file_id).unwrap();
        let node = self.source().value.to_node(&root);
        ast::RecordField::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct MissingFields {
    pub file: HirFileId,
    pub field_list: AstPtr<ast::RecordFieldList>,
    pub missed_fields: Vec<Name>,
}

impl Diagnostic for MissingFields {
    fn message(&self) -> String {
        let mut buf = String::from("Missing structure fields:\n");
        for field in &self.missed_fields {
            format_to!(buf, "- {}\n", field);
        }
        buf
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.field_list.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for MissingFields {
    type AST = ast::RecordFieldList;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.source().file_id).unwrap();
        let node = self.source().value.to_node(&root);
        ast::RecordFieldList::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct MissingPatFields {
    pub file: HirFileId,
    pub field_list: AstPtr<ast::RecordFieldPatList>,
    pub missed_fields: Vec<Name>,
}

impl Diagnostic for MissingPatFields {
    fn message(&self) -> String {
        let mut buf = String::from("Missing structure fields:\n");
        for field in &self.missed_fields {
            format_to!(buf, "- {}\n", field);
        }
        buf
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.field_list.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

#[derive(Debug)]
pub struct MissingMatchArms {
    pub file: HirFileId,
    pub match_expr: AstPtr<ast::Expr>,
    pub arms: AstPtr<ast::MatchArmList>,
}

impl Diagnostic for MissingMatchArms {
    fn message(&self) -> String {
        String::from("Missing match arm")
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.match_expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

#[derive(Debug)]
pub struct MissingOkInTailExpr {
    pub file: HirFileId,
    pub expr: AstPtr<ast::Expr>,
}

impl Diagnostic for MissingOkInTailExpr {
    fn message(&self) -> String {
        "wrap return expression in Ok".to_string()
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for MissingOkInTailExpr {
    type AST = ast::Expr;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.file).unwrap();
        let node = self.source().value.to_node(&root);
        ast::Expr::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct BreakOutsideOfLoop {
    pub file: HirFileId,
    pub expr: AstPtr<ast::Expr>,
}

impl Diagnostic for BreakOutsideOfLoop {
    fn message(&self) -> String {
        "break outside of loop".to_string()
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for BreakOutsideOfLoop {
    type AST = ast::Expr;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.file).unwrap();
        let node = self.source().value.to_node(&root);
        ast::Expr::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct MissingUnsafe {
    pub file: HirFileId,
    pub expr: AstPtr<ast::Expr>,
}

impl Diagnostic for MissingUnsafe {
    fn message(&self) -> String {
        format!("This operation is unsafe and requires an unsafe function or block")
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for MissingUnsafe {
    type AST = ast::Expr;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.source().file_id).unwrap();
        let node = self.source().value.to_node(&root);
        ast::Expr::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct MismatchedArgCount {
    pub file: HirFileId,
    pub call_expr: AstPtr<ast::Expr>,
    pub expected: usize,
    pub found: usize,
}

impl Diagnostic for MismatchedArgCount {
    fn message(&self) -> String {
        let s = if self.expected == 1 { "" } else { "s" };
        format!("Expected {} argument{}, found {}", self.expected, s, self.found)
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.call_expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for MismatchedArgCount {
    type AST = ast::CallExpr;
    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.source().file_id).unwrap();
        let node = self.source().value.to_node(&root);
        ast::CallExpr::cast(node).unwrap()
    }
}

#[cfg(test)]
mod tests {
    use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
    use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder};
    use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
    use ra_syntax::{TextRange, TextSize};
    use rustc_hash::FxHashMap;

    use crate::{diagnostics::validate_body, test_db::TestDB};

    impl TestDB {
        fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
            let crate_graph = self.crate_graph();
            for krate in crate_graph.iter() {
                let crate_def_map = self.crate_def_map(krate);

                let mut fns = Vec::new();
                for (module_id, _) in crate_def_map.modules.iter() {
                    for decl in crate_def_map[module_id].scope.declarations() {
                        if let ModuleDefId::FunctionId(f) = decl {
                            fns.push(f)
                        }
                    }

                    for impl_id in crate_def_map[module_id].scope.impls() {
                        let impl_data = self.impl_data(impl_id);
                        for item in impl_data.items.iter() {
                            if let AssocItemId::FunctionId(f) = item {
                                fns.push(*f)
                            }
                        }
                    }
                }

                for f in fns {
                    let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
                    validate_body(self, f.into(), &mut sink);
                }
            }
        }
    }

    pub(crate) fn check_diagnostics(ra_fixture: &str) {
        let db = TestDB::with_files(ra_fixture);
        let annotations = db.extract_annotations();

        let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
        db.diagnostics(|d| {
            // FXIME: macros...
            let file_id = d.source().file_id.original_file(&db);
            let range = d.syntax_node(&db).text_range();
            let message = d.message().to_owned();
            actual.entry(file_id).or_default().push((range, message));
        });

        for (file_id, diags) in actual.iter_mut() {
            diags.sort_by_key(|it| it.0.start());
            let text = db.file_text(*file_id);
            // For multiline spans, place them on line start
            for (range, content) in diags {
                if text[*range].contains('\n') {
                    *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
                    *content = format!("... {}", content);
                }
            }
        }

        assert_eq!(annotations, actual);
    }

    #[test]
    fn no_such_field_diagnostics() {
        check_diagnostics(
            r#"
struct S { foo: i32, bar: () }
impl S {
    fn new() -> S {
        S {
        //^... Missing structure fields:
        //|    - bar
            foo: 92,
            baz: 62,
          //^^^^^^^ no such field
        }
    }
}
"#,
        );
    }
    #[test]
    fn no_such_field_with_feature_flag_diagnostics() {
        check_diagnostics(
            r#"
//- /lib.rs crate:foo cfg:feature=foo
struct MyStruct {
    my_val: usize,
    #[cfg(feature = "foo")]
    bar: bool,
}

impl MyStruct {
    #[cfg(feature = "foo")]
    pub(crate) fn new(my_val: usize, bar: bool) -> Self {
        Self { my_val, bar }
    }
    #[cfg(not(feature = "foo"))]
    pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
        Self { my_val }
    }
}
"#,
        );
    }

    #[test]
    fn no_such_field_enum_with_feature_flag_diagnostics() {
        check_diagnostics(
            r#"
//- /lib.rs crate:foo cfg:feature=foo
enum Foo {
    #[cfg(not(feature = "foo"))]
    Buz,
    #[cfg(feature = "foo")]
    Bar,
    Baz
}

fn test_fn(f: Foo) {
    match f {
        Foo::Bar => {},
        Foo::Baz => {},
    }
}
"#,
        );
    }

    #[test]
    fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
        check_diagnostics(
            r#"
//- /lib.rs crate:foo cfg:feature=foo
struct S {
    #[cfg(feature = "foo")]
    foo: u32,
    #[cfg(not(feature = "foo"))]
    bar: u32,
}

impl S {
    #[cfg(feature = "foo")]
    fn new(foo: u32) -> Self {
        Self { foo }
    }
    #[cfg(not(feature = "foo"))]
    fn new(bar: u32) -> Self {
        Self { bar }
    }
    fn new2(bar: u32) -> Self {
        #[cfg(feature = "foo")]
        { Self { foo: bar } }
        #[cfg(not(feature = "foo"))]
        { Self { bar } }
    }
    fn new2(val: u32) -> Self {
        Self {
            #[cfg(feature = "foo")]
            foo: val,
            #[cfg(not(feature = "foo"))]
            bar: val,
        }
    }
}
"#,
        );
    }

    #[test]
    fn no_such_field_with_type_macro() {
        check_diagnostics(
            r#"
macro_rules! Type { () => { u32 }; }
struct Foo { bar: Type![] }

impl Foo {
    fn new() -> Self {
        Foo { bar: 0 }
    }
}
"#,
        );
    }

    #[test]
    fn missing_record_pat_field_diagnostic() {
        check_diagnostics(
            r#"
struct S { foo: i32, bar: () }
fn baz(s: S) {
    let S { foo: _ } = s;
        //^^^^^^^^^^ Missing structure fields:
        //         | - bar
}
"#,
        );
    }

    #[test]
    fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
        check_diagnostics(
            r"
struct S { foo: i32, bar: () }
fn baz(s: S) -> i32 {
    match s {
        S { foo, .. } => foo,
    }
}
",
        )
    }

    #[test]
    fn break_outside_of_loop() {
        check_diagnostics(
            r#"
fn foo() { break; }
         //^^^^^ break outside of loop
"#,
        );
    }
}
<commit_msg>Mark MismatchedArgCount as experimental<commit_after>//! FIXME: write short doc here
mod expr;
mod match_check;
mod unsafe_check;

use std::any::Any;

use hir_def::DefWithBodyId;
use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
use ra_prof::profile;
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
use stdx::format_to;

use crate::db::HirDatabase;

pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};

pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
    let _p = profile("validate_body");
    let infer = db.infer(owner);
    infer.add_diagnostics(db, owner, sink);
    let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
    validator.validate_body(db);
    let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
    validator.validate_body(db);
}

#[derive(Debug)]
pub struct NoSuchField {
    pub file: HirFileId,
    pub field: AstPtr<ast::RecordField>,
}

impl Diagnostic for NoSuchField {
    fn message(&self) -> String {
        "no such field".to_string()
    }

    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.field.clone().into())
    }

    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for NoSuchField {
    type AST = ast::RecordField;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.source().file_id).unwrap();
        let node = self.source().value.to_node(&root);
        ast::RecordField::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct MissingFields {
    pub file: HirFileId,
    pub field_list: AstPtr<ast::RecordFieldList>,
    pub missed_fields: Vec<Name>,
}

impl Diagnostic for MissingFields {
    fn message(&self) -> String {
        let mut buf = String::from("Missing structure fields:\n");
        for field in &self.missed_fields {
            format_to!(buf, "- {}\n", field);
        }
        buf
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.field_list.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for MissingFields {
    type AST = ast::RecordFieldList;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.source().file_id).unwrap();
        let node = self.source().value.to_node(&root);
        ast::RecordFieldList::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct MissingPatFields {
    pub file: HirFileId,
    pub field_list: AstPtr<ast::RecordFieldPatList>,
    pub missed_fields: Vec<Name>,
}

impl Diagnostic for MissingPatFields {
    fn message(&self) -> String {
        let mut buf = String::from("Missing structure fields:\n");
        for field in &self.missed_fields {
            format_to!(buf, "- {}\n", field);
        }
        buf
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.field_list.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

#[derive(Debug)]
pub struct MissingMatchArms {
    pub file: HirFileId,
    pub match_expr: AstPtr<ast::Expr>,
    pub arms: AstPtr<ast::MatchArmList>,
}

impl Diagnostic for MissingMatchArms {
    fn message(&self) -> String {
        String::from("Missing match arm")
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.match_expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

#[derive(Debug)]
pub struct MissingOkInTailExpr {
    pub file: HirFileId,
    pub expr: AstPtr<ast::Expr>,
}

impl Diagnostic for MissingOkInTailExpr {
    fn message(&self) -> String {
        "wrap return expression in Ok".to_string()
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for MissingOkInTailExpr {
    type AST = ast::Expr;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.file).unwrap();
        let node = self.source().value.to_node(&root);
        ast::Expr::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct BreakOutsideOfLoop {
    pub file: HirFileId,
    pub expr: AstPtr<ast::Expr>,
}

impl Diagnostic for BreakOutsideOfLoop {
    fn message(&self) -> String {
        "break outside of loop".to_string()
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for BreakOutsideOfLoop {
    type AST = ast::Expr;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.file).unwrap();
        let node = self.source().value.to_node(&root);
        ast::Expr::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct MissingUnsafe {
    pub file: HirFileId,
    pub expr: AstPtr<ast::Expr>,
}

impl Diagnostic for MissingUnsafe {
    fn message(&self) -> String {
        format!("This operation is unsafe and requires an unsafe function or block")
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

impl AstDiagnostic for MissingUnsafe {
    type AST = ast::Expr;

    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.source().file_id).unwrap();
        let node = self.source().value.to_node(&root);
        ast::Expr::cast(node).unwrap()
    }
}

#[derive(Debug)]
pub struct MismatchedArgCount {
    pub file: HirFileId,
    pub call_expr: AstPtr<ast::Expr>,
    pub expected: usize,
    pub found: usize,
}

impl Diagnostic for MismatchedArgCount {
    fn message(&self) -> String {
        let s = if self.expected == 1 { "" } else { "s" };
        format!("Expected {} argument{}, found {}", self.expected, s, self.found)
    }
    fn source(&self) -> InFile<SyntaxNodePtr> {
        InFile { file_id: self.file, value: self.call_expr.clone().into() }
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
    fn is_experimental(&self) -> bool {
        true
    }
}

impl AstDiagnostic for MismatchedArgCount {
    type AST = ast::CallExpr;
    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
        let root = db.parse_or_expand(self.source().file_id).unwrap();
        let node = self.source().value.to_node(&root);
        ast::CallExpr::cast(node).unwrap()
    }
}

#[cfg(test)]
mod tests {
    use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
    use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder};
    use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
    use ra_syntax::{TextRange, TextSize};
    use rustc_hash::FxHashMap;

    use crate::{diagnostics::validate_body, test_db::TestDB};

    impl TestDB {
        fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
            let crate_graph = self.crate_graph();
            for krate in crate_graph.iter() {
                let crate_def_map = self.crate_def_map(krate);

                let mut fns = Vec::new();
                for (module_id, _) in crate_def_map.modules.iter() {
                    for decl in crate_def_map[module_id].scope.declarations() {
                        if let ModuleDefId::FunctionId(f) = decl {
                            fns.push(f)
                        }
                    }

                    for impl_id in crate_def_map[module_id].scope.impls() {
                        let impl_data = self.impl_data(impl_id);
                        for item in impl_data.items.iter() {
                            if let AssocItemId::FunctionId(f) = item {
                                fns.push(*f)
                            }
                        }
                    }
                }

                for f in fns {
                    let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
                    validate_body(self, f.into(), &mut sink);
                }
            }
        }
    }

    pub(crate) fn check_diagnostics(ra_fixture: &str) {
        let db = TestDB::with_files(ra_fixture);
        let annotations = db.extract_annotations();

        let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
        db.diagnostics(|d| {
            // FXIME: macros...
            let file_id = d.source().file_id.original_file(&db);
            let range = d.syntax_node(&db).text_range();
            let message = d.message().to_owned();
            actual.entry(file_id).or_default().push((range, message));
        });

        for (file_id, diags) in actual.iter_mut() {
            diags.sort_by_key(|it| it.0.start());
            let text = db.file_text(*file_id);
            // For multiline spans, place them on line start
            for (range, content) in diags {
                if text[*range].contains('\n') {
                    *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
                    *content = format!("... {}", content);
                }
            }
        }

        assert_eq!(annotations, actual);
    }

    #[test]
    fn no_such_field_diagnostics() {
        check_diagnostics(
            r#"
struct S { foo: i32, bar: () }
impl S {
    fn new() -> S {
        S {
        //^... Missing structure fields:
        //|    - bar
            foo: 92,
            baz: 62,
          //^^^^^^^ no such field
        }
    }
}
"#,
        );
    }
    #[test]
    fn no_such_field_with_feature_flag_diagnostics() {
        check_diagnostics(
            r#"
//- /lib.rs crate:foo cfg:feature=foo
struct MyStruct {
    my_val: usize,
    #[cfg(feature = "foo")]
    bar: bool,
}

impl MyStruct {
    #[cfg(feature = "foo")]
    pub(crate) fn new(my_val: usize, bar: bool) -> Self {
        Self { my_val, bar }
    }
    #[cfg(not(feature = "foo"))]
    pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
        Self { my_val }
    }
}
"#,
        );
    }

    #[test]
    fn no_such_field_enum_with_feature_flag_diagnostics() {
        check_diagnostics(
            r#"
//- /lib.rs crate:foo cfg:feature=foo
enum Foo {
    #[cfg(not(feature = "foo"))]
    Buz,
    #[cfg(feature = "foo")]
    Bar,
    Baz
}

fn test_fn(f: Foo) {
    match f {
        Foo::Bar => {},
        Foo::Baz => {},
    }
}
"#,
        );
    }

    #[test]
    fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
        check_diagnostics(
            r#"
//- /lib.rs crate:foo cfg:feature=foo
struct S {
    #[cfg(feature = "foo")]
    foo: u32,
    #[cfg(not(feature = "foo"))]
    bar: u32,
}

impl S {
    #[cfg(feature = "foo")]
    fn new(foo: u32) -> Self {
        Self { foo }
    }
    #[cfg(not(feature = "foo"))]
    fn new(bar: u32) -> Self {
        Self { bar }
    }
    fn new2(bar: u32) -> Self {
        #[cfg(feature = "foo")]
        { Self { foo: bar } }
        #[cfg(not(feature = "foo"))]
        { Self { bar } }
    }
    fn new2(val: u32) -> Self {
        Self {
            #[cfg(feature = "foo")]
            foo: val,
            #[cfg(not(feature = "foo"))]
            bar: val,
        }
    }
}
"#,
        );
    }

    #[test]
    fn no_such_field_with_type_macro() {
        check_diagnostics(
            r#"
macro_rules! Type { () => { u32 }; }
struct Foo { bar: Type![] }

impl Foo {
    fn new() -> Self {
        Foo { bar: 0 }
    }
}
"#,
        );
    }

    #[test]
    fn missing_record_pat_field_diagnostic() {
        check_diagnostics(
            r#"
struct S { foo: i32, bar: () }
fn baz(s: S) {
    let S { foo: _ } = s;
        //^^^^^^^^^^ Missing structure fields:
        //         | - bar
}
"#,
        );
    }

    #[test]
    fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
        check_diagnostics(
            r"
struct S { foo: i32, bar: () }
fn baz(s: S) -> i32 {
    match s {
        S { foo, .. } => foo,
    }
}
",
        )
    }

    #[test]
    fn break_outside_of_loop() {
        check_diagnostics(
            r#"
fn foo() { break; }
         //^^^^^ break outside of loop
"#,
        );
    }
}
<|endoftext|>