<commit_before>use gfx;
use gfx::traits::FactoryExt;

use core::resource;

pub type HDR = (gfx::format::R16_G16_B16_A16, gfx::format::Float);
pub type LDR = gfx::format::Srgba8;

gfx_defines!{
	vertex BlitVertex {
		pos: [f32; 2] = "a_Pos",
		tex_coord: [f32; 2] = "a_TexCoord",
	}
	pipeline postprocess {
		vbuf: gfx::VertexBuffer<BlitVertex> = (),
		src: gfx::TextureSampler<[f32; 4]> = "t_Source",
		dst: gfx::RenderTarget<HDR> = "o_Color",
	}
	constant SmoothFragmentArgs {
        exp_alpha: f32 = "u_ExpAlpha",
    }
	pipeline smooth {
		vbuf: gfx::VertexBuffer<BlitVertex> = (),
		value: gfx::TextureSampler<[f32; 4]> = "t_Value",
		acc: gfx::TextureSampler<[f32; 4]> = "t_Acc",
		fragment_args: gfx::ConstantBuffer<SmoothFragmentArgs> = "cb_FragmentArgs",
		dst: gfx::RenderTarget<HDR> = "o_Smooth",
	}
	constant ToneMapVertexArgs {
        white: f32 = "u_White",
        black: f32 = "u_Black",
    }
	pipeline tone_map {
		vbuf: gfx::VertexBuffer<BlitVertex> = (),
		vertex_luminance: gfx::TextureSampler<[f32; 4]> = "t_VertexLuminance",
		vertex_args: gfx::ConstantBuffer<ToneMapVertexArgs> = "cb_VertexArgs",
		src: gfx::TextureSampler<[f32; 4]> = "t_Source",
		dst: gfx::RenderTarget<HDR> = "o_Color",
	}
	pipeline compose {
		vbuf: gfx::VertexBuffer<BlitVertex> = (),
		src1: gfx::TextureSampler<[f32; 4]> = "t_Source1",
		src2: gfx::TextureSampler<[f32; 4]> = "t_Source2",
		dst: gfx::RenderTarget<LDR> = "o_Color",
	}
}

use std::marker::PhantomData;
type HDRRenderSurface<R> = (gfx::handle::Texture<R, gfx::format::R16_G16_B16_A16>,
                            gfx::handle::ShaderResourceView<R, [f32; 4]>,
                            gfx::handle::RenderTargetView<R, (gfx::format::R16_G16_B16_A16, gfx::format::Float)>);
pub struct PostLighting<R: gfx::Resources, C: gfx::CommandBuffer<R>> {
	vertex_buffer: gfx::handle::Buffer<R, BlitVertex>,
	index_buffer_slice: gfx::Slice<R>,
	nearest_sampler: gfx::handle::Sampler<R>,
	linear_sampler: gfx::handle::Sampler<R>,

	ping_pong_small: [HDRRenderSurface<R>; 2],
	ping_pong_large: [HDRRenderSurface<R>; 2],

	mips: Vec<HDRRenderSurface<R>>,
	luminance_smooth: HDRRenderSurface<R>,
	luminance_acc: HDRRenderSurface<R>,

	blit_pso: gfx::pso::PipelineState<R, postprocess::Meta>,
	average_pso: gfx::pso::PipelineState<R, postprocess::Meta>,

	smooth_fragment_args: gfx::handle::Buffer<R, SmoothFragmentArgs>,
	smooth_pso: gfx::pso::PipelineState<R, smooth::Meta>,

	highlight_pso: gfx::pso::PipelineState<R, postprocess::Meta>,
	blur_h_pso: gfx::pso::PipelineState<R, postprocess::Meta>,
	blur_v_pso: gfx::pso::PipelineState<R, postprocess::Meta>,

	tone_map_vertex_args: gfx::handle::Buffer<R, ToneMapVertexArgs>,
	tone_map_pso: gfx::pso::PipelineState<R, tone_map::Meta>,

	compose_pso: gfx::pso::PipelineState<R, compose::Meta>,

	_buffer: PhantomData<C>,
}

impl<R: gfx::Resources, C: gfx::CommandBuffer<R>> PostLighting<R, C> {
	pub fn new<F>(factory: &mut F, res: &resource::ResourceLoader<u8>, w: u16, h: u16) -> PostLighting<R, C>
		where F: gfx::Factory<R> {

		let full_screen_triangle = vec![BlitVertex {
			                                pos: [-1., -1.],
			                                tex_coord: [0., 0.],
		                                },
		                                BlitVertex {
			                                pos: [-1., 3.],
			                                tex_coord: [0., 2.],
		                                },
		                                BlitVertex {
			                                pos: [3., -1.],
			                                tex_coord: [2., 0.],
		                                }];

		let (vertex_buffer, slice) = factory.create_vertex_buffer_with_slice(&full_screen_triangle, ());

		let nearest_sampler = factory.create_sampler(gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Scale,
		                                                                        gfx::tex::WrapMode::Clamp));

		let linear_sampler = factory.create_sampler(gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Bilinear,
		                                                                       gfx::tex::WrapMode::Clamp));

		let tone_map_vertex_args = factory.create_constant_buffer(1);
		let smooth_fragment_args = factory.create_constant_buffer(1);

		macro_rules! load_pipeline_simple {
			($v:expr, $f:expr, $s:ident) => { factory.create_pipeline_simple(
					&res.load(concat!("shaders/effects/", $v, ".vert")).unwrap(),
					&res.load(concat!("shaders/effects/", $f, ".frag")).unwrap(),
					$s::new()
				)
				.unwrap() }
		};

		let tone_map_pso = load_pipeline_simple!("luminance", "exposure_tone_map", tone_map);
		let highlight_pso = load_pipeline_simple!("identity", "clip_luminance", postprocess);
		let blur_h_pso = load_pipeline_simple!("identity", "gaussian_blur_horizontal", postprocess);
		let blur_v_pso = load_pipeline_simple!("identity", "gaussian_blur_vertical", postprocess);
		let blit_pso = load_pipeline_simple!("identity", "simple_blit", postprocess);
		let smooth_pso = load_pipeline_simple!("identity", "exponential_smooth", smooth);
		let average_pso = load_pipeline_simple!("identity", "quad_smooth", postprocess);
		let compose_pso = load_pipeline_simple!("identity", "compose_2", compose);

		let ping_pong_small = [factory.create_render_target::<HDR>(w / 4, h / 4).unwrap(),
		                       factory.create_render_target::<HDR>(w / 4, h / 4).unwrap()];

		let ping_pong_large = [factory.create_render_target::<HDR>(w, h).unwrap(),
		                       factory.create_render_target::<HDR>(w, h).unwrap()];

		let mut mips: Vec<HDRRenderSurface<R>> = Vec::new();
		let mut w2 = w;
		let mut h2 = h;

		while w2 > 1 || h2 > 1 {
			w2 = (w2 + 1) / 2;
			h2 = (h2 + 1) / 2;
			// println!("{}x{}", w2, h2);
			mips.push(factory.create_render_target::<HDR>(w2, h2).unwrap());
		}

		let luminance_smooth = factory.create_render_target::<HDR>(1, 1).unwrap();
		let luminance_acc = factory.create_render_target::<HDR>(1, 1).unwrap();

		PostLighting {
			vertex_buffer: vertex_buffer,
			index_buffer_slice: slice,
			nearest_sampler: nearest_sampler,
			linear_sampler: linear_sampler,

			blit_pso: blit_pso,
			average_pso: average_pso,

			smooth_fragment_args: smooth_fragment_args,
			smooth_pso: smooth_pso,

			tone_map_vertex_args: tone_map_vertex_args,
			tone_map_pso: tone_map_pso,

			highlight_pso: highlight_pso,
			blur_h_pso: blur_h_pso,
			blur_v_pso: blur_v_pso,

			compose_pso: compose_pso,

			mips: mips,
			luminance_smooth: luminance_smooth,
			luminance_acc: luminance_acc,

			ping_pong_small: ping_pong_small,
			ping_pong_large: ping_pong_large,

			_buffer: PhantomData,
		}
	}

	fn full_screen_pass(&self,
	                    encoder: &mut gfx::Encoder<R, C>,
	                    pso: &gfx::pso::PipelineState<R, postprocess::Meta>,
	                    src: &gfx::handle::ShaderResourceView<R, [f32; 4]>,
	                    dst: &gfx::handle::RenderTargetView<R, HDR>) {
		encoder.draw(&self.index_buffer_slice,
		             pso,
		             &postprocess::Data {
			             vbuf: self.vertex_buffer.clone(),
			             src: (src.clone(), self.nearest_sampler.clone()),
			             dst: (dst.clone()),
		             });
	}

	pub fn apply_all(&mut self,
	                 encoder: &mut gfx::Encoder<R, C>,
	                 raw_hdr_src: gfx::handle::ShaderResourceView<R, [f32; 4]>,
	                 color_target: gfx::handle::RenderTargetView<R, LDR>) {

		let ping_pong_large = &self.ping_pong_large[..];
		let ping_pong_small = &self.ping_pong_small[..];

		// get average lum
		let mut exposure_src = raw_hdr_src.clone();
		let mut exposure_dst;
		// this is a fold, can we do it functionally?
		for mip in &self.mips {
			exposure_dst = mip.2.clone();
			self.full_screen_pass(encoder, &self.average_pso, &exposure_src, &exposure_dst);
			exposure_src = mip.1.clone();
			// println!("{:?}", mip.0.clone());
		}

		// Exponential smoothing
		encoder.update_constant_buffer(&self.smooth_fragment_args,
		                               &SmoothFragmentArgs { exp_alpha: 0.05 });
		encoder.draw(&self.index_buffer_slice,
		             &self.smooth_pso,
		             &smooth::Data {
			             vbuf: self.vertex_buffer.clone(),
			             fragment_args: self.smooth_fragment_args.clone(),
			             value: (exposure_src, self.nearest_sampler.clone()),
			             acc: (self.luminance_acc.1.clone(), self.nearest_sampler.clone()),
			             dst: self.luminance_smooth.2.clone(),
		             });

		// Tone mapping
		encoder.update_constant_buffer(&self.tone_map_vertex_args,
		                               &ToneMapVertexArgs {
			                               white: 4.0,
			                               black: 0.5,
		                               });
		encoder.draw(&self.index_buffer_slice,
		             &self.tone_map_pso,
		             &tone_map::Data {
			             vbuf: self.vertex_buffer.clone(),
			             vertex_luminance: (self.luminance_smooth.1.clone(),
			                                self.nearest_sampler.clone()),
			             vertex_args: self.tone_map_vertex_args.clone(),
			             src: (raw_hdr_src, self.nearest_sampler.clone()),
			             dst: ping_pong_large[0].2.clone(),
		             });

		// blits smoothed luminance to "acc" buffer. TODO: pingpong
		self.full_screen_pass(encoder,
		                      &self.blit_pso,
		                      &self.luminance_smooth.1,
		                      &self.luminance_acc.2);
		// Bloom
		// 1. extract high luminance
		self.full_screen_pass(encoder,
		                      &self.highlight_pso,
		                      &ping_pong_large[0].1,
		                      &ping_pong_small[0].2);
		// 2. horizontal 4x, 9x9 gaussian blur
		self.full_screen_pass(encoder,
		                      &self.blur_h_pso,
		                      &ping_pong_small[0].1,
		                      &ping_pong_small[1].2);
		// 2. vertical 4x, 9x9 gaussian blur
		self.full_screen_pass(encoder,
		                      &self.blur_v_pso,
		                      &ping_pong_small[1].1,
		                      &ping_pong_small[0].2);

		// compose tone mapped + bloom and resolve
		encoder.draw(&self.index_buffer_slice,
		             &self.compose_pso,
		             &compose::Data {
			             vbuf: self.vertex_buffer.clone(),
			             src1: (ping_pong_large[0].1.clone(), self.nearest_sampler.clone()),
			             // src1: (self.mips[10].1.clone(), self.nearest_sampler.clone()),
			             src2: (ping_pong_small[0].1.clone(), self.linear_sampler.clone()),
			             dst: color_target.clone(),
		             });
	}
}
<commit_msg>Reduced bloom size and increased stability<commit_after>use gfx;
use gfx::traits::FactoryExt;

