<commit_before>// Copyright (c) 2017 V Bota
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

extern crate rppal;

use std::env;
use std::ffi::OsString;
use rppal::gpio::{GPIO,Mode,Level};

const LAST_PIN_NUMBER: u8 = 27;
const HELP_CMD_STR: &'static str = "help";
const SET_CMD_STR: &'static str = "set";
const GET_CMD_STR: &'static str = "get";
const HIGH_PIN_STATE_STR: &'static str = "high";
const LOW_PIN_STATE_STR: &'static str = "low";
const ALL_PINS_STR: &'static str = "all";
const HELP_EXAMPLE_STR: &'static str = "sudo pi3gpio help";

fn main() {
	handle_cmd ( env::args_os() );
}

fn handle_cmd (mut cmd: env::ArgsOs) {
	match cmd.nth(1) {
		Some (action) => {
			if action == OsString::from(HELP_CMD_STR) {
				print_help_text ();
			}
			else if action == OsString::from(SET_CMD_STR) {
				set_pins (cmd);
			}
			else if action == OsString::from(GET_CMD_STR) {
				read_pins (cmd);
			}
			else {
				panic! ("Unknown command {:?}! \nFor help run \"{}\"", action, HELP_EXAMPLE_STR );
			}
		},
		None => panic! ("No command found! \nFor help run \"{}\"", HELP_EXAMPLE_STR ),
	}
}

fn print_help_text () {
	println! ("SYNTAX: sudo pi3gpio command [state] [pins]");
	println! ("	command: ");
	println! ("		{}	prints the help text", HELP_CMD_STR );
	println! ("		{}	sets the value of the indicated pins to the indicated value", SET_CMD_STR );
	println! ("		{}	prints the state of the indicated pins", GET_CMD_STR );
	println! ("	state: ");
	println! ("		{}	logical 1 for the indicated pins, equivalent voltage 3.3[V]", HIGH_PIN_STATE_STR );
	println! ("		{}	logical 0 for the indicated pins, equivalent voltage 0[V]", LOW_PIN_STATE_STR );
	println! ("	pins: ");
	println! ("		{}	BCM pins between 0 and {}", ALL_PINS_STR, LAST_PIN_NUMBER );
	println! ("Examples:");
	println! ("	sudo pi3gpio help ");
	println! ("	sudo pi3gpio get all");
	println! ("	sudo pi3gpio get 4");
	println! ("	sudo pi3gpio get 10 11");
	println! ("	sudo pi3gpio set low all");
	println! ("	sudo pi3gpio set high 12");
	println! ("	sudo pi3gpio set low 2 5 7");
}

trait ReadState{
	fn read_state (self, gpio: &mut GPIO);
}

impl ReadState for OsString {
	fn read_state (self, gpio: &mut GPIO) {
		match self.to_u8() {
			Ok (pin) => read_pin ( gpio, pin ),
			Err (e) => {
				panic! ("{} \nFor help run \"{}\"", e,  HELP_EXAMPLE_STR );
			},
		}
	}
}

fn read_all_pins ( gpio: &mut GPIO ) {
	for pin in 0..(LAST_PIN_NUMBER+1) {
		read_pin ( gpio, pin );
	}
}

fn read_pin ( gpio: &mut GPIO, pin: u8) {
	gpio.set_mode( pin, Mode::Input );
	match gpio.read(pin) {
		Ok(state) =>	println! ("Pin {} = {:?}", pin, state ),
		Err (_) => println! ("Could not read state of pin {}",pin),
	}
}

fn read_pins (mut cmd: env::ArgsOs) {
	let mut gpio = initiate_gpio_handle();

	match cmd.nth(0) {
		Some (pin_as_os_string) => {
			if pin_as_os_string == OsString::from(ALL_PINS_STR) {
				read_all_pins ( &mut gpio );
				return;
			}
			else {
				pin_as_os_string.read_state ( &mut gpio );
			}
		}
		None => panic! ("Pin not found! \nFor help run \"{}\"",  HELP_EXAMPLE_STR ),
	}

	for argument in cmd {
		argument.read_state ( &mut gpio );
	}

}

trait ToU8 {
	fn to_u8 (self) -> Result<u8, &'static str >;
}

impl ToU8 for OsString {
	fn to_u8 (self) -> Result<u8, &'static str> {
		match self.into_string() {
			Ok(x) => match x.parse::<u8>() {
				Ok (y) => return Ok (y),
				Err(_) => return Err("String indicating pin number could not be converted to u8!" ),
			},
			Err(_) => return Err("OsString indicating pin number could not be converted to String!" ),
		}
	}
}

fn initiate_gpio_handle () -> GPIO {
    let gpio: GPIO;
    match GPIO::new(){
	    Ok (x) => gpio = x,
	    Err (e) => panic! ("Could not access GPIO memory! {}",e),
    }

    gpio
}

fn get_pin_state ( argument: Option<OsString>  ) -> Level {
	let mut pin_state = Level::Low;
	match argument {
		Some (state) => {
			if state == OsString::from(HIGH_PIN_STATE_STR) {
				pin_state = Level::High;
			}
			else if state != OsString::from(LOW_PIN_STATE_STR) {
				panic! ("State {:?} is invalid! \nFor help run \"{}\"", state, HELP_EXAMPLE_STR );
			}
		},
		None => panic! ("State not found! \nFor help run \"{}\"",  HELP_EXAMPLE_STR ),
	}

    pin_state
}

trait SetState{
	fn set_state (self, pin_state: Level ,gpio: &mut GPIO);
}

impl SetState for OsString {
	fn set_state (self, pin_state: Level, gpio: &mut GPIO) {
		match self.to_u8() {
			Ok (pin) => set_pin ( gpio, pin, pin_state ),
			Err (e) => {
				panic! ("{} \nFor help run \"{}\"", e,  HELP_EXAMPLE_STR );
			},
		}
	}
}

fn set_all_pins ( gpio: &mut GPIO, pin_state: Level ) {
	for pin in 0..(LAST_PIN_NUMBER+1) {
		set_pin ( gpio, pin, pin_state );
	}
}

fn set_pin ( gpio: &mut GPIO, pin: u8, pin_state: Level ) {
	println! ("Set pin {} to {:?}", pin, pin_state);
	gpio.set_mode( pin, Mode::Output );
	gpio.write ( pin, pin_state );
}

fn set_pins (mut cmd: env::ArgsOs) {
	let mut gpio = initiate_gpio_handle();
	gpio.set_clear_on_drop ( false );

	let pin_state = get_pin_state( cmd.nth(0) );

	match cmd.nth(0) {
		Some (pin_as_os_string) => {
			if pin_as_os_string == OsString::from(ALL_PINS_STR) {
				set_all_pins ( &mut gpio, pin_state );
				return;
			}
			else {
				pin_as_os_string.set_state( pin_state, &mut gpio );
			}

		}
		None => panic! ("Pin not found! \nFor help run \"{}\"",  HELP_EXAMPLE_STR ),
	}

	for argument in cmd {
		argument.set_state( pin_state, &mut gpio );
	}
}

// For library "rppal" version "0.1.2" the following LICENSE (MIT) applyes:
// "
//   Copyright (c) 2017 Rene van der Meer
//
//   Permission is hereby granted, free of charge, to any person obtaining a
//   copy of this software and associated documentation files (the "Software"),
//   to deal in the Software without restriction, including without limitation
//   the rights to use, copy, modify, merge, publish, distribute, sublicense,
//   and/or sell copies of the Software, and to permit persons to whom the
//   Software is furnished to do so, subject to the following conditions:
//
//   The above copyright notice and this permission notice shall be included in
//   all copies or substantial portions of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
//   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//   DEALINGS IN THE SOFTWARE.
// "

// For the "std" rust library the following LINCESE (MIT) applyes:
// "
//   Copyright (c) 2010 The Rust Project Developers
//
//   Permission is hereby granted, free of charge, to any
//   person obtaining a copy of this software and associated
//   documentation files (the "Software"), to deal in the
//   Software without restriction, including without
//   limitation the rights to use, copy, modify, merge,
//   publish, distribute, sublicense, and/or sell copies of
//   the Software, and to permit persons to whom the Software
//   is furnished to do so, subject to the following
//   conditions:
//
//   The above copyright notice and this permission notice
//   shall be included in all copies or substantial portions
//   of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
//   ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//   TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
//   PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
//   SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
//   IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//   DEALINGS IN THE SOFTWARE.
// "
<commit_msg>Update main.rs<commit_after>// Copyright (c) 2017 Viorel Bota
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

extern crate rppal;

use std::env;
use std::ffi::OsString;
use rppal::gpio::{GPIO,Mode,Level};

const LAST_PIN_NUMBER: u8 = 27;
const HELP_CMD_STR: &'static str = "help";
const SET_CMD_STR: &'static str = "set";
const GET_CMD_STR: &'static str = "get";
const HIGH_PIN_STATE_STR: &'static str = "high";
const LOW_PIN_STATE_STR: &'static str = "low";
const ALL_PINS_STR: &'static str = "all";
const HELP_EXAMPLE_STR: &'static str = "sudo pi3gpio help";

fn main() {
	handle_cmd ( env::args_os() );
}

fn handle_cmd (mut cmd: env::ArgsOs) {
	match cmd.nth(1) {
		Some (action) => {
			if action == OsString::from(HELP_CMD_STR) {
				print_help_text ();
			}
			else if action == OsString::from(SET_CMD_STR) {
				set_pins (cmd);
			}
			else if action == OsString::from(GET_CMD_STR) {
				read_pins (cmd);
			}
			else {
				panic! ("Unknown command {:?}! \nFor help run \"{}\"", action, HELP_EXAMPLE_STR );
			}
		},
		None => panic! ("No command found! \nFor help run \"{}\"", HELP_EXAMPLE_STR ),
	}
}

fn print_help_text () {
	println! ("SYNTAX: sudo pi3gpio command [state] [pins]");
	println! ("	command: ");
	println! ("		{}	prints the help text", HELP_CMD_STR );
	println! ("		{}	sets the value of the indicated pins to the indicated value", SET_CMD_STR );
	println! ("		{}	prints the state of the indicated pins", GET_CMD_STR );
	println! ("	state: ");
	println! ("		{}	logical 1 for the indicated pins, equivalent voltage 3.3[V]", HIGH_PIN_STATE_STR );
	println! ("		{}	logical 0 for the indicated pins, equivalent voltage 0[V]", LOW_PIN_STATE_STR );
	println! ("	pins: ");
	println! ("		{}	BCM pins between 0 and {}", ALL_PINS_STR, LAST_PIN_NUMBER );
	println! ("Examples:");
	println! ("	sudo pi3gpio help ");
	println! ("	sudo pi3gpio get all");
	println! ("	sudo pi3gpio get 4");
	println! ("	sudo pi3gpio get 10 11");
	println! ("	sudo pi3gpio set low all");
	println! ("	sudo pi3gpio set high 12");
	println! ("	sudo pi3gpio set low 2 5 7");
}

trait ReadState{
	fn read_state (self, gpio: &mut GPIO);
}

impl ReadState for OsString {
	fn read_state (self, gpio: &mut GPIO) {
		match self.to_u8() {
			Ok (pin) => read_pin ( gpio, pin ),
			Err (e) => {
				panic! ("{} \nFor help run \"{}\"", e,  HELP_EXAMPLE_STR );
			},
		}
	}
}

fn read_all_pins ( gpio: &mut GPIO ) {
	for pin in 0..(LAST_PIN_NUMBER+1) {
		read_pin ( gpio, pin );
	}
}