use core::resource;

pub type HDR = (gfx::format::R16_G16_B16_A16, gfx::format::Float);
pub type LDR = gfx::format::Srgba8;

gfx_defines!{
	vertex BlitVertex {
		pos: [f32; 2] = "a_Pos",
		tex_coord: [f32; 2] = "a_TexCoord",
	}
	pipeline postprocess {
		vbuf: gfx::VertexBuffer<BlitVertex> = (),
		src: gfx::TextureSampler<[f32; 4]> = "t_Source",
		dst: gfx::RenderTarget<HDR> = "o_Color",
	}
	constant SmoothFragmentArgs {
        exp_alpha: f32 = "u_ExpAlpha",
    }
	pipeline smooth {
		vbuf: gfx::VertexBuffer<BlitVertex> = (),
		value: gfx::TextureSampler<[f32; 4]> = "t_Value",
		acc: gfx::TextureSampler<[f32; 4]> = "t_Acc",
		fragment_args: gfx::ConstantBuffer<SmoothFragmentArgs> = "cb_FragmentArgs",
		dst: gfx::RenderTarget<HDR> = "o_Smooth",
	}
	constant ToneMapVertexArgs {
        white: f32 = "u_White",
        black: f32 = "u_Black",
    }
	pipeline tone_map {
		vbuf: gfx::VertexBuffer<BlitVertex> = (),
		vertex_luminance: gfx::TextureSampler<[f32; 4]> = "t_VertexLuminance",
		vertex_args: gfx::ConstantBuffer<ToneMapVertexArgs> = "cb_VertexArgs",
		src: gfx::TextureSampler<[f32; 4]> = "t_Source",
		dst: gfx::RenderTarget<HDR> = "o_Color",
	}
	pipeline compose {
		vbuf: gfx::VertexBuffer<BlitVertex> = (),
		src1: gfx::TextureSampler<[f32; 4]> = "t_Source1",
		src2: gfx::TextureSampler<[f32; 4]> = "t_Source2",
		dst: gfx::RenderTarget<LDR> = "o_Color",
	}
}

use std::marker::PhantomData;
type HDRRenderSurface<R> = (gfx::handle::Texture<R, gfx::format::R16_G16_B16_A16>,
                            gfx::handle::ShaderResourceView<R, [f32; 4]>,
                            gfx::handle::RenderTargetView<R, (gfx::format::R16_G16_B16_A16, gfx::format::Float)>);
pub struct PostLighting<R: gfx::Resources, C: gfx::CommandBuffer<R>> {
	vertex_buffer: gfx::handle::Buffer<R, BlitVertex>,
	index_buffer_slice: gfx::Slice<R>,
	nearest_sampler: gfx::handle::Sampler<R>,
	linear_sampler: gfx::handle::Sampler<R>,

	ping_pong_small: [HDRRenderSurface<R>; 2],
	ping_pong_large: [HDRRenderSurface<R>; 2],

	mips: Vec<HDRRenderSurface<R>>,
	luminance_smooth: HDRRenderSurface<R>,
	luminance_acc: HDRRenderSurface<R>,

	blit_pso: gfx::pso::PipelineState<R, postprocess::Meta>,
	average_pso: gfx::pso::PipelineState<R, postprocess::Meta>,

	smooth_fragment_args: gfx::handle::Buffer<R, SmoothFragmentArgs>,
	smooth_pso: gfx::pso::PipelineState<R, smooth::Meta>,

	highlight_pso: gfx::pso::PipelineState<R, postprocess::Meta>,
	blur_h_pso: gfx::pso::PipelineState<R, postprocess::Meta>,
	blur_v_pso: gfx::pso::PipelineState<R, postprocess::Meta>,

	tone_map_vertex_args: gfx::handle::Buffer<R, ToneMapVertexArgs>,
	tone_map_pso: gfx::pso::PipelineState<R, tone_map::Meta>,

	compose_pso: gfx::pso::PipelineState<R, compose::Meta>,

	_buffer: PhantomData<C>,
}

impl<R: gfx::Resources, C: gfx::CommandBuffer<R>> PostLighting<R, C> {
	pub fn new<F>(factory: &mut F, res: &resource::ResourceLoader<u8>, w: u16, h: u16) -> PostLighting<R, C>
		where F: gfx::Factory<R> {

		let full_screen_triangle = vec![BlitVertex {
			                                pos: [-1., -1.],
			                                tex_coord: [0., 0.],
		                                },
		                                BlitVertex {
			                                pos: [-1., 3.],
			                                tex_coord: [0., 2.],
		                                },
		                                BlitVertex {
			                                pos: [3., -1.],
			                                tex_coord: [2., 0.],
		                                }];

		let (vertex_buffer, slice) = factory.create_vertex_buffer_with_slice(&full_screen_triangle, ());

		let nearest_sampler = factory.create_sampler(gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Scale,
		                                                                        gfx::tex::WrapMode::Clamp));

		let linear_sampler = factory.create_sampler(gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Bilinear,
		                                                                       gfx::tex::WrapMode::Clamp));

		let tone_map_vertex_args = factory.create_constant_buffer(1);
		let smooth_fragment_args = factory.create_constant_buffer(1);

		macro_rules! load_pipeline_simple {
			($v:expr, $f:expr, $s:ident) => { factory.create_pipeline_simple(
					&res.load(concat!("shaders/effects/", $v, ".vert")).unwrap(),
					&res.load(concat!("shaders/effects/", $f, ".frag")).unwrap(),
					$s::new()
				)
				.unwrap() }
		};

		let tone_map_pso = load_pipeline_simple!("luminance", "exposure_tone_map", tone_map);
		let highlight_pso = load_pipeline_simple!("identity", "clip_luminance", postprocess);
		let blur_h_pso = load_pipeline_simple!("identity", "gaussian_blur_horizontal", postprocess);
		let blur_v_pso = load_pipeline_simple!("identity", "gaussian_blur_vertical", postprocess);
		let blit_pso = load_pipeline_simple!("identity", "simple_blit", postprocess);
		let smooth_pso = load_pipeline_simple!("identity", "exponential_smooth", smooth);
		let average_pso = load_pipeline_simple!("identity", "quad_smooth", postprocess);
		let compose_pso = load_pipeline_simple!("identity", "compose_2", compose);

		let ping_pong_small = [factory.create_render_target::<HDR>(w / 2, h / 2).unwrap(),
		                       factory.create_render_target::<HDR>(w / 2, h / 2).unwrap()];

		let ping_pong_large = [factory.create_render_target::<HDR>(w, h).unwrap(),
		                       factory.create_render_target::<HDR>(w, h).unwrap()];

		let mut mips: Vec<HDRRenderSurface<R>> = Vec::new();
		let mut w2 = w;
		let mut h2 = h;

		while w2 > 1 || h2 > 1 {
			w2 = (w2 + 1) / 2;
			h2 = (h2 + 1) / 2;
			// println!("{}x{}", w2, h2);
			mips.push(factory.create_render_target::<HDR>(w2, h2).unwrap());
		}

		let luminance_smooth = factory.create_render_target::<HDR>(1, 1).unwrap();
		let luminance_acc = factory.create_render_target::<HDR>(1, 1).unwrap();

		PostLighting {
			vertex_buffer: vertex_buffer,
			index_buffer_slice: slice,
			nearest_sampler: nearest_sampler,
			linear_sampler: linear_sampler,

			blit_pso: blit_pso,
			average_pso: average_pso,

			smooth_fragment_args: smooth_fragment_args,
			smooth_pso: smooth_pso,

			tone_map_vertex_args: tone_map_vertex_args,
			tone_map_pso: tone_map_pso,

			highlight_pso: highlight_pso,
			blur_h_pso: blur_h_pso,
			blur_v_pso: blur_v_pso,

			compose_pso: compose_pso,

			mips: mips,
			luminance_smooth: luminance_smooth,
			luminance_acc: luminance_acc,

			ping_pong_small: ping_pong_small,
			ping_pong_large: ping_pong_large,

			_buffer: PhantomData,
		}
	}

	fn full_screen_pass(&self,
	                    encoder: &mut gfx::Encoder<R, C>,
	                    pso: &gfx::pso::PipelineState<R, postprocess::Meta>,
	                    src: &gfx::handle::ShaderResourceView<R, [f32; 4]>,
	                    dst: &gfx::handle::RenderTargetView<R, HDR>) {
		encoder.draw(&self.index_buffer_slice,
		             pso,
		             &postprocess::Data {
			             vbuf: self.vertex_buffer.clone(),
			             src: (src.clone(), self.nearest_sampler.clone()),
			             dst: (dst.clone()),
		             });
	}

	pub fn apply_all(&mut self,
	                 encoder: &mut gfx::Encoder<R, C>,
	                 raw_hdr_src: gfx::handle::ShaderResourceView<R, [f32; 4]>,
	                 color_target: gfx::handle::RenderTargetView<R, LDR>) {

		let ping_pong_large = &self.ping_pong_large[..];
		let ping_pong_small = &self.ping_pong_small[..];

		// get average lum
		let mut exposure_src = raw_hdr_src.clone();
		let mut exposure_dst;
		// this is a fold, can we do it functionally?
		for mip in &self.mips {
			exposure_dst = mip.2.clone();
			self.full_screen_pass(encoder, &self.average_pso, &exposure_src, &exposure_dst);
			exposure_src = mip.1.clone();
			// println!("{:?}", mip.0.clone());
		}

		// Exponential smoothing
		encoder.update_constant_buffer(&self.smooth_fragment_args,
		                               &SmoothFragmentArgs { exp_alpha: 0.05 });
		encoder.draw(&self.index_buffer_slice,
		             &self.smooth_pso,
		             &smooth::Data {
			             vbuf: self.vertex_buffer.clone(),
			             fragment_args: self.smooth_fragment_args.clone(),
			             value: (exposure_src, self.nearest_sampler.clone()),
			             acc: (self.luminance_acc.1.clone(), self.nearest_sampler.clone()),
			             dst: self.luminance_smooth.2.clone(),
		             });

		// Tone mapping
		encoder.update_constant_buffer(&self.tone_map_vertex_args,
		                               &ToneMapVertexArgs {
			                               white: 4.0,
			                               black: 0.5,
		                               });
		encoder.draw(&self.index_buffer_slice,
		             &self.tone_map_pso,
		             &tone_map::Data {
			             vbuf: self.vertex_buffer.clone(),
			             vertex_luminance: (self.luminance_smooth.1.clone(),
			                                self.nearest_sampler.clone()),
			             vertex_args: self.tone_map_vertex_args.clone(),
			             src: (raw_hdr_src, self.nearest_sampler.clone()),
			             dst: ping_pong_large[0].2.clone(),
		             });

		// blits smoothed luminance to "acc" buffer. TODO: pingpong
		self.full_screen_pass(encoder,
		                      &self.blit_pso,
		                      &self.luminance_smooth.1,
		                      &self.luminance_acc.2);
		// Bloom
		// 1. extract high luminance
		self.full_screen_pass(encoder,
		                      &self.highlight_pso,
		                      &ping_pong_large[0].1,
		                      &ping_pong_small[0].2);
		// 2. horizontal 4x, 9x9 gaussian blur
		self.full_screen_pass(encoder,
		                      &self.blur_h_pso,
		                      &ping_pong_small[0].1,
		                      &ping_pong_small[1].2);
		// 2. vertical 4x, 9x9 gaussian blur
		self.full_screen_pass(encoder,
		                      &self.blur_v_pso,
		                      &ping_pong_small[1].1,
		                      &ping_pong_small[0].2);

		// compose tone mapped + bloom and resolve
		encoder.draw(&self.index_buffer_slice,
		             &self.compose_pso,
		             &compose::Data {
			             vbuf: self.vertex_buffer.clone(),
			             src1: (ping_pong_large[0].1.clone(), self.nearest_sampler.clone()),
			             // src1: (self.mips[10].1.clone(), self.nearest_sampler.clone()),
			             src2: (ping_pong_small[0].1.clone(), self.linear_sampler.clone()),
			             dst: color_target.clone(),
		             });
	}
}
<|endoftext|>