fn read_pin ( gpio: &mut GPIO, pin: u8) {
	gpio.set_mode( pin, Mode::Input );
	match gpio.read(pin) {
		Ok(state) =>	println! ("Pin {} = {:?}", pin, state ),
		Err (_) => println! ("Could not read state of pin {}",pin),
	}
}

fn read_pins (mut cmd: env::ArgsOs) {
	let mut gpio = initiate_gpio_handle();

	match cmd.nth(0) {
		Some (pin_as_os_string) => {
			if pin_as_os_string == OsString::from(ALL_PINS_STR) {
				read_all_pins ( &mut gpio );
				return;
			}
			else {
				pin_as_os_string.read_state ( &mut gpio );
			}
		}
		None => panic! ("Pin not found! \nFor help run \"{}\"",  HELP_EXAMPLE_STR ),
	}

	for argument in cmd {
		argument.read_state ( &mut gpio );
	}

}

trait ToU8 {
	fn to_u8 (self) -> Result<u8, &'static str >;
}

impl ToU8 for OsString {
	fn to_u8 (self) -> Result<u8, &'static str> {
		match self.into_string() {
			Ok(x) => match x.parse::<u8>() {
				Ok (y) => return Ok (y),
				Err(_) => return Err("String indicating pin number could not be converted to u8!" ),
			},
			Err(_) => return Err("OsString indicating pin number could not be converted to String!" ),
		}
	}
}

fn initiate_gpio_handle () -> GPIO {
    let gpio: GPIO;
    match GPIO::new(){
	    Ok (x) => gpio = x,
	    Err (e) => panic! ("Could not access GPIO memory! {}",e),
    }

    gpio
}

fn get_pin_state ( argument: Option<OsString>  ) -> Level {
	let mut pin_state = Level::Low;
	match argument {
		Some (state) => {
			if state == OsString::from(HIGH_PIN_STATE_STR) {
				pin_state = Level::High;
			}
			else if state != OsString::from(LOW_PIN_STATE_STR) {
				panic! ("State {:?} is invalid! \nFor help run \"{}\"", state, HELP_EXAMPLE_STR );
			}
		},
		None => panic! ("State not found! \nFor help run \"{}\"",  HELP_EXAMPLE_STR ),
	}

    pin_state
}

trait SetState{
	fn set_state (self, pin_state: Level ,gpio: &mut GPIO);
}

impl SetState for OsString {
	fn set_state (self, pin_state: Level, gpio: &mut GPIO) {
		match self.to_u8() {
			Ok (pin) => set_pin ( gpio, pin, pin_state ),
			Err (e) => {
				panic! ("{} \nFor help run \"{}\"", e,  HELP_EXAMPLE_STR );
			},
		}
	}
}

fn set_all_pins ( gpio: &mut GPIO, pin_state: Level ) {
	for pin in 0..(LAST_PIN_NUMBER+1) {
		set_pin ( gpio, pin, pin_state );
	}
}

fn set_pin ( gpio: &mut GPIO, pin: u8, pin_state: Level ) {
	println! ("Set pin {} to {:?}", pin, pin_state);
	gpio.set_mode( pin, Mode::Output );
	gpio.write ( pin, pin_state );
}

fn set_pins (mut cmd: env::ArgsOs) {
	let mut gpio = initiate_gpio_handle();
	gpio.set_clear_on_drop ( false );

	let pin_state = get_pin_state( cmd.nth(0) );

	match cmd.nth(0) {
		Some (pin_as_os_string) => {
			if pin_as_os_string == OsString::from(ALL_PINS_STR) {
				set_all_pins ( &mut gpio, pin_state );
				return;
			}
			else {
				pin_as_os_string.set_state( pin_state, &mut gpio );
			}

		}
		None => panic! ("Pin not found! \nFor help run \"{}\"",  HELP_EXAMPLE_STR ),
	}

	for argument in cmd {
		argument.set_state( pin_state, &mut gpio );
	}
}

// For library "rppal" version "0.1.2" the following LICENSE (MIT) applyes:
// "
//   Copyright (c) 2017 Rene van der Meer
//
//   Permission is hereby granted, free of charge, to any person obtaining a
//   copy of this software and associated documentation files (the "Software"),
//   to deal in the Software without restriction, including without limitation
//   the rights to use, copy, modify, merge, publish, distribute, sublicense,
//   and/or sell copies of the Software, and to permit persons to whom the
//   Software is furnished to do so, subject to the following conditions:
//
//   The above copyright notice and this permission notice shall be included in
//   all copies or substantial portions of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
//   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//   DEALINGS IN THE SOFTWARE.
// "

// For the "std" rust library the following LINCESE (MIT) applyes:
// "
//   Copyright (c) 2010 The Rust Project Developers
//
//   Permission is hereby granted, free of charge, to any
//   person obtaining a copy of this software and associated
//   documentation files (the "Software"), to deal in the
//   Software without restriction, including without
//   limitation the rights to use, copy, modify, merge,
//   publish, distribute, sublicense, and/or sell copies of
//   the Software, and to permit persons to whom the Software
//   is furnished to do so, subject to the following
//   conditions:
//
//   The above copyright notice and this permission notice
//   shall be included in all copies or substantial portions
//   of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
//   ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//   TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
//   PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
//   SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
//   IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//   DEALINGS IN THE SOFTWARE.
// "
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;
extern crate calx_system;
extern crate calx_window;
extern crate cgmath;

use std::default::Default;
use calx_window::{WindowBuilder, Event, Key};
use cgmath::Angle;
use glium::Surface;

/// Cube face.
#[derive(Copy, Clone)]
enum Face {
    East,
    North,
    Up,
    West,
    South,
    Down,
}

impl Face {
    /// Normal vector for the face.
    pub fn normal(&self) -> [f32; 3] {
        match *self {
            Face::East => [1.0, 0.0, 0.0],
            Face::North => [0.0, 1.0, 0.0],
            Face::Up => [0.0, 0.0, 1.0],
            Face::West => [-1.0, 0.0, 0.0],
            Face::South => [0.0, -1.0, 0.0],
            Face::Down => [0.0, 0.0, -1.0],
        }
    }

    /// Counter-clockwise vertex list using cube vertex indices.
    #[inline]
    pub fn vertices(&self) -> [usize; 4] {
        [[1, 3, 7, 5],
         [2, 6, 7, 3],
         [4, 5, 7, 6],
         [0, 4, 6, 2],
         [0, 1, 5, 4],
         [0, 2, 3, 1]][*self as usize]
    }
}

/// Vertex of a default [0, 0, 0] - [1, 1, 1] cube.
///
/// There are 8 indices, corresponding to 3 bits. The low bit denotes x-axis
/// extension, the middle bit y-axis extension and the high bit z-axis
/// extension.
///
/// ```notrust
///     6 --- 7
///    /:    /|
///   4 --- 5 |
///   | :   | |      Z
///   | 2 ..|.3      | Y
///   |.    |/       |/
///   0 --- 1        o---X
///
/// ```
#[inline]
fn cube_vertex(index: usize) -> [f32; 3] {
    [[0.0, 0.0, 0.0],
     [0.1, 0.0, 0.0],
     [0.0, 0.1, 0.0],
     [0.1, 0.1, 0.0],
     [0.0, 0.0, 0.1],
     [0.1, 0.0, 0.1],
     [0.0, 0.1, 0.1],
     [0.1, 0.1, 0.1]][index]
}

#[derive(Copy, Clone)]
pub struct Vertex {
    pub pos: [f32; 3],
    pub normal: [f32; 3],
    pub color: [f32; 4],
}
implement_vertex!(Vertex, pos, normal, color);

struct Voxel {
    color: [f32; 4],
}

fn get_voxel(voxel_pos: [i32; 3]) -> Option<Voxel> {
    // TODO: Perlin noise landscape or something.
    if voxel_pos[2] < 0 {
        Some(Voxel { color: [0.0, 1.0, 0.0, 1.0] })
    } else {
        None
    }
}

fn main() {
    let mut window = WindowBuilder::new().set_title("Voxel demo").build();

    let shader = program!(&window.display,
            150 => {
            vertex: "
                #version 150 core

                uniform mat4 projection;
                uniform mat4 modelview;

                in vec3 pos;
                in vec3 normal;
                in vec4 color;
                out vec3 v_pos;
                out vec3 v_normal;
                out vec4 v_color;

                void main() {
                    v_pos = pos;
                    v_normal = normal;
                    v_color = color;
                    gl_Position = projection * modelview * vec4(v_pos, 1.0);
                }
            ",

            fragment: "
                #version 150 core

                in vec3 v_normal;
                in vec4 v_color;
                out vec4 f_color;
                const vec3 LIGHT = vec3(-0.2, 0.8, 0.1);

                void main() {
                    float lum = max(dot(normalize(v_normal), normalize(LIGHT)), 0.0);
                    f_color = (0.3 + 0.7 * lum) * v_color;
                }
            ",
            }
        ).unwrap();

    loop {
        for e in window.events().into_iter() {
            match e {
                Event::Quit => return,
                Event::KeyPress(Key::Escape) => return,
                Event::KeyPress(Key::F12) => {
                    calx_system::save_screenshot("calx", window.screenshot())
                        .unwrap();
                }
                _ => (),
            }
        }
        window.clear(0x7799DDFF);

        // Camera setup

        let projection: cgmath::Matrix4<f32> =
            cgmath::PerspectiveFov {
                fovy: cgmath::Deg::new(90.0).into(),
                aspect: 640.0 / 380.0, // XXX: Hardcoding
                near: 0.01,
                far: 1024.0,
            }
            .into();

        let modelview: cgmath::Matrix4<f32> =
            cgmath::Matrix4::look_at(cgmath::Point3::new(1.0, -1.0, 2.0),
                                     cgmath::Point3::new(0.0, 0.0, 0.0),
                                     cgmath::vec3(0.0, 0.0, 1.0));

        // Convert to the format the shader expects.
        let projection: [[f32; 4]; 4] = projection.into();
        let modelview: [[f32; 4]; 4] = modelview.into();

        let uniforms = uniform! {
            projection: projection,
            modelview: modelview,
        };

        // Draw parameters

        let params = glium::DrawParameters {
            depth: glium::Depth {
                test: glium::DepthTest::IfLess,
                write: true,
                ..Default::default()
            },
            ..Default::default()
        };

        // Setup the cube model
        let faces = [Face::East,
                     Face::North,
                     Face::Up,
                     Face::West,
                     Face::South,
                     Face::Down];
        let mut vertices = Vec::new();
        let mut indices = Vec::new();
        for f in 0..6 {
            let normal = faces[f].normal();
            let idx = vertices.len() as u16;
            for &v_idx in faces[f].vertices().into_iter() {
                vertices.push(Vertex {
                    pos: cube_vertex(v_idx),
                    normal: normal,
                    color: [1.0, 0.0, 0.0, 1.0],
                });

            }
            indices.push(idx);
            indices.push(idx + 1);
            indices.push(idx + 2);
            indices.push(idx);
            indices.push(idx + 2);
            indices.push(idx + 3);
        }
        let v_buf = glium::VertexBuffer::new(&window.display, &vertices).unwrap();
        let i_buf =
            glium::IndexBuffer::new(&window.display,
                                    glium::index::PrimitiveType::TrianglesList,
                                    &indices)
                .unwrap();

        // Draw the thing

        {
            let mut target = window.get_framebuffer_target();
            target.draw(&v_buf, &i_buf, &shader, &uniforms, &params).unwrap();
        }

        // window.display(&mut room);

        window.end_frame();
    }
}
<commit_msg>Voxel demo tweaking<commit_after>#[macro_use]
extern crate glium;
extern crate calx_system;
extern crate calx_window;
extern crate cgmath;