<commit_before>/* diosix abstracted hardware manager
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use alloc::vec::Vec;
use super::lock::Mutex;
use platform::devices::Devices;
use platform::physmem::{PhysMemBase, PhysMemSize};
use platform::timer;
use super::error::Cause;

lazy_static!
{
    /* acquire HARDWARE before accessing any system hardware */
    static ref HARDWARE: Mutex<Option<Devices>> = Mutex::new(None);
}

/* parse_and_init
   Parse a device tree structure to create a base set of hardware devices.
   also initialize the devices so they can be used.
   call before using acquire_hardware_lock() to access HARDWARE.
   => device_tree = byte slice containing the device tree in physical memory
   <= return Ok for success, or error code on failure
*/
pub fn parse_and_init(dtb: &[u8]) -> Result<(), Cause>
{
    if let Ok(dt) = Devices::new(dtb)
    {
        *(HARDWARE.lock()) = Some(dt);
        return Ok(())
    }
    else
    {
        return Err(Cause::DeviceTreeBad);
    }
}

/* routines to interact with the system's base devices */

/* write the string msg out to the debug logging console.
   if the system is busy, return
   => msg = string to write out
   <= true if able to write, false if not */
pub fn write_debug_string(msg: &str) -> bool
{
    /* avoid blocking if we can */
    if HARDWARE.is_locked() == true
    {
        return false;
    }

    match &*(HARDWARE.lock())
    {
        Some(d) =>
        {
            d.write_debug_string(msg);
            true
        },
        None => false
    }
}

/* return number of discovered logical CPU cores, or None if value unavailable */
pub fn get_nr_cpu_cores() -> Option<usize>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => Some(d.get_nr_cpu_cores()),
        None => None
    }
}

/* return a list of the physical RAM chunks present in the system,
or None if we can't read the available memory */
pub fn get_phys_ram_chunks() -> Option<Vec<platform::physmem::RAMArea>>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => Some(d.get_phys_ram_areas()),
        None => None
    }
}

/* return total amount of physical RAM present in the system */
pub fn get_phys_ram_total() -> Option<usize>
{
    if let Some(areas) = get_phys_ram_chunks()
    {
        let mut total = 0;
        for area in areas
        {
            total = total + area.size;
        }

        return Some(total);
    }

    None
}

/* for this CPU core, enable scheduler timer interrupt */
pub fn scheduler_timer_start()
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_timer_start(),
        None => ()
    };
}

/* tell the scheduler to interrupt this core
when duration number of timer ticks or sub-seconds passes */
pub fn scheduler_timer_next_in(duration: timer::TimerValue)
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_timer_next_in(duration),
        None => ()
    };
}

/* tell the scheduler to interrupt this core when the system clock equals
target value in ticks or sub-seconds as its current value */
pub fn scheduler_timer_at(target: timer::TimerValue)
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_timer_at(target),
        None => ()
    };
}

/* get when the scheduler timer IRQ is next set to fire on this core.
this is a clock-on-the-wall value: it's a number of ticks or
sub-seconds since the timer started, not the duration to the next IRQ */
pub fn scheduler_get_timer_next_at() -> Option<timer::TimerValue>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_get_timer_next_at(),
        None => None
    }
}

/* get the CPU's timer frequency in Hz */
pub fn scheduler_get_timer_frequency() -> Option<u64>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_get_timer_frequency(),
        None => None
    }
}

/* return the timer's current value in microseconds, or None for no timer
this is a clock-on-the-wall value in that it always incremements and does
not reset. the underlying platform code can do what it needs to implement this */
pub fn scheduler_get_timer_now() -> Option<timer::TimerValue>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_get_timer_now(),
        None => None
    }
}

/* clone the system's base device tree blob structure so it can be passed
to guest capsules. the platform code should customize the tree to ensure
peripherals are virtualized. the platform code therefore controls what
hardware is provided. the hypervisor sets how many CPUs and RAM are available.
the rest is decided by the platform code.
   => cpus = number of virtual CPU cores in this capsule
      boot_cpu_id = ID of system's boot CPU (typically 0)
      mem_base = base physical address of the contiguous system RAM
      mem_size = number of bytes available in the system RAM
   <= returns dtb as a byte array, or an error code
*/
pub fn clone_dtb_for_capsule(cpus: usize, boot_cpu_id: u32, mem_base: PhysMemBase, mem_size: PhysMemSize) -> Result<Vec<u8>, Cause>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => match d.spawn_virtual_environment(cpus, boot_cpu_id, mem_base, mem_size)
        {
            Some(v) => return Ok(v),
            None => return Err(Cause::DeviceTreeBad)
        },
        None => Err(Cause::CantCloneDevices)
    }
}<commit_msg>Use named mutex<commit_after>/* diosix abstracted hardware manager
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use alloc::vec::Vec;
use super::lock::Mutex;
use platform::devices::Devices;
use platform::physmem::{PhysMemBase, PhysMemSize};
use platform::timer;
use super::error::Cause;

lazy_static!
{
    /* acquire HARDWARE before accessing any system hardware */
    static ref HARDWARE: Mutex<Option<Devices>> = Mutex::new("hardware management", None);
}

/* parse_and_init
   Parse a device tree structure to create a base set of hardware devices.
   also initialize the devices so they can be used.
   call before using acquire_hardware_lock() to access HARDWARE.
   => device_tree = byte slice containing the device tree in physical memory
   <= return Ok for success, or error code on failure
*/
pub fn parse_and_init(dtb: &[u8]) -> Result<(), Cause>
{
    if let Ok(dt) = Devices::new(dtb)
    {
        *(HARDWARE.lock()) = Some(dt);
        return Ok(())
    }
    else
    {
        return Err(Cause::DeviceTreeBad);
    }
}

/* routines to interact with the system's base devices */

/* write the string msg out to the debug logging console.
   if the system is busy, return
   => msg = string to write out
   <= true if able to write, false if not */
pub fn write_debug_string(msg: &str) -> bool
{
    /* avoid blocking if we can */
    if HARDWARE.is_locked() == true
    {
        return false;
    }

    match &*(HARDWARE.lock())
    {
        Some(d) =>
        {
            d.write_debug_string(msg);
            true
        },
        None => false
    }
}

/* return number of discovered logical CPU cores, or None if value unavailable */
pub fn get_nr_cpu_cores() -> Option<usize>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => Some(d.get_nr_cpu_cores()),
        None => None
    }
}

/* return a list of the physical RAM chunks present in the system,
or None if we can't read the available memory */
pub fn get_phys_ram_chunks() -> Option<Vec<platform::physmem::RAMArea>>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => Some(d.get_phys_ram_areas()),
        None => None
    }
}

/* return total amount of physical RAM present in the system */
pub fn get_phys_ram_total() -> Option<usize>
{
    if let Some(areas) = get_phys_ram_chunks()
    {
        let mut total = 0;
        for area in areas
        {
            total = total + area.size;
        }

        return Some(total);
    }

    None
}

/* for this CPU core, enable scheduler timer interrupt */
pub fn scheduler_timer_start()
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_timer_start(),
        None => ()
    };
}

/* tell the scheduler to interrupt this core
when duration number of timer ticks or sub-seconds passes */
pub fn scheduler_timer_next_in(duration: timer::TimerValue)
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_timer_next_in(duration),
        None => ()
    };
}

/* tell the scheduler to interrupt this core when the system clock equals
target value in ticks or sub-seconds as its current value */
pub fn scheduler_timer_at(target: timer::TimerValue)
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_timer_at(target),
        None => ()
    };
}

/* get when the scheduler timer IRQ is next set to fire on this core.
this is a clock-on-the-wall value: it's a number of ticks or
sub-seconds since the timer started, not the duration to the next IRQ */
pub fn scheduler_get_timer_next_at() -> Option<timer::TimerValue>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_get_timer_next_at(),
        None => None
    }
}

/* get the CPU's timer frequency in Hz */
pub fn scheduler_get_timer_frequency() -> Option<u64>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_get_timer_frequency(),
        None => None
    }
}

/* return the timer's current value in microseconds, or None for no timer
this is a clock-on-the-wall value in that it always incremements and does
not reset. the underlying platform code can do what it needs to implement this */
pub fn scheduler_get_timer_now() -> Option<timer::TimerValue>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => d.scheduler_get_timer_now(),
        None => None
    }
}

/* clone the system's base device tree blob structure so it can be passed
to guest capsules. the platform code should customize the tree to ensure
peripherals are virtualized. the platform code therefore controls what
hardware is provided. the hypervisor sets how many CPUs and RAM are available.
the rest is decided by the platform code.
   => cpus = number of virtual CPU cores in this capsule
      boot_cpu_id = ID of system's boot CPU (typically 0)
      mem_base = base physical address of the contiguous system RAM
      mem_size = number of bytes available in the system RAM
   <= returns dtb as a byte array, or an error code
*/
pub fn clone_dtb_for_capsule(cpus: usize, boot_cpu_id: u32, mem_base: PhysMemBase, mem_size: PhysMemSize) -> Result<Vec<u8>, Cause>
{
    match &*(HARDWARE.lock())
    {
        Some(d) => match d.spawn_virtual_environment(cpus, boot_cpu_id, mem_base, mem_size)
        {
            Some(v) => return Ok(v),
            None => return Err(Cause::DeviceTreeBad)
        },
        None => Err(Cause::CantCloneDevices)
    }
}<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::io::BufReader;
use std::io::SeekFrom;
use std::fs::File;
use std::cmp::Ordering;
use std::mem;
use utils;
use errors::Word2VecError;

/// Representation of a word vector space
///
/// Each word of a vocabulary is represented by a vector. All words span a vector space. This data
/// structure manages this vector space of words.
pub struct WordVector {
	vocabulary: Vec<(String, Vec<f32>)>,
	vector_size: usize
}

impl WordVector{
	/// Load a word vector space from file
	///
	/// Word2vec is able to store the word vectors in a binary file. This function parses the file
	/// and loads the vectors into RAM.
	pub fn load_from_binary(file_name: &str) -> Result<WordVector, Word2VecError>
	{
		let file = try!(File::open(file_name));
		let mut reader = BufReader::new(file);
		let mut header = String::new();
		try!(reader.read_line(&mut header));
		let header_info = header.split_whitespace().filter_map(|x| x.parse::<usize>().ok()).take(2).collect::<Vec<usize>>();
		if header_info.len() != 2{
			return Err(Word2VecError::WrongHeader)
		}
		let vocabulary_size = header_info[0];
		let vector_size = header_info[1];
		let mut vocabulary: Vec<(String, Vec<f32>)> = Vec::with_capacity(vocabulary_size);
		for _ in 0..vocabulary_size{
			let mut word_bytes: Vec<u8> = Vec::new();
			try!(reader.read_until(b' ', &mut word_bytes));
			word_bytes.pop();
			let word = try!(String::from_utf8(word_bytes));
			let mut current_vector: Vec<f32> = Vec::with_capacity(vector_size);
			for _ in 0..vector_size{
				let mut buf: [u8; 4] = [0; 4];
				try!(reader.read(&mut buf));
				let vec = unsafe{ mem::transmute::<[u8; 4], f32>(buf) };
				current_vector.push(vec);
			}
			current_vector = utils::vector_norm(current_vector);
			vocabulary.push((word, current_vector));
			try!(reader.seek(SeekFrom::Current(1)));
		}
		Ok(WordVector{
			vocabulary: vocabulary,
			vector_size: vector_size,
		})
	}

	fn get_index(&self, word: &str) -> Option<usize>
	{
		self.vocabulary.iter().position(|x| x.0 == word)
	}

	/// Get word vector for the given word.
	pub fn get_vector(&self, word: &str) -> Option<&Vec<f32>>
	{
		let index = self.get_index(word);
		match index {
			Some(val) => {
				Some(&self.vocabulary[val].1)
			},
			None => None,
		}
	}

	/// Compute consine distance to similar words.
	///
	/// The words in the vector space are characterized through the position and angle to each
	/// other. This method calculates the `n` closest words via the cosine of the requested word to
	/// all other words.
	pub fn cosine(&self, word: &str, n: usize) -> Option<Vec<(String, f32)>>
	{
		let word_vector = self.get_vector(word);
		match word_vector {
		    Some(val) => {
		        let mut metrics: Vec<(String, f32)> = Vec::with_capacity(self.vocabulary.len());
		        for word in self.vocabulary.iter(){
		    	    metrics.push((word.0.clone(), utils::dot_product(&word.1, val)));
		        }
		        metrics.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
		        metrics.remove(0);
		        metrics.truncate(n);
		        return Some(metrics)
	        },
		    None => None,
		}
	}

	pub fn analogy(&self, pos: Vec<&str>, neg: Vec<&str>, n: usize) -> Option<Vec<(String, f32)>>
	{
		let mut vectors: Vec<Vec<f32>> = Vec::new();
		let mut exclude: Vec<String> = Vec::new();
		for word in pos {
			exclude.push(word.to_string());
			match self.get_vector(word){
				Some(val) => vectors.push(val.clone()),
				None => {}
			}
		}
		for word in neg.iter(){
			exclude.push(word.to_string());
			match self.get_vector(word) {
			    Some(val) => vectors.push(val.iter().map(|x| -x).collect::<Vec<f32>>()),
			    None => {},
			}
		}
		if exclude.is_empty(){
			return None
		}
		let mut mean: Vec<f32> = Vec::with_capacity(self.vector_size);
		for i in 0..self.vector_size{
			mean.push(utils::mean(vectors.iter().map(|v| v[i])));
		}
		let mut metrics: Vec<(String, f32)> = Vec::new();
		for word in self.vocabulary.iter(){
		    metrics.push((word.0.clone(), utils::dot_product(&word.1, &mean)));
		}
		metrics.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
		metrics.retain(|x| exclude.contains(&x.0) == false);
		metrics.truncate(n);
		Some(metrics)
	}

	/// Get the count of all known words from the vocabulary.
	pub fn word_count(&self) -> usize
	{
	    self.vocabulary.len()
    }

    pub fn get_words<'a>(&'a self) -> Words<'a> {
    	Words::new(&self.vocabulary)
	}
}

pub struct Words<'parent> {
	words: &'parent Vec<(String, Vec<f32>)>,
	index: usize,
}

impl<'a> Words<'a> {
	fn new(x: &'a Vec<(String, Vec<f32>)>) -> Words<'a> {
		Words { words: x, index: 0 }
	}
}

impl<'a> Iterator for Words<'a> {
	type Item = String;

	fn next(&mut self) -> Option<Self::Item>{
		if self.index >= self.words.len() {
			return None
		}
		self.index += 1;
		Some(self.words[self.index - 1].0.clone())
	}
}

<commit_msg>WordVecor: add method to retrieve number of columns of the vector.<commit_after>use std::io::prelude::*;
use std::io::BufReader;
use std::io::SeekFrom;
use std::fs::File;
use std::cmp::Ordering;
use std::mem;
use utils;
use errors::Word2VecError;

/// Representation of a word vector space
///
/// Each word of a vocabulary is represented by a vector. All words span a vector space. This data
/// structure manages this vector space of words.
pub struct WordVector {
	vocabulary: Vec<(String, Vec<f32>)>,
	vector_size: usize
}

impl WordVector{
	/// Load a word vector space from file
	///
	/// Word2vec is able to store the word vectors in a binary file. This function parses the file
	/// and loads the vectors into RAM.
	pub fn load_from_binary(file_name: &str) -> Result<WordVector, Word2VecError>
	{
		let file = try!(File::open(file_name));
		let mut reader = BufReader::new(file);
		let mut header = String::new();
		try!(reader.read_line(&mut header));
		let header_info = header.split_whitespace().filter_map(|x| x.parse::<usize>().ok()).take(2).collect::<Vec<usize>>();
		if header_info.len() != 2{
			return Err(Word2VecError::WrongHeader)
		}
		let vocabulary_size = header_info[0];
		let vector_size = header_info[1];
		let mut vocabulary: Vec<(String, Vec<f32>)> = Vec::with_capacity(vocabulary_size);
		for _ in 0..vocabulary_size{
			let mut word_bytes: Vec<u8> = Vec::new();
			try!(reader.read_until(b' ', &mut word_bytes));
			word_bytes.pop();
			let word = try!(String::from_utf8(word_bytes));
			let mut current_vector: Vec<f32> = Vec::with_capacity(vector_size);
			for _ in 0..vector_size{
				let mut buf: [u8; 4] = [0; 4];
				try!(reader.read(&mut buf));
				let vec = unsafe{ mem::transmute::<[u8; 4], f32>(buf) };
				current_vector.push(vec);
			}
			current_vector = utils::vector_norm(current_vector);
			vocabulary.push((word, current_vector));
			try!(reader.seek(SeekFrom::Current(1)));
		}
		Ok(WordVector{
			vocabulary: vocabulary,
			vector_size: vector_size,
		})
	}

	fn get_index(&self, word: &str) -> Option<usize>
	{
		self.vocabulary.iter().position(|x| x.0 == word)
	}

	/// Get word vector for the given word.
	pub fn get_vector(&self, word: &str) -> Option<&Vec<f32>>
	{
		let index = self.get_index(word);
		match index {
			Some(val) => {
				Some(&self.vocabulary[val].1)
			},
			None => None,
		}
	}

	/// Compute consine distance to similar words.
	///
	/// The words in the vector space are characterized through the position and angle to each
	/// other. This method calculates the `n` closest words via the cosine of the requested word to
	/// all other words.
	pub fn cosine(&self, word: &str, n: usize) -> Option<Vec<(String, f32)>>
	{
		let word_vector = self.get_vector(word);
		match word_vector {
			Some(val) => {
				let mut metrics: Vec<(String, f32)> = Vec::with_capacity(self.vocabulary.len());
				for word in self.vocabulary.iter(){
					metrics.push((word.0.clone(), utils::dot_product(&word.1, val)));
				}
				metrics.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
				metrics.remove(0);
				metrics.truncate(n);
				return Some(metrics)
			},
			None => None,
		}
	}

	pub fn analogy(&self, pos: Vec<&str>, neg: Vec<&str>, n: usize) -> Option<Vec<(String, f32)>>
	{
		let mut vectors: Vec<Vec<f32>> = Vec::new();
		let mut exclude: Vec<String> = Vec::new();
		for word in pos {
			exclude.push(word.to_string());
			match self.get_vector(word){
				Some(val) => vectors.push(val.clone()),
				None => {}
			}
		}
		for word in neg.iter(){
			exclude.push(word.to_string());
			match self.get_vector(word) {
				Some(val) => vectors.push(val.iter().map(|x| -x).collect::<Vec<f32>>()),
				None => {},
			}
		}
		if exclude.is_empty(){
			return None
		}
		let mut mean: Vec<f32> = Vec::with_capacity(self.vector_size);
		for i in 0..self.vector_size{
			mean.push(utils::mean(vectors.iter().map(|v| v[i])));
		}
		let mut metrics: Vec<(String, f32)> = Vec::new();
		for word in self.vocabulary.iter(){
			metrics.push((word.0.clone(), utils::dot_product(&word.1, &mean)));
		}
		metrics.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
		metrics.retain(|x| exclude.contains(&x.0) == false);
		metrics.truncate(n);
		Some(metrics)
	}

	/// Get the number of all known words from the vocabulary.
	pub fn word_count(&self) -> usize
	{
		self.vocabulary.len()
	}

	/// Return the number of columns of the word vector.
	pub fn get_col_count(&self) -> usize
	{
		self.vector_size // size == column count
	}

	/// Get all known words from the vocabulary.
	pub fn get_words<'a>(&'a self) -> Words<'a>
	{
		Words::new(&self.vocabulary)
	}
}

pub struct Words<'parent> {
	words: &'parent Vec<(String, Vec<f32>)>,
	index: usize,
}

impl<'a> Words<'a> {
	fn new(x: &'a Vec<(String, Vec<f32>)>) -> Words<'a> {
		Words { words: x, index: 0 }
	}
}

impl<'a> Iterator for Words<'a> {
	type Item = String;