use std::default::Default;
use calx_window::{WindowBuilder, Event, Key};
use cgmath::Angle;
use glium::Surface;

/// Cube face.
#[derive(Copy, Clone)]
enum Face {
    East,
    North,
    Up,
    West,
    South,
    Down,
}

impl Face {
    /// Normal vector for the face.
    pub fn normal(&self) -> [f32; 3] {
        match *self {
            Face::East => [1.0, 0.0, 0.0],
            Face::North => [0.0, 1.0, 0.0],
            Face::Up => [0.0, 0.0, 1.0],
            Face::West => [-1.0, 0.0, 0.0],
            Face::South => [0.0, -1.0, 0.0],
            Face::Down => [0.0, 0.0, -1.0],
        }
    }

    /// Counter-clockwise vertex list using cube vertex indices.
    #[inline]
    pub fn vertices(&self) -> [usize; 4] {
        [[1, 3, 7, 5],
         [2, 6, 7, 3],
         [4, 5, 7, 6],
         [0, 4, 6, 2],
         [0, 1, 5, 4],
         [0, 2, 3, 1]][*self as usize]
    }
}

/// Vertex of a default [0, 0, 0] - [1, 1, 1] cube.
///
/// There are 8 indices, corresponding to 3 bits. The low bit denotes x-axis
/// extension, the middle bit y-axis extension and the high bit z-axis
/// extension.
///
/// ```notrust
///     6 --- 7
///    /:    /|
///   4 --- 5 |
///   | :   | |      Z
///   | 2 ..|.3      | Y
///   |.    |/       |/
///   0 --- 1        o---X
///
/// ```
#[inline]
fn cube_vertex(index: usize) -> [f32; 3] {
    [[0.0, 0.0, 0.0],
     [1.0, 0.0, 0.0],
     [0.0, 1.0, 0.0],
     [1.0, 1.0, 0.0],
     [0.0, 0.0, 1.0],
     [1.0, 0.0, 1.0],
     [0.0, 1.0, 1.0],
     [1.0, 1.0, 1.0]][index]
}

#[derive(Copy, Clone)]
pub struct Vertex {
    pub pos: [f32; 3],
    pub normal: [f32; 3],
    pub color: [f32; 4],
}
implement_vertex!(Vertex, pos, normal, color);

struct Voxel {
    color: [f32; 4],
}

fn get_voxel(voxel_pos: [i32; 3]) -> Option<Voxel> {
    // TODO: Perlin noise landscape or something.
    if voxel_pos[2] < 0 {
        Some(Voxel { color: [0.0, 1.0, 0.0, 1.0] })
    } else {
        None
    }
}

fn main() {
    let mut window = WindowBuilder::new().set_title("Voxel demo").build();

    let shader = program!(&window.display,
            150 => {
            vertex: "
                #version 150 core

                uniform mat4 projection;
                uniform mat4 modelview;

                in vec3 pos;
                in vec3 normal;
                in vec4 color;
                out vec3 v_pos;
                out vec3 v_normal;
                out vec4 v_color;

                void main() {
                    v_pos = pos;
                    v_normal = normal;
                    v_color = color;
                    gl_Position = projection * modelview * vec4(v_pos, 1.0);
                }
            ",

            fragment: "
                #version 150 core

                in vec3 v_normal;
                in vec4 v_color;
                out vec4 f_color;
                const vec3 LIGHT = vec3(-0.2, 0.8, 0.1);

                void main() {
                    float lum = max(dot(normalize(v_normal), normalize(LIGHT)), 0.0);
                    f_color = (0.3 + 0.7 * lum) * v_color;
                }
            ",
            }
        ).unwrap();

    let mut tick = 0;
    loop {
        for e in window.events().into_iter() {
            match e {
                Event::Quit => return,
                Event::KeyPress(Key::Escape) => return,
                Event::KeyPress(Key::F12) => {
                    calx_system::save_screenshot("calx", window.screenshot())
                        .unwrap();
                }
                _ => (),
            }
        }
        window.clear(0x7799DDFF);

        // Camera setup

        let projection: cgmath::Matrix4<f32> =
            cgmath::PerspectiveFov {
                fovy: cgmath::Deg::new(45.0).into(),
                aspect: 640.0 / 380.0, // XXX: Hardcoding
                near: 0.1,
                far: 1024.0,
            }
            .into();

        let a = (tick as f32) / 32.0;
        let modelview: cgmath::Matrix4<f32> =
            cgmath::Matrix4::look_at(cgmath::Point3::new(5.0 * a.sin() + 0.5, 5.0 * a.cos() + 0.5, 2.0),
                                     cgmath::Point3::new(0.5, 0.5, 0.0),
                                     cgmath::vec3(0.0, 0.0, 1.0));

        // Convert to the format the shader expects.
        let projection: [[f32; 4]; 4] = projection.into();
        let modelview: [[f32; 4]; 4] = modelview.into();

        let uniforms = uniform! {
            projection: projection,
            modelview: modelview,
        };

        // Draw parameters

        let params = glium::DrawParameters {
            depth: glium::Depth {
                test: glium::DepthTest::IfLess,
                write: true,
                ..Default::default()
            },
            ..Default::default()
        };

        // Setup the cube model
        let faces = [Face::East,
                     Face::North,
                     Face::Up,
                     Face::West,
                     Face::South,
                     Face::Down];
        let mut vertices = Vec::new();
        let mut indices = Vec::new();
        for f in 0..6 {
            let normal = faces[f].normal();
            let idx = vertices.len() as u16;
            for &v_idx in faces[f].vertices().into_iter() {
                vertices.push(Vertex {
                    pos: cube_vertex(v_idx),
                    normal: normal,
                    color: [1.0, 0.0, 0.0, 1.0],
                });

            }
            indices.push(idx);
            indices.push(idx + 1);
            indices.push(idx + 2);
            indices.push(idx);
            indices.push(idx + 2);
            indices.push(idx + 3);
        }
        let v_buf = glium::VertexBuffer::new(&window.display, &vertices).unwrap();
        let i_buf =
            glium::IndexBuffer::new(&window.display,
                                    glium::index::PrimitiveType::TrianglesList,
                                    &indices)
                .unwrap();

        // Draw the thing

        {
            let mut target = window.get_framebuffer_target();
            target.draw(&v_buf, &i_buf, &shader, &uniforms, &params).unwrap();
        }

        // window.display(&mut room);

        window.end_frame();

        tick += 1;
    }
}
<|endoftext|>


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

#![feature(io)]

use std::old_io::process::{Command, ProcessExit, StdioContainer};
use std::os;

fn main() {
    let out_dir = os::getenv("OUT_DIR").unwrap();
    let result = Command::new("make")
        .args(&["-f", "makefile.cargo"])
        .stdout(StdioContainer::InheritFd(1))
        .stderr(StdioContainer::InheritFd(2))
        .status()
        .unwrap();
    assert_eq!(result, ProcessExit::ExitStatus(0));
    println!("cargo:rustc-flags=-L native={}", out_dir);
}
<commit_msg>Fix warnings in rust-task_info.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(env)]
#![feature(io)]

use std::old_io::process::{Command, ProcessExit, StdioContainer};
use std::env;

fn main() {
    let out_dir = env::var_string("OUT_DIR").unwrap();
    let result = Command::new("make")
        .args(&["-f", "makefile.cargo"])
        .stdout(StdioContainer::InheritFd(1))
        .stderr(StdioContainer::InheritFd(2))
        .status()
        .unwrap();
    assert_eq!(result, ProcessExit::ExitStatus(0));
    println!("cargo:rustc-flags=-L native={}", out_dir);
}
<|endoftext|>


<commit_before>use rand::Rng;
use std::fmt;

pub struct ReservoirSampling<T, R>
where
    R: Rng,
{
    k: usize,
    rng: R,
    reservoir: Vec<T>,
    i: usize,
    skip_until: usize,
}

impl<T, R> ReservoirSampling<T, R>
where
    R: Rng,
{
    pub fn new(k: usize, rng: R) -> ReservoirSampling<T, R> {
        ReservoirSampling {
            k: k,
            rng: rng,
            reservoir: vec![],
            i: 0,
            skip_until: 0,
        }
    }

    pub fn k(&self) -> usize {
        self.k
    }

    pub fn reservoir(&self) -> &Vec<T> {
        &self.reservoir
    }

    pub fn i(&self) -> usize {
        self.i
    }

    pub fn add(&mut self, obj: T) {
        let t = self.k * 4; // TODO: make this a parameter

        if self.i < self.k {
            // initial fill-up
            self.reservoir.push(obj)
        } else if self.i < t {
            // normal reservoir sampling
            let j = self.rng.gen_range::<usize>(0, self.i);
            if j < self.k {
                self.reservoir[j] = obj;
            }
        } else if self.i >= self.skip_until {
            // fast skipping approximation
            let j = self.rng.gen_range::<usize>(0, self.k);
            self.reservoir[j] = obj;

            // calculate next skip
            let p = (self.k as f64) / ((self.i + 1) as f64);
            let u = 1. - self.rng.gen_range::<f64>(0., 1.); // (0.0, 1.0]
            let g = (u.ln() / (1. - p).ln()).floor() as usize;
            self.skip_until = self.i + g;
        }

        self.i += 1;
    }

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

impl<T, R> fmt::Debug for ReservoirSampling<T, R>
where
    R: Rng,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ReservoirSampling {{ k: {} }}", self.k)
    }
}

#[cfg(test)]
mod tests {
    use super::ReservoirSampling;
    use rand;

    #[test]
    fn getter() {
        let rs = ReservoirSampling::<u64, rand::ThreadRng>::new(10, rand::thread_rng());
        assert_eq!(rs.k(), 10);
        assert_eq!(rs.i(), 0);
        assert_eq!(rs.reservoir(), &vec![]);
    }

    #[test]
    fn empty() {
        let rs = ReservoirSampling::<u64, rand::ThreadRng>::new(10, rand::thread_rng());
        assert!(rs.is_empty());
        assert!(rs.reservoir().is_empty());
    }

    #[test]
    fn add_k() {
        let mut rs = ReservoirSampling::<u64, rand::ThreadRng>::new(10, rand::thread_rng());
        for i in 0..10 {
            rs.add(i);
        }
        assert_eq!(rs.i(), 10);
        assert_eq!(rs.reservoir(), &vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
    }

    #[test]
    fn add_parts() {
        let mut rs = ReservoirSampling::<u64, rand::ThreadRng>::new(100, rand::thread_rng());
        for _ in 0..150 {
            rs.add(0);
        }
        for _ in 0..750 {
            rs.add(1);
        }
        for _ in 0..100 {
            rs.add(0);
        }
        assert!((rs.reservoir().iter().sum::<u64>() as i64 - 75).abs() < 5);
    }

    #[test]
    fn debug() {
        let rs = ReservoirSampling::<u64, rand::ThreadRng>::new(10, rand::thread_rng());
        assert_eq!(format!("{:?}", rs), "ReservoirSampling { k: 10 }");
    }
}
<commit_msg>reservoirsampling: less random tests/failues<commit_after>use rand::Rng;
use std::fmt;

pub struct ReservoirSampling<T, R>
where
    R: Rng,
{
    k: usize,
    rng: R,
    reservoir: Vec<T>,
    i: usize,
    skip_until: usize,
}

impl<T, R> ReservoirSampling<T, R>
where
    R: Rng,
{
    pub fn new(k: usize, rng: R) -> ReservoirSampling<T, R> {
        ReservoirSampling {
            k: k,
            rng: rng,
            reservoir: vec![],
            i: 0,
            skip_until: 0,
        }
    }

    pub fn k(&self) -> usize {
        self.k
    }

    pub fn reservoir(&self) -> &Vec<T> {
        &self.reservoir
    }

    pub fn i(&self) -> usize {
        self.i
    }

    pub fn add(&mut self, obj: T) {
        let t = self.k * 4; // TODO: make this a parameter

        if self.i < self.k {
            // initial fill-up
            self.reservoir.push(obj)
        } else if self.i < t {
            // normal reservoir sampling
            let j = self.rng.gen_range::<usize>(0, self.i);
            if j < self.k {
                self.reservoir[j] = obj;
            }
        } else if self.i >= self.skip_until {
            // fast skipping approximation
            let j = self.rng.gen_range::<usize>(0, self.k);
            self.reservoir[j] = obj;

            // calculate next skip
            let p = (self.k as f64) / ((self.i + 1) as f64);
            let u = 1. - self.rng.gen_range::<f64>(0., 1.); // (0.0, 1.0]
            let g = (u.ln() / (1. - p).ln()).floor() as usize;
            self.skip_until = self.i + g;
        }

        self.i += 1;
    }

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

impl<T, R> fmt::Debug for ReservoirSampling<T, R>
where
    R: Rng,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ReservoirSampling {{ k: {} }}", self.k)
    }
}

#[cfg(test)]
mod tests {
    use super::ReservoirSampling;
    use rand::ChaChaRng;

    #[test]
    fn getter() {
        let rs = ReservoirSampling::<u64, ChaChaRng>::new(10, ChaChaRng::new_unseeded());
        assert_eq!(rs.k(), 10);
        assert_eq!(rs.i(), 0);
        assert_eq!(rs.reservoir(), &vec![]);
    }

    #[test]
    fn empty() {
        let rs = ReservoirSampling::<u64, ChaChaRng>::new(10, ChaChaRng::new_unseeded());
        assert!(rs.is_empty());
        assert!(rs.reservoir().is_empty());
    }