	fn next(&mut self) -> Option<Self::Item>{
		if self.index >= self.words.len() {
			return None
		}
		self.index += 1;
		Some(self.words[self.index - 1].0.clone())
	}
}

<|endoftext|>


<commit_before>//! Helper functions and traits for all configuration sections.

use toml;
use std::result;
use std::fmt;

/// `Result` type used throughout the configuration parser.
pub type Result<T> = result::Result<T, String>;

/// Trait that provides a interface for parsing a (sub-) tree of the configuration.
pub trait ConfTreeParser<C> {
    /// Try to parse the given `tree` into the type this trait is implemented for.
    /// Returns the parsed object or a error message, with the first error encountered while
    /// parsing the `tree`.
    ///
    /// # Arguments
    /// * `tree`: The `toml` tree to parse
    fn parse(tree: &toml::Table) -> Result<C>;
}

/// Parse a required key, returning a appropriate error message, if the key can't be found in the
/// configuration.
///
/// # Arguments
/// * `subtree`: The `toml` tree to parse.
/// * `key`: The key to look for.
/// * `group`: The group, this (sub-) tree is associated with.
pub fn get_required_key<D>(subtree: &toml::Value, key: &str, group: &str)
    -> Result<D> where D: ParseTomlValue {
    let value = try!(subtree.lookup(key)
                     .ok_or(format!("Missing required key \"{}\" in \"{}\"",
                                    key, group)));
    ParseTomlValue::parse(value, key, group)
}

/// Parse a optional key, returning None if it can't be found.
///
/// This basically does a `Option<Result>` to `Result<Option>` translation
///
/// # Arguments
/// * `subtree`: The `toml` tree to parse.
/// * `key`: The key to look for.
/// * `group`: The group, this (sub-) tree is associated with.
pub fn get_optional_key<D>(subtree: &toml::Value, key: &str, group: &str)
    -> Result<Option<D>> where D: ParseTomlValue {
    match subtree.lookup(key) {
        Some(val) => {
            Ok(Some(try!(ParseTomlValue::parse(val, key, group))))
        },
        None => Ok(None)
    }
}

/// Trait that provides a interface for parsing a single `toml` value.
pub trait ParseTomlValue {
    /// Parse a `String` from a `toml` value. Returns the parsed value on success or a error
    /// message on failure.
    ///
    /// # Arguments
    /// * `val`: The `toml` value to parse.
    /// * `key`: The key this value is associated with.
    /// * `group`: The group, this (sub-) tree is associated with.
    fn parse(val: &toml::Value, key: &str, group: &str) -> Result<Self>;
}

impl ParseTomlValue for String {
    fn parse(val: &toml::Value, key: &str, group: &str)
        -> Result<String> {
        val.as_str().map(|s| s.to_string())
           .ok_or(format!("Key \"{}\" in \"{}\" is not a string", key, group))
    }
}

impl ParseTomlValue for i32 {
    fn parse(val: &toml::Value, key: &str, group: &str)
        -> Result<i32> {
        val.as_integer().map(|i| i as i32)
           .ok_or(format!("Key \"{}\" in \"{}\" is not a integer", key, group))
    }
}

impl ParseTomlValue for i64 {
    fn parse(val: &toml::Value, key: &str, group: &str)
        -> Result<i64> {
        val.as_integer()
           .ok_or(format!("Key \"{}\" in \"{}\" is not a integer", key, group))
    }
}

/// Helper function to format a `toml::Parser` error message to the format used in this
/// implementation. This is only safe to call if the `parser` is associated with a *real* file on
/// disk.
///
/// # Arguments
/// * `parser`: Pointer to the `toml::Parser`, that produced a error.
#[cfg(not(test))]
pub fn format_parser_error(parser: &toml::Parser) -> String {
    let linecol = parser.to_linecol(0);
    format!("parse error: {}:{}: {:?}", linecol.0, linecol.1, parser.errors)
}

/// Helper function to format a `toml::Parser` error message to the format used in this
/// implementation. This version is always safe to call, but doesn't print the line and column
/// where the error was encountered.
///
/// # Arguments
/// * `parser`: Pointer to the `toml::Parser`, that produced a error.
#[cfg(test)]
pub fn format_parser_error(parser: &toml::Parser) -> String {
    format!("parse error: {:?}", parser.errors)
}

/// Helper function to copy anything that implements `Display` to a `String`.
pub fn stringify<T>(e: T) -> String
    where T: fmt::Display {
    format!("{}", e)
}

/// Reads the provided `tree` as a `toml::Table`. Returns the `toml::Table` on success or a error
/// message on failure.
///
/// # Arguments
/// * `tree`: Pointer to a `str`, that holds a toml configuration.
#[cfg(test)]
pub fn read_tree(tree: &str) -> Result<toml::Table> {
    let mut parser = toml::Parser::new(tree);
    parser.parse().ok_or(format_parser_error(&parser))
}
<commit_msg>src/configuration/common.rs: Fix Sized for Self<commit_after>//! Helper functions and traits for all configuration sections.

use toml;
use std::result;
use std::fmt;

/// `Result` type used throughout the configuration parser.
pub type Result<T> = result::Result<T, String>;

/// Trait that provides a interface for parsing a (sub-) tree of the configuration.
pub trait ConfTreeParser<C> {
    /// Try to parse the given `tree` into the type this trait is implemented for.
    /// Returns the parsed object or a error message, with the first error encountered while
    /// parsing the `tree`.
    ///
    /// # Arguments
    /// * `tree`: The `toml` tree to parse
    fn parse(tree: &toml::Table) -> Result<C>;
}

/// Parse a required key, returning a appropriate error message, if the key can't be found in the
/// configuration.
///
/// # Arguments
/// * `subtree`: The `toml` tree to parse.
/// * `key`: The key to look for.
/// * `group`: The group, this (sub-) tree is associated with.
pub fn get_required_key<D>(subtree: &toml::Value, key: &str, group: &str)
    -> Result<D> where D: ParseTomlValue {
    let value = try!(subtree.lookup(key)
                     .ok_or(format!("Missing required key \"{}\" in \"{}\"",
                                    key, group)));
    ParseTomlValue::parse(value, key, group)
}

/// Parse a optional key, returning None if it can't be found.
///
/// This basically does a `Option<Result>` to `Result<Option>` translation
///
/// # Arguments
/// * `subtree`: The `toml` tree to parse.
/// * `key`: The key to look for.
/// * `group`: The group, this (sub-) tree is associated with.
pub fn get_optional_key<D>(subtree: &toml::Value, key: &str, group: &str)
    -> Result<Option<D>> where D: ParseTomlValue {
    match subtree.lookup(key) {
        Some(val) => {
            Ok(Some(try!(ParseTomlValue::parse(val, key, group))))
        },
        None => Ok(None)
    }
}

/// Trait that provides a interface for parsing a single `toml` value.
pub trait ParseTomlValue {
    /// Parse a `String` from a `toml` value. Returns the parsed value on success or a error
    /// message on failure.
    ///
    /// # Arguments
    /// * `val`: The `toml` value to parse.
    /// * `key`: The key this value is associated with.
    /// * `group`: The group, this (sub-) tree is associated with.
    fn parse(val: &toml::Value, key: &str, group: &str) -> Result<Self> where Self: Sized;
}

impl ParseTomlValue for String {
    fn parse(val: &toml::Value, key: &str, group: &str)
        -> Result<String> {
        val.as_str().map(|s| s.to_string())
           .ok_or(format!("Key \"{}\" in \"{}\" is not a string", key, group))
    }
}

impl ParseTomlValue for i32 {
    fn parse(val: &toml::Value, key: &str, group: &str)
        -> Result<i32> {
        val.as_integer().map(|i| i as i32)
           .ok_or(format!("Key \"{}\" in \"{}\" is not a integer", key, group))
    }
}

impl ParseTomlValue for i64 {
    fn parse(val: &toml::Value, key: &str, group: &str)
        -> Result<i64> {
        val.as_integer()
           .ok_or(format!("Key \"{}\" in \"{}\" is not a integer", key, group))
    }
}

/// Helper function to format a `toml::Parser` error message to the format used in this
/// implementation. This is only safe to call if the `parser` is associated with a *real* file on
/// disk.
///
/// # Arguments
/// * `parser`: Pointer to the `toml::Parser`, that produced a error.
#[cfg(not(test))]
pub fn format_parser_error(parser: &toml::Parser) -> String {
    let linecol = parser.to_linecol(0);
    format!("parse error: {}:{}: {:?}", linecol.0, linecol.1, parser.errors)
}

/// Helper function to format a `toml::Parser` error message to the format used in this
/// implementation. This version is always safe to call, but doesn't print the line and column
/// where the error was encountered.
///
/// # Arguments
/// * `parser`: Pointer to the `toml::Parser`, that produced a error.
#[cfg(test)]
pub fn format_parser_error(parser: &toml::Parser) -> String {
    format!("parse error: {:?}", parser.errors)
}

/// Helper function to copy anything that implements `Display` to a `String`.
pub fn stringify<T>(e: T) -> String
    where T: fmt::Display {
    format!("{}", e)
}

/// Reads the provided `tree` as a `toml::Table`. Returns the `toml::Table` on success or a error
/// message on failure.
///
/// # Arguments
/// * `tree`: Pointer to a `str`, that holds a toml configuration.
#[cfg(test)]
pub fn read_tree(tree: &str) -> Result<toml::Table> {
    let mut parser = toml::Parser::new(tree);
    parser.parse().ok_or(format_parser_error(&parser))
}
<|endoftext|>


<commit_before>use context::ExtensionsList;
use version::Version;
use version::Api;
use std::mem;
use gl;

/// Represents the capabilities of the context.
pub struct Capabilities {
    /// List of versions of GLSL that are supported by the compiler.
    ///
    /// An empty list means that the backend doesn't have a compiler.
    pub supported_glsl_versions: Vec<Version>,

    /// Whether the context supports left and right buffers.
    pub stereo: bool,

    /// True if the default framebuffer is in sRGB.
    pub srgb: bool,

    /// Number of bits in the default framebuffer's depth buffer
    pub depth_bits: Option<u16>,

    /// Number of bits in the default framebuffer's stencil buffer
    pub stencil_bits: Option<u16>,

    /// Maximum number of textures that can be bound to a program.
    ///
    /// `glActiveTexture` must be between `GL_TEXTURE0` and `GL_TEXTURE0` + this value - 1.
    pub max_combined_texture_image_units: gl::types::GLint,

    /// Maximum value for `GL_TEXTURE_MAX_ANISOTROPY_EXT`.
    ///
    /// `None` if the extension is not supported by the hardware.
    pub max_texture_max_anisotropy: Option<gl::types::GLfloat>,

    /// Maximum width and height of `glViewport`.
    pub max_viewport_dims: (gl::types::GLint, gl::types::GLint),

    /// Maximum number of elements that can be passed with `glDrawBuffers`.
    pub max_draw_buffers: gl::types::GLint,

    /// Maximum number of vertices per patch. `None` if tessellation is not supported.
    pub max_patch_vertices: Option<gl::types::GLint>,

    /// Number of available buffer bind points for `GL_ATOMIC_COUNTER_BUFFER`.
    pub max_indexed_atomic_counter_buffer: gl::types::GLint,

    /// Number of available buffer bind points for `GL_SHADER_STORAGE_BUFFER`.
    pub max_indexed_shader_storage_buffer: gl::types::GLint,

    /// Number of available buffer bind points for `GL_TRANSFORM_FEEDBACK_BUFFER`.
    pub max_indexed_transform_feedback_buffer: gl::types::GLint,

    /// Number of available buffer bind points for `GL_UNIFORM_BUFFER`.
    pub max_indexed_uniform_buffer: gl::types::GLint,

    /// Number of work groups for compute shaders.
    pub max_compute_work_group_count: (gl::types::GLint, gl::types::GLint, gl::types::GLint),
}

/// Loads the capabilities.
///
/// *Safety*: the OpenGL context corresponding to `gl` must be current in the thread.
///
/// ## Panic
///
/// Can panic if the version number or extensions list don't match the backend, leading to
/// unloaded functions being called.
///
pub unsafe fn get_capabilities(gl: &gl::Gl, version: &Version, extensions: &ExtensionsList)
                               -> Capabilities
{
    use std::mem;

    Capabilities {
        supported_glsl_versions: {
            get_supported_glsl(gl, version, extensions)
        },

        stereo: {
            if version >= &Version(Api::Gl, 1, 0) {
                let mut val: gl::types::GLboolean = mem::uninitialized();
                gl.GetBooleanv(gl::STEREO, &mut val);
                val != 0
            } else {
                false
            }
        },

        srgb: {
            // `glGetFramebufferAttachmentParameteriv` incorrectly returns GL_INVALID_ENUM on some
            // drivers, so we prefer using `glGetIntegerv` if possible.
            if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_ext_framebuffer_srgb {
                let mut value = mem::uninitialized();
                gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, gl::BACK_LEFT,
                                                       gl::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
                                                       &mut value);
                value as gl::types::GLenum == gl::SRGB

            } else if extensions.gl_ext_framebuffer_srgb {
                let mut value = mem::uninitialized();
                gl.GetBooleanv(gl::FRAMEBUFFER_SRGB_CAPABLE_EXT, &mut value);
                value != 0

            } else {
                false
            }
        },

        depth_bits: {
            let mut value = mem::uninitialized();

            // `glGetFramebufferAttachmentParameteriv` incorrectly returns GL_INVALID_ENUM on some
            // drivers, so we prefer using `glGetIntegerv` if possible.
            if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_arb_compatibility &&
               !extensions.gl_arb_es2_compatibility
            {
                gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, gl::DEPTH,
                                                       gl::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE,
                                                       &mut value);
            } else {
                gl.GetIntegerv(gl::DEPTH_BITS, &mut value);
            };

            match value {
                0 => None,
                v => Some(v as u16),
            }
        },

        stencil_bits: {
            let mut value = mem::uninitialized();

            // `glGetFramebufferAttachmentParameteriv` incorrectly returns GL_INVALID_ENUM on some
            // drivers, so we prefer using `glGetIntegerv` if possible.
            if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_arb_compatibility &&
               !extensions.gl_arb_es2_compatibility
            {
                gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, gl::STENCIL,
                                                       gl::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
                                                       &mut value);
            } else {
                gl.GetIntegerv(gl::STENCIL_BITS, &mut value);
            };

            match value {
                0 => None,
                v => Some(v as u16),
            }
        },

        max_combined_texture_image_units: {
            let mut val = 2;
            gl.GetIntegerv(gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mut val);
            val
        },

        max_texture_max_anisotropy: if !extensions.gl_ext_texture_filter_anisotropic {
            None

        } else {
            Some({
                let mut val = mem::uninitialized();
                gl.GetFloatv(gl::MAX_TEXTURE_MAX_ANISOTROPY_EXT, &mut val);
                val
            })
        },

        max_viewport_dims: {
            let mut val: [gl::types::GLint; 2] = [ 0, 0 ];
            gl.GetIntegerv(gl::MAX_VIEWPORT_DIMS, val.as_mut_ptr());
            (val[0], val[1])
        },

        max_draw_buffers: {
            if version >= &Version(Api::Gl, 2, 0) ||
                version >= &Version(Api::GlEs, 3, 0)
            {
                let mut val = 1;
                gl.GetIntegerv(gl::MAX_DRAW_BUFFERS, &mut val);
                val
            } else {
                1
            }
        },

        max_patch_vertices: if version >= &Version(Api::Gl, 4, 0) ||
            extensions.gl_arb_tessellation_shader
        {
            Some({
                let mut val = mem::uninitialized();
                gl.GetIntegerv(gl::MAX_PATCH_VERTICES, &mut val);
                val
            })

        } else {
            None
        },

        max_indexed_atomic_counter_buffer: if version >= &Version(Api::Gl, 4, 2) {      // TODO: ARB_shader_atomic_counters   // TODO: GLES
            let mut val = mem::uninitialized();
            gl.GetIntegerv(gl::MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &mut val);
            val
        } else {
            0
        },

        max_indexed_shader_storage_buffer: if version >= &Version(Api::Gl, 4, 3) {      // TODO: ARB_shader_storage_buffer_object   // TODO: GLES
            let mut val = mem::uninitialized();
            gl.GetIntegerv(gl::MAX_SHADER_STORAGE_BUFFER_BINDINGS, &mut val);
            val
        } else {
            0
        },

        max_indexed_transform_feedback_buffer: {
            if version >= &Version(Api::Gl, 3, 0) || extensions.gl_arb_transform_feedback3 {      // TODO: make sure that GL 3.0 supports it   // TODO: GLES
                let mut val = mem::uninitialized();
                gl.GetIntegerv(gl::MAX_TRANSFORM_FEEDBACK_BUFFERS, &mut val);
                val
            } else if extensions.gl_ext_transform_feedback {
                let mut val = mem::uninitialized();
                gl.GetIntegerv(gl::MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT, &mut val);
                val
            } else {
                0
            }
        },

        max_indexed_uniform_buffer: if version >= &Version(Api::Gl, 3, 0) {      // TODO: ARB_shader_storage_buffer_object   // TODO: GLES
            let mut val = mem::uninitialized();
            gl.GetIntegerv(gl::MAX_UNIFORM_BUFFER_BINDINGS, &mut val);
            val
        } else {
            0
        },

        max_compute_work_group_count: if version >= &Version(Api::Gl, 4, 3) ||
                                         version >= &Version(Api::GlEs, 3, 1) ||
                                         extensions.gl_arb_compute_shader
        {
            let mut val1 = mem::uninitialized();
            let mut val2 = mem::uninitialized();
            let mut val3 = mem::uninitialized();
            gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 0, &mut val1);
            gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 1, &mut val2);
            gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 2, &mut val3);
            (val1, val2, val3)

        } else {
            (0, 0, 0)
        },
    }
}

/// Gets the list of GLSL versions supported by the backend.
///
/// *Safety*: the OpenGL context corresponding to `gl` must be current in the thread.
///
/// ## Panic
///
/// Can panic if the version number or extensions list don't match the backend, leading to
/// unloaded functions being called.
///
pub unsafe fn get_supported_glsl(gl: &gl::Gl, version: &Version, extensions: &ExtensionsList)
                                 -> Vec<Version>
{
    // checking if the implementation has a shader compiler
    // a compiler is optional in OpenGL ES
    if version.0 == Api::GlEs {
        let mut val = mem::uninitialized();
        gl.GetBooleanv(gl::SHADER_COMPILER, &mut val);
        if val == 0 {
            return vec![];
        }
    }

    // some recent versions have an API to determine the list of supported versions
    if version >= &Version(Api::Gl, 4, 3) {
        // FIXME: implement this and return the result directly
    }

    let mut result = Vec::with_capacity(8);

    if version >= &Version(Api::GlEs, 2, 0) || version >= &Version(Api::Gl, 4, 1) ||
       extensions.gl_arb_es2_compatibility
    {
        result.push(Version(Api::GlEs, 1, 0));
    }

    if version >= &Version(Api::GlEs, 3, 0) || version >= &Version(Api::Gl, 4, 3) ||
       extensions.gl_arb_es3_compatibility
    {
        result.push(Version(Api::GlEs, 3, 0));
    }

    if version >= &Version(Api::GlEs, 3, 1) || version >= &Version(Api::Gl, 4, 5) ||
       extensions.gl_arb_es3_1_compatibility
    {
        result.push(Version(Api::GlEs, 3, 1));
    }

    if version >= &Version(Api::Gl, 2, 0) && version <= &Version(Api::Gl, 3, 0) ||
       extensions.gl_arb_compatibility
    {
        result.push(Version(Api::Gl, 1, 1));
    }

    if version >= &Version(Api::Gl, 2, 1) && version <= &Version(Api::Gl, 3, 0) ||
       extensions.gl_arb_compatibility
    {
        result.push(Version(Api::Gl, 1, 2));
    }

    if version == &Version(Api::Gl, 3, 0) || extensions.gl_arb_compatibility {
        result.push(Version(Api::Gl, 1, 3));
    }

    if version >= &Version(Api::Gl, 3, 1) {
        result.push(Version(Api::Gl, 1, 4));
    }

    if version >= &Version(Api::Gl, 3, 2) {
        result.push(Version(Api::Gl, 1, 5));
    }

    for &(major, minor) in &[(3, 3), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5)] {
        if version >= &Version(Api::Gl, major, minor) {
            result.push(Version(Api::Gl, major, minor));
        }
    }

    result
}
<commit_msg>Attempt to fix framebuffer parameters again<commit_after>use context::ExtensionsList;
use version::Version;
use version::Api;
use std::mem;
use gl;

/// Represents the capabilities of the context.
pub struct Capabilities {
    /// List of versions of GLSL that are supported by the compiler.
    ///
    /// An empty list means that the backend doesn't have a compiler.
    pub supported_glsl_versions: Vec<Version>,

    /// Whether the context supports left and right buffers.
    pub stereo: bool,

    /// True if the default framebuffer is in sRGB.
    pub srgb: bool,

    /// Number of bits in the default framebuffer's depth buffer
    pub depth_bits: Option<u16>,

    /// Number of bits in the default framebuffer's stencil buffer
    pub stencil_bits: Option<u16>,

    /// Maximum number of textures that can be bound to a program.
    ///
    /// `glActiveTexture` must be between `GL_TEXTURE0` and `GL_TEXTURE0` + this value - 1.
    pub max_combined_texture_image_units: gl::types::GLint,

    /// Maximum value for `GL_TEXTURE_MAX_ANISOTROPY_EXT`.
    ///
    /// `None` if the extension is not supported by the hardware.
    pub max_texture_max_anisotropy: Option<gl::types::GLfloat>,

    /// Maximum width and height of `glViewport`.
    pub max_viewport_dims: (gl::types::GLint, gl::types::GLint),

    /// Maximum number of elements that can be passed with `glDrawBuffers`.
    pub max_draw_buffers: gl::types::GLint,

    /// Maximum number of vertices per patch. `None` if tessellation is not supported.
    pub max_patch_vertices: Option<gl::types::GLint>,

    /// Number of available buffer bind points for `GL_ATOMIC_COUNTER_BUFFER`.
    pub max_indexed_atomic_counter_buffer: gl::types::GLint,

    /// Number of available buffer bind points for `GL_SHADER_STORAGE_BUFFER`.
    pub max_indexed_shader_storage_buffer: gl::types::GLint,

    /// Number of available buffer bind points for `GL_TRANSFORM_FEEDBACK_BUFFER`.
    pub max_indexed_transform_feedback_buffer: gl::types::GLint,

    /// Number of available buffer bind points for `GL_UNIFORM_BUFFER`.
    pub max_indexed_uniform_buffer: gl::types::GLint,

    /// Number of work groups for compute shaders.
    pub max_compute_work_group_count: (gl::types::GLint, gl::types::GLint, gl::types::GLint),
}

/// Loads the capabilities.
///
/// *Safety*: the OpenGL context corresponding to `gl` must be current in the thread.
///
/// ## Panic
///
/// Can panic if the version number or extensions list don't match the backend, leading to
/// unloaded functions being called.
///
pub unsafe fn get_capabilities(gl: &gl::Gl, version: &Version, extensions: &ExtensionsList)
                               -> Capabilities
{
    use std::mem;

    Capabilities {
        supported_glsl_versions: {
            get_supported_glsl(gl, version, extensions)
        },

        stereo: {
            if version >= &Version(Api::Gl, 1, 0) {
                let mut val: gl::types::GLboolean = mem::uninitialized();
                gl.GetBooleanv(gl::STEREO, &mut val);
                val != 0
            } else {
                false
            }
        },

        srgb: {
            // `glGetFramebufferAttachmentParameteriv` incorrectly returns GL_INVALID_ENUM on some
            // drivers, so we prefer using `glGetIntegerv` if possible.
            if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_ext_framebuffer_srgb {
                let mut value = mem::uninitialized();
                gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, gl::BACK_LEFT,
                                                       gl::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
                                                       &mut value);
                value as gl::types::GLenum == gl::SRGB

            } else if extensions.gl_ext_framebuffer_srgb {
                let mut value = mem::uninitialized();
                gl.GetBooleanv(gl::FRAMEBUFFER_SRGB_CAPABLE_EXT, &mut value);
                value != 0

            } else {
                false
            }
        },

        depth_bits: {
            let mut value = mem::uninitialized();

            // `glGetFramebufferAttachmentParameteriv` incorrectly returns GL_INVALID_ENUM on some
            // drivers, so we prefer using `glGetIntegerv` if possible.
            //
            // Also note that `gl_arb_es2_compatibility` may provide `GL_DEPTH_BITS` but os/x
            // doesn't even though it provides this extension. I'm not sure whether this is a bug
            // with OS/X or just the extension actually not providing it.
            if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_arb_compatibility {
                let mut ty = mem::uninitialized();
                gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, gl::DEPTH,
                                                       gl::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
                                                       &mut ty);

                if ty as gl::types::GLenum == gl::NONE {
                    value = 0;
                } else {
                    gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, gl::DEPTH,
                                                           gl::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE,
                                                           &mut value);
                }

            } else {
                gl.GetIntegerv(gl::DEPTH_BITS, &mut value);
            };

            match value {
                0 => None,
                v => Some(v as u16),
            }
        },

        stencil_bits: {
            let mut value = mem::uninitialized();

            // `glGetFramebufferAttachmentParameteriv` incorrectly returns GL_INVALID_ENUM on some
            // drivers, so we prefer using `glGetIntegerv` if possible.
            //
            // Also note that `gl_arb_es2_compatibility` may provide `GL_STENCIL_BITS` but os/x
            // doesn't even though it provides this extension. I'm not sure whether this is a bug
            // with OS/X or just the extension actually not providing it.
            if version >= &Version(Api::Gl, 3, 0) && !extensions.gl_arb_compatibility {
                let mut ty = mem::uninitialized();
                gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, gl::STENCIL,
                                                       gl::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
                                                       &mut ty);

                if ty as gl::types::GLenum == gl::NONE {
                    value = 0;
                } else {
                    gl.GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, gl::STENCIL,
                                                           gl::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
                                                           &mut value);
                }

            } else {
                gl.GetIntegerv(gl::STENCIL_BITS, &mut value);
            };

            match value {
                0 => None,
                v => Some(v as u16),
            }
        },

        max_combined_texture_image_units: {
            let mut val = 2;
            gl.GetIntegerv(gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mut val);
            val
        },

        max_texture_max_anisotropy: if !extensions.gl_ext_texture_filter_anisotropic {
            None

        } else {
            Some({
                let mut val = mem::uninitialized();
                gl.GetFloatv(gl::MAX_TEXTURE_MAX_ANISOTROPY_EXT, &mut val);
                val
            })
        },

        max_viewport_dims: {
            let mut val: [gl::types::GLint; 2] = [ 0, 0 ];
            gl.GetIntegerv(gl::MAX_VIEWPORT_DIMS, val.as_mut_ptr());
            (val[0], val[1])
        },

        max_draw_buffers: {
            if version >= &Version(Api::Gl, 2, 0) ||
                version >= &Version(Api::GlEs, 3, 0)
            {
                let mut val = 1;
                gl.GetIntegerv(gl::MAX_DRAW_BUFFERS, &mut val);
                val
            } else {
                1
            }
        },

        max_patch_vertices: if version >= &Version(Api::Gl, 4, 0) ||
            extensions.gl_arb_tessellation_shader
        {
            Some({
                let mut val = mem::uninitialized();
                gl.GetIntegerv(gl::MAX_PATCH_VERTICES, &mut val);
                val
            })

        } else {
            None
        },

        max_indexed_atomic_counter_buffer: if version >= &Version(Api::Gl, 4, 2) {      // TODO: ARB_shader_atomic_counters   // TODO: GLES
            let mut val = mem::uninitialized();
            gl.GetIntegerv(gl::MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &mut val);
            val
        } else {
            0
        },

        max_indexed_shader_storage_buffer: if version >= &Version(Api::Gl, 4, 3) {      // TODO: ARB_shader_storage_buffer_object   // TODO: GLES
            let mut val = mem::uninitialized();
            gl.GetIntegerv(gl::MAX_SHADER_STORAGE_BUFFER_BINDINGS, &mut val);
            val
        } else {
            0
        },

        max_indexed_transform_feedback_buffer: {
            if version >= &Version(Api::Gl, 3, 0) || extensions.gl_arb_transform_feedback3 {      // TODO: make sure that GL 3.0 supports it   // TODO: GLES
                let mut val = mem::uninitialized();
                gl.GetIntegerv(gl::MAX_TRANSFORM_FEEDBACK_BUFFERS, &mut val);
                val
            } else if extensions.gl_ext_transform_feedback {
                let mut val = mem::uninitialized();
                gl.GetIntegerv(gl::MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT, &mut val);
                val
            } else {
                0
            }
        },

        max_indexed_uniform_buffer: if version >= &Version(Api::Gl, 3, 0) {      // TODO: ARB_shader_storage_buffer_object   // TODO: GLES
            let mut val = mem::uninitialized();
            gl.GetIntegerv(gl::MAX_UNIFORM_BUFFER_BINDINGS, &mut val);
            val
        } else {
            0
        },

        max_compute_work_group_count: if version >= &Version(Api::Gl, 4, 3) ||
                                         version >= &Version(Api::GlEs, 3, 1) ||
                                         extensions.gl_arb_compute_shader
        {
            let mut val1 = mem::uninitialized();
            let mut val2 = mem::uninitialized();
            let mut val3 = mem::uninitialized();
            gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 0, &mut val1);
            gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 1, &mut val2);
            gl.GetIntegeri_v(gl::MAX_COMPUTE_WORK_GROUP_COUNT, 2, &mut val3);
            (val1, val2, val3)

        } else {
            (0, 0, 0)
        },
    }
}