    #[test]
    fn add_k() {
        let mut rs = ReservoirSampling::<u64, ChaChaRng>::new(10, ChaChaRng::new_unseeded());
        for i in 0..10 {
            rs.add(i);
        }
        assert_eq!(rs.i(), 10);
        assert_eq!(rs.reservoir(), &vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
    }

    #[test]
    fn add_parts() {
        let mut rs = ReservoirSampling::<u64, ChaChaRng>::new(100, ChaChaRng::new_unseeded());
        for _ in 0..1500 {
            rs.add(0);
        }
        for _ in 0..7500 {
            rs.add(1);
        }
        for _ in 0..1000 {
            rs.add(0);
        }
        assert!((rs.reservoir().iter().sum::<u64>() as i64 - 75).abs() < 5);
    }

    #[test]
    fn debug() {
        let rs = ReservoirSampling::<u64, ChaChaRng>::new(10, ChaChaRng::new_unseeded());
        assert_eq!(format!("{:?}", rs), "ReservoirSampling { k: 10 }");
    }
}
<|endoftext|>


<commit_before>//! Generator configurations specific for each Qt module.

use crate::detect_signals_and_slots::detect_signals_and_slots;
use crate::doc_parser::{parse_docs, set_crate_root_doc};
use crate::slot_wrappers::add_signal_slot_wrappers;
use log::info;
use qt_ritual_common::{all_crate_names, get_full_build_config, lib_dependencies, lib_folder_name};
use ritual::config::{Config, CrateDependencyKind, GlobalConfig};
use ritual::config::{CrateDependencySource, CrateProperties};
use ritual_common::cpp_build_config::CppLibraryType;
use ritual_common::cpp_build_config::{CppBuildConfigData, CppBuildPaths};
use ritual_common::errors::{bail, format_err, Result, ResultExt};
use ritual_common::file_utils::repo_dir_path;
use ritual_common::target;
use ritual_common::toml;
use std::path::{Path, PathBuf};

mod _3d;
mod charts;
mod core;
mod gui;
mod qml;
mod ui_tools;
mod widgets;

use self::_3d::{
    core_3d_config, extras_3d_config, input_3d_config, logic_3d_config, render_3d_config,
};
use self::{charts::charts_config, core::core_config, gui::gui_config, widgets::widgets_config};
use crate::lib_configs::qml::qml_config;
use crate::lib_configs::ui_tools::ui_tools_config;
use ritual::cpp_data::{CppItem, CppPath};
use ritual::cpp_type::CppType;
use std::env;

pub const MOQT_INSTALL_DIR_ENV_VAR_NAME: &str = "MOQT_INSTALL_DIR";
pub const MOQT_TEMPLATE_DIR_ENV_VAR_NAME: &str = "MOQT_TEMPLATE_DIR";

#[allow(dead_code)]
fn empty_config(_config: &mut Config) -> Result<()> {
    Ok(())
}

/// Executes the generator for a single Qt module with given configuration.
pub fn create_config(
    mut crate_properties: CrateProperties,
    qmake_path: Option<&str>,
) -> Result<Config> {
    let crate_name = crate_properties.name().to_string();
    info!("Preparing generator config for crate: {}", crate_name);
    let mut custom_fields = toml::value::Table::new();
    let mut package_data = toml::value::Table::new();
    package_data.insert(
        "authors".to_string(),
        toml::Value::Array(vec![toml::Value::String(
            "Pavel Strakhov <ri@idzaaus.org>".to_string(),
        )]),
    );
    let description = format!("Bindings for {} C++ library", lib_folder_name(&crate_name));
    package_data.insert("description".to_string(), toml::Value::String(description));
    let doc_url = format!("https://rust-qt.github.io/rustdoc/qt/{}", &crate_name);
    package_data.insert("documentation".to_string(), toml::Value::String(doc_url));
    package_data.insert(
        "repository".to_string(),
        toml::Value::String("https://github.com/rust-qt/ritual".to_string()),
    );
    package_data.insert(
        "license".to_string(),
        toml::Value::String("MIT OR Apache-2.0".to_string()),
    );
    package_data.insert(
        "keywords".to_string(),
        toml::Value::Array(vec![
            toml::Value::String("gui".to_string()),
            toml::Value::String("ffi".to_string()),
            toml::Value::String("qt".to_string()),
            toml::Value::String("ritual".to_string()),
        ]),
    );
    package_data.insert(
        "categories".to_string(),
        toml::Value::Array(vec![
            toml::Value::String("external-ffi-bindings".to_string()),
            toml::Value::String("gui".to_string()),
        ]),
    );

    custom_fields.insert("package".to_string(), toml::Value::Table(package_data));
    crate_properties.set_custom_fields(custom_fields);

    for &dependency in lib_dependencies(&crate_name)? {
        crate_properties.add_dependency(
            dependency,
            CrateDependencyKind::Ritual,
            CrateDependencySource::CurrentWorkspace,
        )?;
    }

    let mut config = if crate_name.starts_with("moqt_") {
        let mut config = Config::new(crate_properties);
        let moqt_path =
            PathBuf::from(env::var(MOQT_INSTALL_DIR_ENV_VAR_NAME).with_context(|_| {
                format_err!("{} env var is missing", MOQT_INSTALL_DIR_ENV_VAR_NAME)
            })?);

        config.add_include_directive(format!("{}.h", crate_name));
        let include_path = moqt_path.join("include");
        if !include_path.exists() {
            bail!("Path does not exist: {}", include_path.display());
        }
        let lib_path = moqt_path.join("lib");
        if !lib_path.exists() {
            bail!("Path does not exist: {}", lib_path.display());
        }
        let sublib_include_path = include_path.join(&crate_name);
        if !sublib_include_path.exists() {
            bail!("Path does not exist: {}", sublib_include_path.display());
        }
        {
            let mut paths = CppBuildPaths::new();
            paths.add_include_path(&sublib_include_path);
            paths.add_lib_path(&lib_path);

            for &lib in lib_dependencies(&crate_name)? {
                let dep_include_path = include_path.join(lib);
                if !dep_include_path.exists() {
                    bail!("Path does not exist: {}", dep_include_path.display());
                }
                paths.add_include_path(&dep_include_path);
            }
            config.set_cpp_build_paths(paths);
        }
        config.add_target_include_path(&sublib_include_path);

        {
            let mut data = CppBuildConfigData::new();
            data.add_linked_lib(&crate_name);
            for &lib in lib_dependencies(&crate_name)? {
                data.add_linked_lib(lib);
            }
            // TODO: why shared for moqt?
            data.set_library_type(CppLibraryType::Static);
            config
                .cpp_build_config_mut()
                .add(target::Condition::True, data);
        }
        {
            let mut data = CppBuildConfigData::new();
            data.add_compiler_flag("-fPIC");
            data.add_compiler_flag("-std=gnu++11");
            config
                .cpp_build_config_mut()
                .add(target::Condition::Env(target::Env::Msvc).negate(), data);
        }

        let template_path =
            PathBuf::from(env::var(MOQT_TEMPLATE_DIR_ENV_VAR_NAME).with_context(|_| {
                format_err!("{} env var is missing", MOQT_TEMPLATE_DIR_ENV_VAR_NAME)
            })?);
        config.set_crate_template_path(template_path.join(&crate_name));

        let steps = config.processing_steps_mut();
        let crate_name_clone = crate_name.to_string();
        steps.add_after(&["cpp_parser"], "qt_doc_parser", move |data| {
            parse_docs(data, &crate_name_clone, &Path::new("."))
        })?;

        config
    } else {
        crate_properties.remove_default_build_dependencies();
        crate_properties.add_build_dependency(
            "qt_ritual_build",
            CrateDependencySource::Local {
                path: repo_dir_path("qt_ritual_build")?,
            },
        )?;

        let mut config = Config::new(crate_properties);

        let qt_config = get_full_build_config(&crate_name, qmake_path)?;
        config.set_cpp_build_config(qt_config.cpp_build_config);
        config.set_cpp_build_paths(qt_config.cpp_build_paths);

        config.add_target_include_path(&qt_config.installation_data.lib_include_path);
        config.set_cpp_lib_version(qt_config.installation_data.qt_version.as_str());
        // TODO: does parsing work on MacOS without adding "-F"?

        config.add_include_directive(&lib_folder_name(&crate_name));

        // TODO: allow to override parser flags
        if target::current_os() != target::OS::Windows {
            config.add_cpp_parser_argument("-fPIC");
        }
        config.add_cpp_parser_argument("-fcxx-exceptions");

        let steps = config.processing_steps_mut();
        let crate_name_clone = crate_name.to_string();
        let docs_path = qt_config.installation_data.docs_path.clone();

        steps.add_after(&["cpp_parser"], "qt_doc_parser", move |data| {
            parse_docs(data, &crate_name_clone, &docs_path)
        })?;

        config
            .set_crate_template_path(repo_dir_path("qt_ritual/crate_templates")?.join(&crate_name));

        config
    };

    if target::current_env() == target::Env::Msvc {
        config.add_cpp_parser_argument("-std=c++14");
    } else {
        config.add_cpp_parser_argument("-std=gnu++11");
    }

    config.add_after_cpp_parser_hook(detect_signals_and_slots);

    let steps = config.processing_steps_mut();
    for cpp_parser_stage in &["cpp_parser", "cpp_parser_stage2"] {
        steps.add_after(
            &[cpp_parser_stage],
            "add_slot_wrappers",
            add_signal_slot_wrappers,
        )?;
    }

    steps.add_after(
        &["rust_generator"],
        "set_crate_root_doc",
        set_crate_root_doc,
    )?;

    let lib_config = match crate_name.as_str() {
        "qt_core" => core_config,
        "qt_gui" => gui_config,
        "qt_widgets" => widgets_config,
        "qt_3d_core" => core_3d_config,
        "qt_3d_render" => render_3d_config,
        "qt_3d_input" => input_3d_config,
        "qt_3d_logic" => logic_3d_config,
        "qt_3d_extras" => extras_3d_config,
        "qt_ui_tools" => ui_tools_config,
        "qt_charts" => charts_config,
        "qt_qml" => qml_config,
        "moqt_core" => core_config,
        "moqt_gui" => gui_config,
        _ => bail!("Unknown crate name: {}", crate_name),
    };
    lib_config(&mut config)?;

    config.set_cpp_item_filter_hook(cpp_item_hook);

    Ok(config)
}

pub fn global_config() -> GlobalConfig {
    let mut config = GlobalConfig::new();
    config.set_all_crate_names(all_crate_names().iter().map(|&s| s.to_string()).collect());
    config.set_create_config_hook(|crate_name| create_config(crate_name, None));
    config
}

fn cpp_item_hook(item: &CppItem) -> Result<bool> {
    if let CppItem::Function(function) = &item {
        if let Ok(class_type) = function.class_path() {
            let class_text = class_type.to_templateless_string();
            if class_text == "QFlags" {
                return Ok(false);
            }
        }
        if function.is_operator() {
            if let CppType::Class(path) = &function.return_type {
                if path.to_templateless_string() == "QFlags" {
                    return Ok(false);
                }
                if path.to_templateless_string() == "QDebug" && function.arguments.len() == 2 {
                    if let CppType::Class(path2) = &function.arguments[1].argument_type {
                        if path2.to_templateless_string() == "QFlags" {
                            return Ok(false);
                        }
                    }
                }
            }
        }
        let path = function.path.to_templateless_string();
        if path == "QObject::findChild" || path == "QObject::findChildren" {
            if let Some(arg) = function
                .path
                .last()
                .template_arguments
                .as_ref()
                .and_then(|args| args.get(0))
            {
                let qobject_ptr =
                    CppType::new_pointer(false, CppType::Class(CppPath::from_good_str("QObject")));

                if arg != &qobject_ptr && !arg.is_template_parameter() {
                    return Ok(false);
                }
            }
        }
    }
    Ok(true)
}
<commit_msg>Fix Qt crate documentation URLs<commit_after>//! Generator configurations specific for each Qt module.

use crate::detect_signals_and_slots::detect_signals_and_slots;
use crate::doc_parser::{parse_docs, set_crate_root_doc};
use crate::slot_wrappers::add_signal_slot_wrappers;
use log::info;
use qt_ritual_common::{all_crate_names, get_full_build_config, lib_dependencies, lib_folder_name};
use ritual::config::{Config, CrateDependencyKind, GlobalConfig};
use ritual::config::{CrateDependencySource, CrateProperties};
use ritual_common::cpp_build_config::CppLibraryType;
use ritual_common::cpp_build_config::{CppBuildConfigData, CppBuildPaths};
use ritual_common::errors::{bail, format_err, Result, ResultExt};
use ritual_common::file_utils::repo_dir_path;
use ritual_common::target;
use ritual_common::toml;
use std::path::{Path, PathBuf};

mod _3d;
mod charts;
mod core;
mod gui;
mod qml;
mod ui_tools;
mod widgets;

use self::_3d::{
    core_3d_config, extras_3d_config, input_3d_config, logic_3d_config, render_3d_config,
};
use self::{charts::charts_config, core::core_config, gui::gui_config, widgets::widgets_config};
use crate::lib_configs::qml::qml_config;
use crate::lib_configs::ui_tools::ui_tools_config;
use ritual::cpp_data::{CppItem, CppPath};
use ritual::cpp_type::CppType;
use std::env;

pub const MOQT_INSTALL_DIR_ENV_VAR_NAME: &str = "MOQT_INSTALL_DIR";
pub const MOQT_TEMPLATE_DIR_ENV_VAR_NAME: &str = "MOQT_TEMPLATE_DIR";

#[allow(dead_code)]
fn empty_config(_config: &mut Config) -> Result<()> {
    Ok(())
}

/// Executes the generator for a single Qt module with given configuration.
pub fn create_config(
    mut crate_properties: CrateProperties,
    qmake_path: Option<&str>,
) -> Result<Config> {
    let crate_name = crate_properties.name().to_string();
    info!("Preparing generator config for crate: {}", crate_name);
    let mut custom_fields = toml::value::Table::new();
    let mut package_data = toml::value::Table::new();
    package_data.insert(
        "authors".to_string(),
        toml::Value::Array(vec![toml::Value::String(
            "Pavel Strakhov <ri@idzaaus.org>".to_string(),
        )]),
    );
    let description = format!("Bindings for {} C++ library", lib_folder_name(&crate_name));
    package_data.insert("description".to_string(), toml::Value::String(description));
    let doc_url = format!("https://docs.rs/{}", &crate_name);
    package_data.insert("documentation".to_string(), toml::Value::String(doc_url));
    package_data.insert(
        "repository".to_string(),
        toml::Value::String("https://github.com/rust-qt/ritual".to_string()),
    );
    package_data.insert(
        "license".to_string(),
        toml::Value::String("MIT OR Apache-2.0".to_string()),
    );
    package_data.insert(
        "keywords".to_string(),
        toml::Value::Array(vec![
            toml::Value::String("gui".to_string()),
            toml::Value::String("ffi".to_string()),
            toml::Value::String("qt".to_string()),
            toml::Value::String("ritual".to_string()),
        ]),
    );
    package_data.insert(
        "categories".to_string(),
        toml::Value::Array(vec![
            toml::Value::String("external-ffi-bindings".to_string()),
            toml::Value::String("gui".to_string()),
        ]),
    );

    custom_fields.insert("package".to_string(), toml::Value::Table(package_data));
    crate_properties.set_custom_fields(custom_fields);

    for &dependency in lib_dependencies(&crate_name)? {
        crate_properties.add_dependency(
            dependency,
            CrateDependencyKind::Ritual,
            CrateDependencySource::CurrentWorkspace,
        )?;
    }

    let mut config = if crate_name.starts_with("moqt_") {
        let mut config = Config::new(crate_properties);
        let moqt_path =
            PathBuf::from(env::var(MOQT_INSTALL_DIR_ENV_VAR_NAME).with_context(|_| {
                format_err!("{} env var is missing", MOQT_INSTALL_DIR_ENV_VAR_NAME)
            })?);

        config.add_include_directive(format!("{}.h", crate_name));
        let include_path = moqt_path.join("include");
        if !include_path.exists() {
            bail!("Path does not exist: {}", include_path.display());
        }
        let lib_path = moqt_path.join("lib");
        if !lib_path.exists() {
            bail!("Path does not exist: {}", lib_path.display());
        }
        let sublib_include_path = include_path.join(&crate_name);
        if !sublib_include_path.exists() {
            bail!("Path does not exist: {}", sublib_include_path.display());
        }
        {
            let mut paths = CppBuildPaths::new();
            paths.add_include_path(&sublib_include_path);
            paths.add_lib_path(&lib_path);

            for &lib in lib_dependencies(&crate_name)? {
                let dep_include_path = include_path.join(lib);
                if !dep_include_path.exists() {
                    bail!("Path does not exist: {}", dep_include_path.display());
                }
                paths.add_include_path(&dep_include_path);
            }
            config.set_cpp_build_paths(paths);
        }
        config.add_target_include_path(&sublib_include_path);

        {
            let mut data = CppBuildConfigData::new();
            data.add_linked_lib(&crate_name);
            for &lib in lib_dependencies(&crate_name)? {
                data.add_linked_lib(lib);
            }
            // TODO: why shared for moqt?
            data.set_library_type(CppLibraryType::Static);
            config
                .cpp_build_config_mut()
                .add(target::Condition::True, data);
        }
        {
            let mut data = CppBuildConfigData::new();
            data.add_compiler_flag("-fPIC");
            data.add_compiler_flag("-std=gnu++11");
            config
                .cpp_build_config_mut()
                .add(target::Condition::Env(target::Env::Msvc).negate(), data);
        }

        let template_path =
            PathBuf::from(env::var(MOQT_TEMPLATE_DIR_ENV_VAR_NAME).with_context(|_| {
                format_err!("{} env var is missing", MOQT_TEMPLATE_DIR_ENV_VAR_NAME)
            })?);
        config.set_crate_template_path(template_path.join(&crate_name));

        let steps = config.processing_steps_mut();
        let crate_name_clone = crate_name.to_string();
        steps.add_after(&["cpp_parser"], "qt_doc_parser", move |data| {
            parse_docs(data, &crate_name_clone, &Path::new("."))
        })?;

        config
    } else {
        crate_properties.remove_default_build_dependencies();
        crate_properties.add_build_dependency(
            "qt_ritual_build",
            CrateDependencySource::Local {
                path: repo_dir_path("qt_ritual_build")?,
            },
        )?;

        let mut config = Config::new(crate_properties);

        let qt_config = get_full_build_config(&crate_name, qmake_path)?;
        config.set_cpp_build_config(qt_config.cpp_build_config);
        config.set_cpp_build_paths(qt_config.cpp_build_paths);

        config.add_target_include_path(&qt_config.installation_data.lib_include_path);
        config.set_cpp_lib_version(qt_config.installation_data.qt_version.as_str());
        // TODO: does parsing work on MacOS without adding "-F"?

        config.add_include_directive(&lib_folder_name(&crate_name));

        // TODO: allow to override parser flags
        if target::current_os() != target::OS::Windows {
            config.add_cpp_parser_argument("-fPIC");
        }
        config.add_cpp_parser_argument("-fcxx-exceptions");

        let steps = config.processing_steps_mut();
        let crate_name_clone = crate_name.to_string();
        let docs_path = qt_config.installation_data.docs_path.clone();

        steps.add_after(&["cpp_parser"], "qt_doc_parser", move |data| {
            parse_docs(data, &crate_name_clone, &docs_path)
        })?;

        config
            .set_crate_template_path(repo_dir_path("qt_ritual/crate_templates")?.join(&crate_name));

        config
    };

    if target::current_env() == target::Env::Msvc {
        config.add_cpp_parser_argument("-std=c++14");
    } else {
        config.add_cpp_parser_argument("-std=gnu++11");
    }

    config.add_after_cpp_parser_hook(detect_signals_and_slots);

    let steps = config.processing_steps_mut();
    for cpp_parser_stage in &["cpp_parser", "cpp_parser_stage2"] {
        steps.add_after(
            &[cpp_parser_stage],
            "add_slot_wrappers",
            add_signal_slot_wrappers,
        )?;
    }

    steps.add_after(
        &["rust_generator"],
        "set_crate_root_doc",
        set_crate_root_doc,
    )?;

    let lib_config = match crate_name.as_str() {
        "qt_core" => core_config,
        "qt_gui" => gui_config,
        "qt_widgets" => widgets_config,
        "qt_3d_core" => core_3d_config,
        "qt_3d_render" => render_3d_config,
        "qt_3d_input" => input_3d_config,
        "qt_3d_logic" => logic_3d_config,
        "qt_3d_extras" => extras_3d_config,
        "qt_ui_tools" => ui_tools_config,
        "qt_charts" => charts_config,
        "qt_qml" => qml_config,
        "moqt_core" => core_config,
        "moqt_gui" => gui_config,
        _ => bail!("Unknown crate name: {}", crate_name),
    };
    lib_config(&mut config)?;

    config.set_cpp_item_filter_hook(cpp_item_hook);

    Ok(config)
}

pub fn global_config() -> GlobalConfig {
    let mut config = GlobalConfig::new();
    config.set_all_crate_names(all_crate_names().iter().map(|&s| s.to_string()).collect());
    config.set_create_config_hook(|crate_name| create_config(crate_name, None));
    config
}

fn cpp_item_hook(item: &CppItem) -> Result<bool> {
    if let CppItem::Function(function) = &item {
        if let Ok(class_type) = function.class_path() {
            let class_text = class_type.to_templateless_string();
            if class_text == "QFlags" {
                return Ok(false);
            }
        }
        if function.is_operator() {
            if let CppType::Class(path) = &function.return_type {
                if path.to_templateless_string() == "QFlags" {
                    return Ok(false);
                }
                if path.to_templateless_string() == "QDebug" && function.arguments.len() == 2 {
                    if let CppType::Class(path2) = &function.arguments[1].argument_type {
                        if path2.to_templateless_string() == "QFlags" {
                            return Ok(false);
                        }
                    }
                }
            }
        }
        let path = function.path.to_templateless_string();
        if path == "QObject::findChild" || path == "QObject::findChildren" {
            if let Some(arg) = function
                .path
                .last()
                .template_arguments
                .as_ref()
                .and_then(|args| args.get(0))
            {
                let qobject_ptr =
                    CppType::new_pointer(false, CppType::Class(CppPath::from_good_str("QObject")));

                if arg != &qobject_ptr && !arg.is_template_parameter() {
                    return Ok(false);
                }
            }
        }
    }
    Ok(true)
}
<|endoftext|>


<commit_before>extern crate serde_derive;

use std::collections::{BTreeMap, HashSet};
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};