/// Gets the list of GLSL versions supported by the backend.
///
/// *Safety*: the OpenGL context corresponding to `gl` must be current in the thread.
///
/// ## Panic
///
/// Can panic if the version number or extensions list don't match the backend, leading to
/// unloaded functions being called.
///
pub unsafe fn get_supported_glsl(gl: &gl::Gl, version: &Version, extensions: &ExtensionsList)
                                 -> Vec<Version>
{
    // checking if the implementation has a shader compiler
    // a compiler is optional in OpenGL ES
    if version.0 == Api::GlEs {
        let mut val = mem::uninitialized();
        gl.GetBooleanv(gl::SHADER_COMPILER, &mut val);
        if val == 0 {
            return vec![];
        }
    }

    // some recent versions have an API to determine the list of supported versions
    if version >= &Version(Api::Gl, 4, 3) {
        // FIXME: implement this and return the result directly
    }

    let mut result = Vec::with_capacity(8);

    if version >= &Version(Api::GlEs, 2, 0) || version >= &Version(Api::Gl, 4, 1) ||
       extensions.gl_arb_es2_compatibility
    {
        result.push(Version(Api::GlEs, 1, 0));
    }

    if version >= &Version(Api::GlEs, 3, 0) || version >= &Version(Api::Gl, 4, 3) ||
       extensions.gl_arb_es3_compatibility
    {
        result.push(Version(Api::GlEs, 3, 0));
    }

    if version >= &Version(Api::GlEs, 3, 1) || version >= &Version(Api::Gl, 4, 5) ||
       extensions.gl_arb_es3_1_compatibility
    {
        result.push(Version(Api::GlEs, 3, 1));
    }

    if version >= &Version(Api::Gl, 2, 0) && version <= &Version(Api::Gl, 3, 0) ||
       extensions.gl_arb_compatibility
    {
        result.push(Version(Api::Gl, 1, 1));
    }

    if version >= &Version(Api::Gl, 2, 1) && version <= &Version(Api::Gl, 3, 0) ||
       extensions.gl_arb_compatibility
    {
        result.push(Version(Api::Gl, 1, 2));
    }

    if version == &Version(Api::Gl, 3, 0) || extensions.gl_arb_compatibility {
        result.push(Version(Api::Gl, 1, 3));
    }

    if version >= &Version(Api::Gl, 3, 1) {
        result.push(Version(Api::Gl, 1, 4));
    }

    if version >= &Version(Api::Gl, 3, 2) {
        result.push(Version(Api::Gl, 1, 5));
    }

    for &(major, minor) in &[(3, 3), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5)] {
        if version >= &Version(Api::Gl, major, minor) {
            result.push(Version(Api::Gl, major, minor));
        }
    }

    result
}
<|endoftext|>


<commit_before>pub mod disk;
pub mod memory;

#[cfg(test)]
mod tests {
    use db::storage::{Memory, Disk, StorageEngine};
    use tempdir::TempDir;

    #[test]
    fn test_storage_engine_creation() {
        let mem = Memory::new().expect("Memory storage");
        let storage = StorageEngine::new(mem);

        let tmp = TempDir::new("totalrecallwhatever").expect("Expected dir");
        let mut tmp2 = tmp.into_path();
        tmp2.push("blah");

        let disk = Disk::new(10, tmp2).expect("Disk storage");
        let s2 = StorageEngine::new(disk);
    }

}
<commit_msg>cool first cut at a macro for testing storage<commit_after>pub mod disk;
pub mod memory;

use db::storage::{Memory, Disk, StorageEngine};
use tempdir::TempDir;
use db::storage::Storage;

macro_rules! test_storage {
    ( $f:ident, $x:ident, $y:ident ) => {
        $f(&$x);
        $f(&$y);
    };
}

#[test]
fn test_storage_engine_creation() {
    let mem = Memory::new().expect("Memory storage");
    let storage = StorageEngine::new(mem);

    // disk storage
    let tmp = TempDir::new("totalrecallwhatever").expect("Expected dir");
    let mut tmp2 = tmp.into_path();
    tmp2.push("blah");

    let disk = Disk::new(10, tmp2).expect("Disk storage");
    let storage2 = StorageEngine::new(disk);

    test_storage!(test_page_write_and_get, storage, storage2);
}

fn test_page_write_and_get(s: &StorageEngine) {

}

<|endoftext|>


<commit_before>use rusqlite::Connection;
use std::result;

#[derive(Debug)]
pub struct JobRequireVariable {
    id: i32,
    fk_job: i32,
    fk_variable: i32,
}

pub fn table_create_job_require_variable(conn: &Connection)  {
    conn.execute("CREATE TABLE JOB_REQUIRE_VARIABLE (
                  id            INTEGER PRIMARY KEY ASC,
                  job           INTEGER NOT NULL,
                  fk_variable   INTEGER NOT NULL,
                  FOREIGN KEY(job) REFERENCES JOB(id) ON UPDATE CASCADE
                  FOREIGN KEY(fk_variable) REFERENCES JOB_REQUIRE_VARIABLE(id) ON UPDATE CASCADE
                  )", &[]).unwrap();
}

pub fn insert_job_require_variable(conn: &Connection, job: &i32, variable: &i32, name: &String) -> Result<i32, &'static str> {
    let me = JobRequireVariable {
        id: 0,
        fk_job: *job,
        fk_variable: *variable,

    };
    let job_require_variable_instance = conn.execute("INSERT INTO JOB_REQUIRE_VARIABLE (fk_job, fk_variable)
                  VALUES (?1, ?2)",
                 &[&me.fk_job, &me.fk_variable]);
    if job_require_variable_instance.is_err() {
        return Err("Insert failed");
    }
    job_require_variable_instance.unwrap();
    return Ok(0);
}

pub fn list_job_require_variable(conn: &Connection)-> Vec<JobRequireVariable> {
    let mut stmt = conn.prepare("SELECT id, fk_job, fk_variable  FROM JOB_REQUIRE_VARIABLE").unwrap();
    let wraped_fs_file_iter = stmt.query_map(&[], |row| {
        JobRequireVariable {
            id: row.get(0),
            fk_job: row.get(1),
            fk_variable: row.get(2),
        }
    });
    let mut items = Vec::<JobRequireVariable>::new();
    if wraped_fs_file_iter.is_err() {
        return items;

    }
    let fs_file_iter = wraped_fs_file_iter.unwrap();
    for person in fs_file_iter {

        items.push(person.unwrap());
    }
    return items;
}

pub fn job_require_variable_list(conn: &Connection) {
    let mut stmt = conn.prepare("SELECT id, fk_job, fk_variable  FROM JOB_REQUIRE_VARIABLE").unwrap();
    let person_iter = stmt.query_map(&[], |row| {
        JobRequireVariable {
            id: row.get(0),
            fk_job: row.get(1),
            fk_variable: row.get(2),
        }
    }).unwrap();

    for person in person_iter {
        println!("Found job_require_variable {:?}", person.unwrap());
    }
}

pub fn pk_job_require_variable_by_name(conn: &Connection, name: &String, pk: &mut i32) -> Result<i32, &'static str>{
    let mut stmt = conn.prepare("SELECT id, fk_job, fk_variable  FROM JOB_REQUIRE_VARIABLE WHERE name = ?1").unwrap();
    let job_require_variable_iter = stmt.query_map(&[name], |row| {
        JobRequireVariable {
            id: row.get(0),
            fk_job: row.get(1),
            fk_variable: row.get(2),
        }
    });
    if job_require_variable_iter.is_err() {
        return Err("Insert failed dfdf");
    }
    let result = job_require_variable_iter.unwrap();
    let mut found = 0;
    let mut items = Vec::<i32>::new();
    for person in result {
        let bill= person.unwrap();
        *pk = bill.id;
        found = 1;
    }
    if found != 0 {
        return Ok(found);
    }
    return Err("None found");
}

<commit_msg>Clean up table fields<commit_after>use rusqlite::Connection;
use std::result;

#[derive(Debug)]
pub struct JobRequireVariable {
    id: i32,
    fk_job: i32,
    fk_variable: i32,
}

pub fn table_create_job_require_variable(conn: &Connection)  {
    conn.execute("CREATE TABLE JOB_REQUIRE_VARIABLE (
                  id                    INTEGER PRIMARY KEY ASC,
                  fk_job                INTEGER NOT NULL,
                  fk_variable           INTEGER NOT NULL,
                  FOREIGN KEY(fk_job) REFERENCES JOB(id) ON UPDATE CASCADE
                  FOREIGN KEY(fk_variable) REFERENCES JOB_REQUIRE_VARIABLE(id) ON UPDATE CASCADE
                  )", &[]).unwrap();
}

pub fn insert_job_require_variable(conn: &Connection, job: &i32, variable: &i32) -> Result<i32, &'static str> {
    let me = JobRequireVariable {
        id: 0,
        fk_job: *job,
        fk_variable: *variable,

    };
    let job_require_variable_instance = conn.execute("INSERT INTO JOB_REQUIRE_VARIABLE (fk_job, fk_variable)
                  VALUES (?1, ?2)",
                 &[&me.fk_job, &me.fk_variable]);
    if job_require_variable_instance.is_err() {
        return Err("Insert failed");
    }
    job_require_variable_instance.unwrap();
    return Ok(0);
}