use chrono::offset::TimeZone;
use chrono::{DateTime, Utc};
use clap::{App, Arg, ArgMatches, SubCommand};
use glob::glob;
use serde_derive::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Clone)]
struct RepoConfig {
    path: String,
    #[serde(default = "default_false")]
    commit: bool,
    #[serde(default = "default_false")]
    sync_updates: bool,
}

fn default_false() -> bool {
    false
}

#[derive(Deserialize, Debug)]
struct Supfile {
    repo: RepoConfig,
    editor: Option<String>,
}

impl Supfile {
    pub fn from(str: &str) -> Result<Self, toml::de::Error> {
        toml::from_str(&str)
    }

    pub fn find() -> PathBuf {
        // TODO: pull in XDG crate for paths
        let default_dir = "~/.config/sup/";

        ["./", default_dir]
            .iter()
            .map(|dir| Path::new(dir).join("Supfile"))
            .find(|p| p.exists())
            .unwrap_or_else(|| {
                // TODO: offer to create path somehow
                panic!("can't find supfile")
            })
    }
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum TaskState {
    Todo,
    InProgress,
    Complete,
    Canceled,
    Deferred,
}

impl TaskState {
    fn is_open(self) -> bool {
        match self {
            TaskState::Todo | TaskState::InProgress | TaskState::Deferred => true,
            _ => false,
        }
    }
}

impl std::fmt::Display for TaskState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let value = match self {
            TaskState::Todo => " ",
            TaskState::InProgress => "•",
            TaskState::Complete => "✔",
            TaskState::Canceled => "-",
            TaskState::Deferred => "⟫",
        };
        write!(f, "{}", value)
    }
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum Action {
    Create {
        task: String,
        #[serde(default)]
        notes: Option<String>,
        #[serde(default)]
        tags: Vec<String>,
    },
    EditTags {
        add: Vec<String>,
        remove: Vec<String>,
    },
    EditTask(String),
    EditNotes(Option<String>),
    EditState(TaskState),
}

#[derive(Deserialize, Serialize)]
struct TaskUpdate {
    id: String,
    timestamp: DateTime<Utc>,
    action: Action,
}

impl TaskUpdate {
    fn from_str(line: &str) -> serde_json::Result<Self> {
        serde_json::from_str(line).map_err(|e| {
            println!("nuts: {:?}", e);
            e
        })
    }

    fn new_todo(task: String, notes: Option<String>, tags: Vec<String>) -> Self {
        Self {
            id: "0003".to_string(),
            timestamp: Utc::now(),
            action: Action::Create { task, notes, tags },
        }
    }
}

#[derive(Clone, Debug)]
struct Task {
    id: String,
    created_at: DateTime<Utc>,
    completed_at: Option<DateTime<Utc>>,
    state: TaskState,
    task: String,
    notes: Option<String>,
    tags: HashSet<String>,
}

impl Task {
    fn apply(&mut self, update: &TaskUpdate) {
        match &update.action {
            Action::Create { task, notes, tags } => {
                self.task = task.clone();
                self.notes = notes.clone();
                for tag in tags.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTags { add, remove } => {
                for tag in remove.iter() {
                    self.tags.remove(tag);
                }

                for tag in add.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTask(task) => self.task = task.clone(),
            Action::EditNotes(notes) => self.notes = notes.clone(),
            Action::EditState(state) => {
                self.state = state.clone();
                if state == &TaskState::Complete {
                    self.completed_at = Some(update.timestamp);
                }
            }
        }
    }

    fn summary(&self) -> String {
        format!("{} [{}] {}", self.id, self.state, self.task)
    }
}

impl From<&TaskUpdate> for Task {
    fn from(update: &TaskUpdate) -> Self {
        let mut task = Task {
            id: update.id.clone(),
            created_at: update.timestamp.clone(),
            completed_at: None,
            state: TaskState::Todo,
            task: "untitled".to_string(),
            notes: None,
            tags: HashSet::new(),
        };

        task.apply(update);
        return task;
    }
}

#[derive(Debug)]
struct SupdateLog {
    name: String,
    path: String,

    timestamp: DateTime<Utc>,
}

impl SupdateLog {
    fn new(path: &Path) -> Self {
        let name: String = path
            .file_name()
            .expect("need file, not directory")
            .to_string_lossy()
            .into();

        let timestamp = Utc
            .datetime_from_str(&name, "%Y%m%d_%H%M%S")
            .unwrap_or_else(|_| {
                println!("{:?} appears to be improperly named!", path);
                Utc.timestamp(0, 0)
            });

        // TODO: Support versioning in line
        SupdateLog {
            name,
            path: path.to_string_lossy().into(),
            timestamp,
        }
    }

    fn write_update(&self, task: &TaskUpdate) -> std::io::Result<()> {
        let mut file = self.open_file()?;
        file.seek(SeekFrom::End(0)).expect("could not seek to end");

        serde_json::ser::to_writer(&file, task)?;
        file.write_all(b"\n")?;

        Ok(())
    }

    fn iter(&self) -> SupdateIterator {
        let mut file = self.open_file().expect("could not open file");
        file.seek(SeekFrom::Start(0)).expect("could not rewind");

        SupdateIterator {
            reader: BufReader::new(file),
        }
    }

    /// Open the log file or create it if it doesn't exist.
    fn open_file(&self) -> std::io::Result<File> {
        OpenOptions::new()
            .read(true)
            .append(true)
            .create(true)
            .open(&self.path)
    }
}

struct SupdateIterator {
    reader: BufReader<File>,
}

impl Iterator for SupdateIterator {
    type Item = TaskUpdate;

    fn next(&mut self) -> Option<TaskUpdate> {
        let mut line = String::new();
        return match self.reader.read_line(&mut line) {
            // End of file
            Ok(0) => None,

            Ok(_) => {
                //println!("line = {:?}", line);
                // TODO: alert when we don't succeed at parsing
                TaskUpdate::from_str(&line).ok()
            }

            Err(e) => {
                println!("Failed to read line: {:?}", e);
                None
            }
        };
    }
}

/**
 * A Repository represents a directory containing SupdateLogs.
 *
 * The directory structure is something like this:
 *
 * repo/
 *   2019/
 *     08/
 *       20190801_235959.sup
 *       20190802_003030.sup
 *       20190802_123030.sup
 *
 * TODO: Support concept of `rollup` files to put an upper limit on
 * how far we need to look back to fully reconstruct the state of
 * everything.
 */

struct Repository {
    config: RepoConfig,
}

impl Repository {
    fn new(cfg: RepoConfig) -> Self {
        // TODO: Handle error, return Result
        if !Path::new(&cfg.path).exists() {
            panic!("given repository does not exist")
        }

        Repository { config: cfg }
    }

    fn open_journal(&self) -> Journal {
        let updates = self.list_update_logs();
        self.build_journal(updates)
    }

    fn build_journal(&self, updates: Vec<SupdateLog>) -> Journal {
        let mut journal = Journal::new();

        // Reverse ordering so that we consume oldest elements first.
        for log in updates.into_iter().rev() {
            println!("reading: {:?}", log);

            for ref update in log.iter() {
                journal.add(update);
            }
        }

        journal
    }

    fn list_update_logs(&self) -> Vec<SupdateLog> {
        let path = Path::new(&self.config.path).join("**/*.sup");

        // TODO: This is really wasteful. Make this an iterator
        // Get list of all files in the repo
        let mut updates = glob(path.to_str().unwrap())
            .expect("glob failed")
            .filter_map(|f| f.map(|p| SupdateLog::new(&p)).ok())
            .collect::<Vec<SupdateLog>>();

        // We want sorted (chronological, desc) order, based on file name.
        updates.sort_by(|a, b| b.name.cmp(&a.name));
        return updates;
    }

    fn active_update_log(&self) -> SupdateLog {
        let now = Utc::now();

        let last_update = self.list_update_logs().pop();

        // Use the last update if it's still the same day, otherwise create a new one.
        last_update
            .filter(|log| log.timestamp.date() == now.date())
            .unwrap_or_else(|| {
                let log_path = now.format("%Y/%m/%Y%m%d_%H%M%S.sup").to_string();
                let abs_path = Path::new(&self.config.path).join(log_path);

                let dir = abs_path.parent().unwrap();

                std::fs::create_dir_all(dir).expect("create directory failed");

                SupdateLog::new(&abs_path)
            })
    }
}

/**
 * A collection of Updates.
 *
 * Represents the cumulative, "rolled up" set of task updates.
 */
#[derive(Debug)]
struct Journal {
    /// id -> task
    entries: BTreeMap<String, Task>,
}

impl Journal {
    fn new() -> Self {
        return Self {
            entries: BTreeMap::new(),
        };
    }

    fn add(&mut self, update: &TaskUpdate) {
        let task = self.entries.get_mut(&update.id);

        match task {
            Some(t) => t.apply(update),
            None => {
                self.entries.insert(update.id.clone(), Task::from(update));
            }
        }
    }

    // TODO: Hack until a custom iterator is implemented.
    fn for_each(&self, f: &Fn(&Task) -> ()) {
        self.entries.values().for_each(f)
    }
}

struct SupCli {
    matches: ArgMatches<'static>,
}

impl SupCli {
    fn new() -> Self {
        Self {
            matches: Self::parse_args(),
        }
    }

    fn parse_args<'a>() -> ArgMatches<'a> {
        App::new("sup")
            .version("0.0.0")
            .about("Keep track of what's up.")
            .arg(
                Arg::with_name("config")
                    .short("c")
                    .long("config")
                    .value_name("FILE")
                    .takes_value(true)
                    .help("Set custom location for Supfile"),
            )
            .subcommand(
                SubCommand::with_name("todo")
                    .about("create a new todo item")
                    .arg(Arg::with_name("TASK").takes_value(true).multiple(true)),
            )
            .subcommand(
                SubCommand::with_name("show")
                    .about("asdf")
                    .arg(Arg::with_name("DATE").takes_value(true).help("asdf")),
            )
            .subcommand(
                SubCommand::with_name("serve")
                    .about("launch web interface")
                    .arg(
                        Arg::with_name("port")
                            .short("p")
                            .long("port")
                            .takes_value(true),
                    ),
            )
            .get_matches()
    }

    fn load_config(&self) -> Result<Supfile, Box<dyn Error>> {
        let path = self
            .matches
            .value_of("config")
            .map(|p| Path::new(p).to_path_buf())
            .unwrap_or_else(Supfile::find);

        let bytes = std::fs::read_to_string(path.as_os_str())?;
        Ok(Supfile::from(&bytes)?)
    }

    fn run(&self) -> Result<(), Box<dyn Error>> {
        let cfg = self.load_config().expect("error in config file");
        let app = SupApp::new(&cfg);

        match self.matches.subcommand() {
            ("show", Some(m)) => println!("TODO: show {:?}", m),
            ("serve", Some(m)) => println!("TODO: serve {:?}", m),
            ("todo", Some(m)) => {
                let task = m.values_of("TASK").map(|v| v.collect::<Vec<_>>().join(" "));
                app.run_add_task(task)?;
            }
            _ => {
                println!("add update");
                app.run_show_updates()?;
            }
        };

        Ok(())
    }
}

struct SupApp {
    repo: Repository,
    journal: Journal,
}

impl SupApp {
    fn new(cfg: &Supfile) -> Self {
        let repo = Repository::new(cfg.repo.clone());
        let journal = repo.open_journal();

        Self { repo, journal }
    }

    fn run_add_task(&self, task: Option<String>) -> Result<(), Box<dyn Error>> {
        println!("journal: {:?}: {:?}", self.journal, task);
        let task = task.expect("TODO: ui task creation");

        let update = TaskUpdate::new_todo(task, None, vec![]);
        self.repo.active_update_log().write_update(&update)?;

        Ok(())
    }

    fn run_show_updates(&self) -> Result<(), Box<dyn Error>> {
        self.journal.for_each(&|task| {
            println!("{}", task.summary());
        });

        Ok(())
    }
}

// TODO: Investiage this instead of Box<Error>
// https://github.com/rust-lang-nursery/failure
fn main() {
    let app = SupCli::new();
    match app.run() {
        Err(e) => println!("oh no! {:?}", e),
        Ok(_) => (),
    };
}
<commit_msg>Assign incrementing ids<commit_after>extern crate serde_derive;

use std::collections::{BTreeMap, HashSet};
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};

use chrono::offset::TimeZone;
use chrono::{DateTime, Utc};
use clap::{App, Arg, ArgMatches, SubCommand};
use glob::glob;
use serde_derive::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Clone)]
struct RepoConfig {
    path: String,
    #[serde(default = "default_false")]
    commit: bool,
    #[serde(default = "default_false")]
    sync_updates: bool,
}