pub fn list_job_require_variable(conn: &Connection)-> Vec<JobRequireVariable> {
    let mut stmt = conn.prepare("SELECT id, fk_job, fk_variable  FROM JOB_REQUIRE_VARIABLE").unwrap();
    let wraped_fs_file_iter = stmt.query_map(&[], |row| {
        JobRequireVariable {
            id: row.get(0),
            fk_job: row.get(1),
            fk_variable: row.get(2),
        }
    });
    let mut items = Vec::<JobRequireVariable>::new();
    if wraped_fs_file_iter.is_err() {
        return items;

    }
    let fs_file_iter = wraped_fs_file_iter.unwrap();
    for person in fs_file_iter {

        items.push(person.unwrap());
    }
    return items;
}

pub fn job_require_variable_list(conn: &Connection) {
    let mut stmt = conn.prepare("SELECT id, fk_job, fk_variable  FROM JOB_REQUIRE_VARIABLE").unwrap();
    let person_iter = stmt.query_map(&[], |row| {
        JobRequireVariable {
            id: row.get(0),
            fk_job: row.get(1),
            fk_variable: row.get(2),
        }
    }).unwrap();

    for person in person_iter {
        println!("Found job_require_variable {:?}", person.unwrap());
    }
}

pub fn pk_job_require_variable_by_name(conn: &Connection, job: &i32, variable: &i32, pk: &mut i32) -> Result<i32, &'static str>{
    let mut stmt = conn.prepare("SELECT id, fk_job, fk_variable  FROM JOB_REQUIRE_VARIABLE WHERE fk_job = ?1 AND fk_variable = ?2").unwrap();
    let job_require_variable_iter = stmt.query_map(&[job, variable], |row| {
        JobRequireVariable {
            id: row.get(0),
            fk_job: row.get(1),
            fk_variable: row.get(2),
        }
    });
    if job_require_variable_iter.is_err() {
        return Err("Insert failed dfdf");
    }
    let result = job_require_variable_iter.unwrap();
    let mut found = 0;
    let mut items = Vec::<i32>::new();
    for person in result {
        let bill= person.unwrap();
        *pk = bill.id;
        found = 1;
    }
    if found != 0 {
        return Ok(found);
    }
    return Err("None found");
}

<|endoftext|>


<commit_before>#[cfg(feature = "postgres")]
extern crate url;

use support::{database, project};

#[test]
fn reset_drops_the_database() {
    let p = project("reset_drops_the_database")
        .folder("migrations")
        .build();
    let db = database(&p.database_url()).create();
    db.execute("CREATE TABLE posts ( id INTEGER )");

    assert!(db.table_exists("posts"));

    let result = p.command("database").arg("reset").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
}

#[test]
fn reset_runs_database_setup() {
    let p = project("reset_runs_database_setup")
        .folder("migrations")
        .build();
    let db = database(&p.database_url()).create();

    db.execute("CREATE TABLE posts ( id INTEGER )");
    db.execute("CREATE TABLE users ( id INTEGER )");
    p.create_migration(
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    assert!(db.table_exists("posts"));
    assert!(db.table_exists("users"));

    let result = p.command("database").arg("reset").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
    assert!(db.table_exists("users"));
    assert!(db.table_exists("__diesel_schema_migrations"));
}

#[test]
#[cfg(feature = "postgres")]
fn reset_handles_postgres_urls_with_username_and_password() {
    let p = project("handles_postgres_urls")
        .folder("migrations")
        .build();
    let db = database(&p.database_url()).create();

    db.execute("DROP ROLE IF EXISTS foo");
    db.execute("CREATE ROLE foo WITH LOGIN SUPERUSER PASSWORD 'password'");

    let database_url = {
        let mut new_url = url::Url::parse(&p.database_url()).expect("invalid url");
        new_url.set_username("foo").expect("could not set username");
        new_url
            .set_password(Some("password"))
            .expect("could not set password");
        new_url.to_string()
    };

    let result = p
        .command("database")
        .arg("reset")
        .env("DATABASE_URL", &database_url)
        .run();

    assert!(
        result.is_success(),
        "Result was unsuccessful {:?}",
        result.stdout()
    );
    assert!(
        result.stdout().contains("Dropping database:"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(
        result.stdout().contains("Creating database:"),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn reset_works_with_migration_dir_by_arg() {
    let p = project("reset_works_with_migration_dir_by_arg")
        .folder("foo")
        .build();
    let db = database(&p.database_url()).create();

    db.execute("CREATE TABLE posts ( id INTEGER )");
    db.execute("CREATE TABLE users ( id INTEGER )");
    p.create_migration_in_directory(
        "foo",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    assert!(db.table_exists("posts"));
    assert!(db.table_exists("users"));

    let result = p
        .command("database")
        .arg("reset")
        .arg("--migration-dir=foo")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
    assert!(db.table_exists("users"));
    assert!(db.table_exists("__diesel_schema_migrations"));
}

#[test]
fn reset_works_with_migration_dir_by_env() {
    let p = project("reset_works_with_migration_dir_by_env")
        .folder("bar")
        .build();
    let db = database(&p.database_url()).create();

    db.execute("CREATE TABLE posts ( id INTEGER )");
    db.execute("CREATE TABLE users ( id INTEGER )");
    p.create_migration_in_directory(
        "bar",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    assert!(db.table_exists("posts"));
    assert!(db.table_exists("users"));

    let result = p
        .command("database")
        .arg("reset")
        .env("MIGRATION_DIRECTORY", "bar")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
    assert!(db.table_exists("users"));
    assert!(db.table_exists("__diesel_schema_migrations"));
}

#[test]
fn reset_sanitize_database_name() {
    let p = project("name-with-dashes").folder("migrations").build();
    let _db = database(&p.database_url()).create();

    let result = p.command("database").arg("reset").run();

    assert!(
        result.is_success(),
        "Result was unsuccessful {:?}",
        result.stdout()
    );
    assert!(
        result.stdout().contains("Dropping database:"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(
        result.stdout().contains("Creating database:"),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn reset_updates_schema_if_config_present() {
    let p = project("reset_updates_schema_if_config_present")
        .folder("migrations")
        .file(
            "diesel.toml",
            r#"
            [print_schema]
            file = "src/my_schema.rs"
            "#,
        )
        .build();

    let result = p.command("database").arg("reset").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(p.has_file("src/my_schema.rs"));
}

#[test]
fn reset_respects_migrations_dir_from_diesel_toml() {
    let p = project("reset_respects_migrations_dir_from_diesel_toml")
        .folder("custom_migrations")
        .file(
            "diesel.toml",
            r#"
            [migrations_directory]
            dir = "custom_migrations"
            "#,
        )
        .build();
    let db = database(&p.database_url()).create();

    db.execute("CREATE TABLE posts ( id INTEGER )");
    db.execute("CREATE TABLE users ( id INTEGER )");

    p.create_migration_in_directory(
        "custom_migrations",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    assert!(db.table_exists("posts"));
    assert!(db.table_exists("users"));

    let result = p.command("database").arg("reset").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
    assert!(db.table_exists("users"));
    assert!(db.table_exists("__diesel_schema_migrations"));
}
<commit_msg>Fix database reset test<commit_after>#[cfg(feature = "postgres")]
extern crate url;

use support::{database, project};

#[test]
fn reset_drops_the_database() {
    let p = project("reset_drops_the_database")
        .folder("migrations")
        .build();
    let db = database(&p.database_url()).create();
    db.execute("CREATE TABLE posts ( id INTEGER )");

    assert!(db.table_exists("posts"));

    let result = p.command("database").arg("reset").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
}

#[test]
fn reset_runs_database_setup() {
    let p = project("reset_runs_database_setup")
        .folder("migrations")
        .build();
    let db = database(&p.database_url()).create();

    db.execute("CREATE TABLE posts ( id INTEGER )");
    db.execute("CREATE TABLE users ( id INTEGER )");
    p.create_migration(
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    assert!(db.table_exists("posts"));
    assert!(db.table_exists("users"));

    let result = p.command("database").arg("reset").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
    assert!(db.table_exists("users"));
    assert!(db.table_exists("__diesel_schema_migrations"));
}

#[test]
#[cfg(feature = "postgres")]
fn reset_handles_postgres_urls_with_username_and_password() {
    let p = project("handles_postgres_urls")
        .folder("migrations")
        .build();
    let db = database(&p.database_url()).create();

    db.execute("DROP ROLE IF EXISTS foo");
    db.execute("CREATE ROLE foo WITH LOGIN SUPERUSER PASSWORD 'password'");

    let database_url = {
        let mut new_url = url::Url::parse(&p.database_url()).expect("invalid url");
        new_url.set_username("foo").expect("could not set username");
        new_url
            .set_password(Some("password"))
            .expect("could not set password");
        new_url.to_string()
    };

    let result = p
        .command("database")
        .arg("reset")
        .env("DATABASE_URL", &database_url)
        .run();

    assert!(
        result.is_success(),
        "Result was unsuccessful {:?}",
        result.stdout()
    );
    assert!(
        result.stdout().contains("Dropping database:"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(
        result.stdout().contains("Creating database:"),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn reset_works_with_migration_dir_by_arg() {
    let p = project("reset_works_with_migration_dir_by_arg")
        .folder("foo")
        .build();
    let db = database(&p.database_url()).create();

    db.execute("CREATE TABLE posts ( id INTEGER )");
    db.execute("CREATE TABLE users ( id INTEGER )");
    p.create_migration_in_directory(
        "foo",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    assert!(db.table_exists("posts"));
    assert!(db.table_exists("users"));

    let result = p
        .command("database")
        .arg("reset")
        .arg("--migration-dir=foo")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
    assert!(db.table_exists("users"));
    assert!(db.table_exists("__diesel_schema_migrations"));
}

#[test]
fn reset_works_with_migration_dir_by_env() {
    let p = project("reset_works_with_migration_dir_by_env")
        .folder("bar")
        .build();
    let db = database(&p.database_url()).create();

    db.execute("CREATE TABLE posts ( id INTEGER )");
    db.execute("CREATE TABLE users ( id INTEGER )");
    p.create_migration_in_directory(
        "bar",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    assert!(db.table_exists("posts"));
    assert!(db.table_exists("users"));

    let result = p
        .command("database")
        .arg("reset")
        .env("MIGRATION_DIRECTORY", "bar")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(!db.table_exists("posts"));
    assert!(db.table_exists("users"));
    assert!(db.table_exists("__diesel_schema_migrations"));
}

#[test]
fn reset_sanitize_database_name() {
    let p = project("name-with-dashes").folder("migrations").build();
    let _db = database(&p.database_url()).create();

    let result = p.command("database").arg("reset").run();

    assert!(
        result.is_success(),
        "Result was unsuccessful {:?}",
        result.stdout()
    );
    assert!(
        result.stdout().contains("Dropping database:"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(
        result.stdout().contains("Creating database:"),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn reset_updates_schema_if_config_present() {
    let p = project("reset_updates_schema_if_config_present")
        .folder("migrations")
        .file(
            "diesel.toml",
            r#"
            [print_schema]
            file = "src/my_schema.rs"
            "#,
        )
        .build();

    let result = p.command("database").arg("reset").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(p.has_file("src/my_schema.rs"));
}

#[test]
fn reset_respects_migrations_dir_from_diesel_toml() {
    let p = project("reset_respects_migrations_dir_from_diesel_toml")
        .folder("custom_migrations")
        .file(
            "diesel.toml",
            r#"
            [migrations_directory]
            dir = "custom_migrations"
            "#,
        )
        .build();
    let db = database(&p.database_url()).create();

    db.execute("CREATE TABLE users ( id INTEGER )");

    p.create_migration_in_directory(
        "custom_migrations",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    assert!(db.table_exists("users"));

    let result = p.command("database").arg("reset").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(db.table_exists("users"));
    assert!(db.table_exists("__diesel_schema_migrations"));
}
<|endoftext|>