fn default_false() -> bool {
    false
}

#[derive(Deserialize, Debug)]
struct Supfile {
    repo: RepoConfig,
    editor: Option<String>,
}

impl Supfile {
    pub fn from(str: &str) -> Result<Self, toml::de::Error> {
        toml::from_str(&str)
    }

    pub fn find() -> PathBuf {
        // TODO: pull in XDG crate for paths
        let default_dir = "~/.config/sup/";

        ["./", default_dir]
            .iter()
            .map(|dir| Path::new(dir).join("Supfile"))
            .find(|p| p.exists())
            .unwrap_or_else(|| {
                // TODO: offer to create path somehow
                panic!("can't find supfile")
            })
    }
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum TaskState {
    Todo,
    InProgress,
    Complete,
    Canceled,
    Deferred,
}

impl TaskState {
    fn is_open(self) -> bool {
        match self {
            TaskState::Todo | TaskState::InProgress | TaskState::Deferred => true,
            _ => false,
        }
    }
}

impl std::fmt::Display for TaskState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let value = match self {
            TaskState::Todo => " ",
            TaskState::InProgress => "•",
            TaskState::Complete => "✔",
            TaskState::Canceled => "-",
            TaskState::Deferred => "⟫",
        };
        write!(f, "{}", value)
    }
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum Action {
    Create {
        task: String,
        #[serde(default)]
        notes: Option<String>,
        #[serde(default)]
        tags: Vec<String>,
    },
    EditTags {
        add: Vec<String>,
        remove: Vec<String>,
    },
    EditTask(String),
    EditNotes(Option<String>),
    EditState(TaskState),
}

#[derive(Deserialize, Serialize)]
struct TaskUpdate {
    id: String,
    timestamp: DateTime<Utc>,
    action: Action,
}

impl TaskUpdate {
    fn from_str(line: &str) -> serde_json::Result<Self> {
        serde_json::from_str(line).map_err(|e| {
            println!("nuts: {:?}", e);
            e
        })
    }

    /// Create a basic TaskUpdate. The `id` is left unset and must be set before being written.
    fn new_todo(
        id: String,
        task: String,
        notes: Option<String>,
        tags: Vec<String>,
    ) -> Self {
        Self {
            id: id,
            timestamp: Utc::now(),
            action: Action::Create { task, notes, tags },
        }
    }
}

#[derive(Clone, Debug)]
struct Task {
    id: String,
    created_at: DateTime<Utc>,
    completed_at: Option<DateTime<Utc>>,
    state: TaskState,
    task: String,
    notes: Option<String>,
    tags: HashSet<String>,
}

impl Task {
    fn apply(&mut self, update: &TaskUpdate) {
        match &update.action {
            Action::Create { task, notes, tags } => {
                self.task = task.clone();
                self.notes = notes.clone();
                for tag in tags.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTags { add, remove } => {
                for tag in remove.iter() {
                    self.tags.remove(tag);
                }

                for tag in add.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTask(task) => self.task = task.clone(),
            Action::EditNotes(notes) => self.notes = notes.clone(),
            Action::EditState(state) => {
                self.state = state.clone();
                if state == &TaskState::Complete {
                    self.completed_at = Some(update.timestamp);
                }
            }
        }
    }

    fn summary(&self) -> String {
        format!("{} [{}] {}", self.id, self.state, self.task)
    }
}

impl From<&TaskUpdate> for Task {
    fn from(update: &TaskUpdate) -> Self {
        let mut task = Task {
            id: update.id.clone(),
            created_at: update.timestamp.clone(),
            completed_at: None,
            state: TaskState::Todo,
            task: "untitled".to_string(),
            notes: None,
            tags: HashSet::new(),
        };

        task.apply(update);
        return task;
    }
}

#[derive(Debug)]
struct SupdateLog {
    name: String,
    path: String,

    timestamp: DateTime<Utc>,
}

impl SupdateLog {
    fn new(path: &Path) -> Self {
        let name: String = path
            .file_name()
            .expect("need file, not directory")
            .to_string_lossy()
            .into();

        let timestamp = Utc
            .datetime_from_str(&name, "%Y%m%d_%H%M%S.sup")
            .unwrap_or_else(|_| {
                println!("{:?} appears to be improperly named!", path);
                Utc.timestamp(0, 0)
            });

        // TODO: Support versioning in line
        SupdateLog {
            name,
            path: path.to_string_lossy().into(),
            timestamp,
        }
    }

    fn write_update(&self, task: &TaskUpdate) -> std::io::Result<()> {
        let mut file = self.open_file()?;
        file.seek(SeekFrom::End(0)).expect("could not seek to end");

        serde_json::ser::to_writer(&file, task)?;
        file.write_all(b"\n")?;

        Ok(())
    }

    fn iter(&self) -> SupdateIterator {
        let mut file = self.open_file().expect("could not open file");
        file.seek(SeekFrom::Start(0)).expect("could not rewind");

        SupdateIterator {
            reader: BufReader::new(file),
        }
    }

    /// Open the log file or create it if it doesn't exist.
    fn open_file(&self) -> std::io::Result<File> {
        OpenOptions::new()
            .read(true)
            .append(true)
            .create(true)
            .open(&self.path)
    }
}

struct SupdateIterator {
    reader: BufReader<File>,
}

impl Iterator for SupdateIterator {
    type Item = TaskUpdate;

    fn next(&mut self) -> Option<TaskUpdate> {
        let mut line = String::new();
        return match self.reader.read_line(&mut line) {
            // End of file
            Ok(0) => None,

            Ok(_) => {
                //println!("line = {:?}", line);
                // TODO: alert when we don't succeed at parsing
                TaskUpdate::from_str(&line).ok()
            }

            Err(e) => {
                println!("Failed to read line: {:?}", e);
                None
            }
        };
    }
}

/**
 * A Repository represents a directory containing SupdateLogs.
 *
 * The directory structure is something like this:
 *
 * repo/
 *   2019/
 *     08/
 *       20190801_235959.sup
 *       20190802_003030.sup
 *       20190802_123030.sup
 *
 * TODO: Support concept of `rollup` files to put an upper limit on
 * how far we need to look back to fully reconstruct the state of
 * everything.
 */

struct Repository {
    config: RepoConfig,
}

impl Repository {
    fn new(cfg: RepoConfig) -> Self {
        // TODO: Handle error, return Result
        if !Path::new(&cfg.path).exists() {
            panic!("given repository does not exist")
        }

        Repository { config: cfg }
    }

    fn open_journal(&self) -> Journal {
        let updates = self.list_update_logs();
        self.build_journal(updates)
    }

    fn build_journal(&self, updates: Vec<SupdateLog>) -> Journal {
        let mut journal = Journal::new();

        // Reverse ordering so that we consume oldest elements first.
        for log in updates.into_iter().rev() {
            println!("reading: {:?}", log);

            for ref update in log.iter() {
                journal.add(update);
            }
        }

        journal
    }

    fn list_update_logs(&self) -> Vec<SupdateLog> {
        let path = Path::new(&self.config.path).join("**/*.sup");

        // TODO: This is really wasteful. Make this an iterator
        // Get list of all files in the repo
        let mut updates = glob(path.to_str().unwrap())
            .expect("glob failed")
            .filter_map(|f| f.map(|p| SupdateLog::new(&p)).ok())
            .collect::<Vec<SupdateLog>>();

        // We want sorted (chronological, desc) order, based on file name.
        updates.sort_by(|a, b| b.name.cmp(&a.name));
        return updates;
    }

    fn active_update_log(&self) -> SupdateLog {
        let now = Utc::now();

        let last_update = self.list_update_logs().pop();

        // Use the last update if it's still the same day, otherwise create a new one.
        last_update
            .filter(|log| log.timestamp.date() == now.date())
            .unwrap_or_else(|| {
                let log_path = now.format("%Y/%m/%Y%m%d_%H%M%S.sup").to_string();
                let abs_path = Path::new(&self.config.path).join(log_path);

                let dir = abs_path.parent().unwrap();

                std::fs::create_dir_all(dir).expect("create directory failed");

                SupdateLog::new(&abs_path)
            })
    }
}

/**
 * A collection of Updates.
 *
 * Represents the cumulative, "rolled up" set of task updates.
 */
#[derive(Debug)]
struct Journal {
    /// id -> task
    entries: BTreeMap<String, Task>,
    last_id: u64,
}

impl Journal {
    fn new() -> Self {
        return Self {
            entries: BTreeMap::new(),
            last_id: 0,
        };
    }

    fn add(&mut self, update: &TaskUpdate) {
        let id = &update.id;

        // Keep track of higest id value we've seen.
        let id_int = id.parse::<u64>().unwrap();
        self.last_id = std::cmp::max(self.last_id, id_int);

        let task = self.entries.get_mut(id);

        match task {
            Some(t) => t.apply(update),
            None => {
                self.entries.insert(id.clone(), Task::from(update));
            }
        }
    }

    fn next_id(&mut self) -> String {
        self.last_id += 1;

        format!("{:04}", self.last_id)
    }

    // TODO: Hack until a custom iterator is implemented.
    fn for_each(&self, f: &Fn(&Task) -> ()) {
        self.entries.values().for_each(f)
    }
}

struct SupCli {
    matches: ArgMatches<'static>,
}

impl SupCli {
    fn new() -> Self {
        Self {
            matches: Self::parse_args(),
        }
    }

    fn parse_args<'a>() -> ArgMatches<'a> {
        App::new("sup")
            .version("0.0.0")
            .about("Keep track of what's up.")
            .arg(
                Arg::with_name("config")
                    .short("c")
                    .long("config")
                    .value_name("FILE")
                    .takes_value(true)
                    .help("Set custom location for Supfile"),
            )
            .subcommand(
                SubCommand::with_name("todo")
                    .about("create a new todo item")
                    .arg(Arg::with_name("TASK").takes_value(true).multiple(true)),
            )
            .subcommand(
                SubCommand::with_name("show")
                    .about("asdf")
                    .arg(Arg::with_name("DATE").takes_value(true).help("asdf")),
            )
            .subcommand(
                SubCommand::with_name("serve")
                    .about("launch web interface")
                    .arg(
                        Arg::with_name("port")
                            .short("p")
                            .long("port")
                            .takes_value(true),
                    ),
            )
            .get_matches()
    }

    fn load_config(&self) -> Result<Supfile, Box<dyn Error>> {
        let path = self
            .matches
            .value_of("config")
            .map(|p| Path::new(p).to_path_buf())
            .unwrap_or_else(Supfile::find);

        let bytes = std::fs::read_to_string(path.as_os_str())?;
        Ok(Supfile::from(&bytes)?)
    }

    fn run(&self) -> Result<(), Box<dyn Error>> {
        let cfg = self.load_config().expect("error in config file");
        let mut app = SupApp::new(&cfg);

        match self.matches.subcommand() {
            ("show", Some(m)) => println!("TODO: show {:?}", m),
            ("serve", Some(m)) => println!("TODO: serve {:?}", m),
            ("todo", Some(m)) => {
                let task = m.values_of("TASK").map(|v| {
                    let vec = v.collect::<Vec<_>>();
                    vec.join(" ")
                });

                app.run_add_task(task)?;
            }

            _ => app.run_show_updates()?,
        };

        Ok(())
    }
}

struct SupApp {
    repo: Repository,
    journal: Journal,
}

impl SupApp {
    fn new(cfg: &Supfile) -> Self {
        let repo = Repository::new(cfg.repo.clone());
        let journal = repo.open_journal();

        Self { repo, journal }
    }

    fn run_add_task(&mut self, task: Option<String>) -> Result<(), Box<dyn Error>> {
        println!("journal: {:?}: {:?}", self.journal, task);
        let task = task.expect("TODO: ui task creation");
        let id = self.journal.next_id();

        let update = TaskUpdate::new_todo(id, task, None, vec![]);
        self.repo.active_update_log().write_update(&update)?;

        Ok(())
    }

    fn run_show_updates(&self) -> Result<(), Box<dyn Error>> {
        self.journal.for_each(&|task| {
            println!("{}", task.summary());
        });

        Ok(())
    }
}

// TODO: Investiage this instead of Box<Error>
// https://github.com/rust-lang-nursery/failure
fn main() {
    let app = SupCli::new();
    match app.run() {
        Err(e) => println!("oh no! {:?}", e),
        Ok(_) => (),
    };
}
<|endoftext|>


<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

use clap::Clap;
use log::{debug, info, LevelFilter};
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::str::FromStr;
use std::time::{Duration, Instant};
use tokio::net::TcpListener;
use tonic::transport::Server;

use rustboard_core::commit::Commit;
use rustboard_core::logdir::LogdirLoader;
use rustboard_core::proto::tensorboard::data;
use rustboard_core::server::DataProviderHandler;

use data::tensor_board_data_provider_server::TensorBoardDataProviderServer;

#[derive(Clap, Debug)]
#[clap(name = "rustboard", version = "0.1.0")]
struct Opts {
    /// Log directory to load
    ///
    /// Directory to recursively scan for event files (files matching the `*tfevents*` glob). This
    /// directory, its descendants, and its event files will be periodically polled for new data.
    #[clap(long)]
    logdir: PathBuf,

    /// Bind to this IP address
    ///
    /// IP address to bind this server to. May be an IPv4 address (e.g., 127.0.0.1 or 0.0.0.0) or
    /// an IPv6 address (e.g., ::1 or ::0).
    #[clap(long, default_value = "::0")]
    host: IpAddr,

    /// Bind to this port
    ///
    /// Port to bind this server to. Use `0` to request an arbitrary free port from the OS.
    #[clap(long, default_value = "6806")]
    port: u16,

    /// Delay between reload cycles (seconds)
    ///
    /// Number of seconds to wait between finishing one load cycle and starting the next one. This
    /// does not include the time for the reload itself.
    #[clap(long, default_value = "5")]
    reload_interval: Seconds,
}

/// A duration in seconds.
#[derive(Debug, Copy, Clone)]
struct Seconds(u64);
impl FromStr for Seconds {
    type Err = <u64 as FromStr>::Err;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        s.parse().map(Seconds)
    }
}
impl Seconds {
    fn duration(self) -> Duration {
        Duration::from_secs(self.0)
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opts = Opts::parse();
    init_logging(LevelFilter::Info);
    debug!("Parsed options: {:?}", opts);

    let addr = SocketAddr::new(opts.host, opts.port);
    let listener = TcpListener::bind(addr).await?;
    let bound = listener.local_addr()?;
    eprintln!("listening on {:?}", bound);

    // Leak the commit object, since the Tonic server must have only 'static references. This only
    // leaks the outer commit structure (of constant size), not the pointers to the actual data.
    let commit: &'static Commit = Box::leak(Box::new(Commit::new()));

    std::thread::spawn(move || {
        let mut loader = LogdirLoader::new(commit, opts.logdir);
        loop {
            info!("Starting load cycle");
            let start = Instant::now();
            loader.reload();
            let end = Instant::now();
            info!("Finished load cycle ({:?})", end - start);
            std::thread::sleep(opts.reload_interval.duration());
        }
    });

    let handler = DataProviderHandler { commit };
    Server::builder()
        .add_service(TensorBoardDataProviderServer::new(handler))
        .serve_with_incoming(listener)
        .await?;
    Ok(())
}

/// Installs a logging handler whose behavior is determined by the `RUST_LOG` environment variable
/// (per <https://docs.rs/env_logger> semantics), or by including all logs at `default_log_level`
/// or above if `RUST_LOG_LEVEL` is not given.
fn init_logging(default_log_level: LevelFilter) {
    use env_logger::{Builder, Env};
    Builder::from_env(Env::default().default_filter_or(default_log_level.to_string())).init();
}
<commit_msg>rust: add `--verbose` to control log level (#4398)<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

use clap::Clap;
use log::{debug, info, LevelFilter};
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::str::FromStr;
use std::time::{Duration, Instant};
use tokio::net::TcpListener;
use tonic::transport::Server;

use rustboard_core::commit::Commit;
use rustboard_core::logdir::LogdirLoader;
use rustboard_core::proto::tensorboard::data;
use rustboard_core::server::DataProviderHandler;

use data::tensor_board_data_provider_server::TensorBoardDataProviderServer;

#[derive(Clap, Debug)]
#[clap(name = "rustboard", version = "0.1.0")]
struct Opts {
    /// Log directory to load
    ///
    /// Directory to recursively scan for event files (files matching the `*tfevents*` glob). This
    /// directory, its descendants, and its event files will be periodically polled for new data.
    #[clap(long)]
    logdir: PathBuf,

    /// Bind to this IP address
    ///
    /// IP address to bind this server to. May be an IPv4 address (e.g., 127.0.0.1 or 0.0.0.0) or
    /// an IPv6 address (e.g., ::1 or ::0).
    #[clap(long, default_value = "::0")]
    host: IpAddr,

    /// Bind to this port
    ///
    /// Port to bind this server to. Use `0` to request an arbitrary free port from the OS.
    #[clap(long, default_value = "6806")]
    port: u16,

    /// Delay between reload cycles (seconds)
    ///
    /// Number of seconds to wait between finishing one load cycle and starting the next one. This
    /// does not include the time for the reload itself.
    #[clap(long, default_value = "5")]
    reload_interval: Seconds,

    /// Use verbose output (-vv for very verbose output)
    #[clap(long = "verbose", short, parse(from_occurrences))]
    verbosity: u32,
}

/// A duration in seconds.
#[derive(Debug, Copy, Clone)]
struct Seconds(u64);
impl FromStr for Seconds {
    type Err = <u64 as FromStr>::Err;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        s.parse().map(Seconds)
    }
}
impl Seconds {
    fn duration(self) -> Duration {
        Duration::from_secs(self.0)
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opts = Opts::parse();
    init_logging(match opts.verbosity {
        0 => LevelFilter::Warn,
        1 => LevelFilter::Info,
        _ => LevelFilter::max(),
    });
    debug!("Parsed options: {:?}", opts);

    let addr = SocketAddr::new(opts.host, opts.port);
    let listener = TcpListener::bind(addr).await?;
    let bound = listener.local_addr()?;
    eprintln!("listening on {:?}", bound);

    // Leak the commit object, since the Tonic server must have only 'static references. This only
    // leaks the outer commit structure (of constant size), not the pointers to the actual data.
    let commit: &'static Commit = Box::leak(Box::new(Commit::new()));

    std::thread::spawn(move || {
        let mut loader = LogdirLoader::new(commit, opts.logdir);
        loop {
            info!("Starting load cycle");
            let start = Instant::now();
            loader.reload();
            let end = Instant::now();
            info!("Finished load cycle ({:?})", end - start);
            std::thread::sleep(opts.reload_interval.duration());
        }
    });

    let handler = DataProviderHandler { commit };
    Server::builder()
        .add_service(TensorBoardDataProviderServer::new(handler))
        .serve_with_incoming(listener)
        .await?;
    Ok(())
}

/// Installs a logging handler whose behavior is determined by the `RUST_LOG` environment variable
/// (per <https://docs.rs/env_logger> semantics), or by including all logs at `default_log_level`
/// or above if `RUST_LOG_LEVEL` is not given.
fn init_logging(default_log_level: LevelFilter) {
    use env_logger::{Builder, Env};
    Builder::from_env(Env::default().default_filter_or(default_log_level.to_string())).init();
}
<|endoftext|>