<commit_before>use game::constants::{RESOLUTION_X, RESOLUTION_Y};
use gfx;
use gfx_device_gl;
use gfx_window_glutin;
use glutin;
use glutin::GlContext;

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct GlutinWindow {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl GlutinWindow {
  pub fn new() -> GlutinWindow {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Hinterland");

    let builder = if cfg!(feature = "windowed") {
      window_title.with_dimensions(RESOLUTION_X, RESOLUTION_Y)
    } else {
      let monitor = {
        events_loop.get_available_monitors().nth(0).expect("Please enter a valid ID")
      };
      window_title.with_fullscreen(Some(monitor))
                  .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
    };

    let context = glutin::ContextBuilder::new()
      .with_vsync(true)
      .with_pixel_format(24, 8)
      .with_gl(glutin::GlRequest::GlThenGles {
        opengles_version: (3, 0),
        opengl_version: (4, 1),
      });

    let (window, device, factory, rtv, dsv) = gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop);

    GlutinWindow {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0),
    }
  }
}

#[derive(Debug, PartialEq, Eq)]
pub enum WindowStatus {
  Open,
  Close,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);
  fn get_viewport_size(&mut self) -> (f32, f32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_hidpi_factor(&mut self) -> f32;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
  fn poll_events(&mut self) -> WindowStatus;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for GlutinWindow {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
        .swap_buffers()
        .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (f32, f32) {
    let viewport = self.window
        .get_inner_size()
        .unwrap_or((RESOLUTION_X, RESOLUTION_Y));
    ((viewport.0 as f32), (viewport.1 as f32))
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_hidpi_factor(&mut self) -> f32 {
    self.window.hidpi_factor()
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }
  fn poll_events(&mut self) -> WindowStatus {
    use glutin::KeyboardInput;
    use glutin::MouseButton;
    use glutin::WindowEvent::{Resized, CloseRequested, CursorMoved, MouseInput};
    use glutin::ElementState::{Pressed, Released};
    use glutin::VirtualKeyCode::{Escape, Z, X, W, A, S, D};

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };
    let window = &self.window;

    let m_rtv = &mut self.render_target_view;
    let m_dsv = &mut self.depth_stencil_view;
    let m_pos = &mut self.mouse_pos;

    let mut game_status = WindowStatus::Open;

    self.events_loop.poll_events(|event| {
      game_status = if let glutin::Event::WindowEvent { event, .. } = event {
        match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => match input {
            KeyboardInput { virtual_keycode: Some(Escape), .. } => WindowStatus::Close,
            KeyboardInput { state: Pressed, virtual_keycode: Some(Z), .. } => {
              controls.zoom_out();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(X), .. } => {
              controls.zoom_in();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(Z), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(X), .. } => {
              controls.zoom_stop();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {
              controls.move_character_up();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {
              controls.move_character_down();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {
              controls.stop_character_y();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {
              controls.move_character_left();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {
              controls.move_character_right();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {
              controls.stop_character_x();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, modifiers, .. } => {
              if modifiers.ctrl {
                controls.ctrl_pressed();
              }
              WindowStatus::Open
            }
            KeyboardInput { state: Released, modifiers, .. } => {
              if !modifiers.ctrl {
                controls.ctrl_released();
              }
              WindowStatus::Open
            }
          },
          MouseInput { state: Pressed, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(None);
            WindowStatus::Open
          }
          MouseInput { state: Pressed, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(None);
            WindowStatus::Open
          }
          CursorMoved { position, .. } => {
            *m_pos = position;
            WindowStatus::Open
          }
          CloseRequested => WindowStatus::Close,
          Resized(w, h) => {
            window.resize(w, h);
            gfx_window_glutin::update_views(&window, m_rtv, m_dsv);
            WindowStatus::Open
          }
          _ => WindowStatus::Open,
        }
      } else {
        WindowStatus::Open
      };
    });
    game_status
  }
}

<commit_msg>Return hard coded window resolution - get_inner_size doesn't seem to work on all screen configurations<commit_after>use game::constants::{RESOLUTION_X, RESOLUTION_Y};
use gfx;
use gfx_device_gl;
use gfx_window_glutin;
use glutin;
use glutin::GlContext;

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct GlutinWindow {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl GlutinWindow {
  pub fn new() -> GlutinWindow {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Hinterland");

    let builder = if cfg!(feature = "windowed") {
      window_title.with_dimensions(RESOLUTION_X, RESOLUTION_Y)
    } else {
      let monitor = {
        events_loop.get_available_monitors().nth(0).expect("Please enter a valid ID")
      };
      window_title.with_fullscreen(Some(monitor))
                  .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
    };

    let context = glutin::ContextBuilder::new()
      .with_vsync(true)
      .with_pixel_format(24, 8)
      .with_gl(glutin::GlRequest::GlThenGles {
        opengles_version: (3, 0),
        opengl_version: (4, 1),
      });

    let (window, device, factory, rtv, dsv) = gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop);

    GlutinWindow {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0),
    }
  }
}

#[derive(Debug, PartialEq, Eq)]
pub enum WindowStatus {
  Open,
  Close,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);
  fn get_viewport_size(&mut self) -> (f32, f32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_hidpi_factor(&mut self) -> f32;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
  fn poll_events(&mut self) -> WindowStatus;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for GlutinWindow {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
        .swap_buffers()
        .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (f32, f32) {
    (RESOLUTION_X as f32, RESOLUTION_Y as f32)
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_hidpi_factor(&mut self) -> f32 {
    self.window.hidpi_factor()
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }
  fn poll_events(&mut self) -> WindowStatus {
    use glutin::KeyboardInput;
    use glutin::MouseButton;
    use glutin::WindowEvent::{Resized, CloseRequested, CursorMoved, MouseInput};
    use glutin::ElementState::{Pressed, Released};
    use glutin::VirtualKeyCode::{Escape, Z, X, W, A, S, D};

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };
    let window = &self.window;

    let m_rtv = &mut self.render_target_view;
    let m_dsv = &mut self.depth_stencil_view;
    let m_pos = &mut self.mouse_pos;

    let mut game_status = WindowStatus::Open;

    self.events_loop.poll_events(|event| {
      game_status = if let glutin::Event::WindowEvent { event, .. } = event {
        match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => match input {
            KeyboardInput { virtual_keycode: Some(Escape), .. } => WindowStatus::Close,
            KeyboardInput { state: Pressed, virtual_keycode: Some(Z), .. } => {
              controls.zoom_out();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(X), .. } => {
              controls.zoom_in();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(Z), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(X), .. } => {
              controls.zoom_stop();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {
              controls.move_character_up();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {
              controls.move_character_down();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {
              controls.stop_character_y();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {
              controls.move_character_left();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {
              controls.move_character_right();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {
              controls.stop_character_x();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, modifiers, .. } => {
              if modifiers.ctrl {
                controls.ctrl_pressed();
              }
              WindowStatus::Open
            }
            KeyboardInput { state: Released, modifiers, .. } => {
              if !modifiers.ctrl {
                controls.ctrl_released();
              }
              WindowStatus::Open
            }
          },
          MouseInput { state: Pressed, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(None);
            WindowStatus::Open
          }
          MouseInput { state: Pressed, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(None);
            WindowStatus::Open
          }
          CursorMoved { position, .. } => {
            *m_pos = position;
            WindowStatus::Open
          }
          CloseRequested => WindowStatus::Close,
          Resized(w, h) => {
            window.resize(w, h);
            gfx_window_glutin::update_views(&window, m_rtv, m_dsv);
            WindowStatus::Open
          }
          _ => WindowStatus::Open,
        }
      } else {
        WindowStatus::Open
      };
    });
    game_status
  }
}

<|endoftext|>


<commit_before>// use std::io::Read;
use gltf;
// use gltf::root::Root;
// use gltf::accessor::{ Accessor };
// use gltf::buffer::{ Target };
use gltf::mesh::{ /*Mode, */Primitive };

pub fn load_file(path: &str) {
    let mut importer = gltf::Importer::new();
    let root = importer.import_from_path(path);
    match root {
        Ok(root) => {
            println!("glTF version 2.0");
            println!("{:#?}", root);
            // load_box(&root);
            // load_box_2(&root);
        }
        Err(err) => {
            println!("Error: {:#?}", err);
        }
    }
}

// struct PrimitiveData<'a> {
//     accessor: &'a Accessor<'a>,
//     buffer_view: &'a BufferView,
//     data: &'a [u8],
// }

// pub fn load_box(root: &Root) {
//     let buffer = &root.buffers()[0];

//     // TODO!: determine base directory...
//     let mut file = std::fs::File::open(format!("src/data/{}", buffer.uri)).unwrap();
//     let mut buffer_contents = Vec::with_capacity(buffer.byte_length as usize);
//     file.read_to_end(&mut buffer_contents).unwrap();
//     assert_eq!(buffer_contents.len(), buffer.byte_length as usize);

//     let mesh = &root.meshes()[0];
//     let primitive = &mesh.primitives[0];
//     assert_eq!(primitive.mode, Mode::Triangles);

//     let pos_accessor_index = primitive.attributes["POSITION"].value() as usize;
//     let pos_accessor = &root.accessors()[pos_accessor_index];
//     let pos_buffer_view = &root.buffer_views()[pos_accessor.buffer_view.value() as usize];

//     let position_data = &buffer_contents[
//         pos_buffer_view.byte_offset as usize .. (pos_buffer_view.byte_offset + pos_buffer_view.byte_length) as usize];

//     // TODO!: deal with no index case
//     let index_accessor_index = primitive.indices.as_ref().unwrap().clone();
//     let index_accessor = &root.accessor(index_accessor_index);
//     let index_buffer_view = &root.buffer_view(index_accessor.buffer_view.clone());
//     let index_data = &buffer_contents[
//         index_buffer_view.byte_offset as usize .. (index_buffer_view.byte_offset + index_buffer_view.byte_length) as usize];

//     let normals_accessor_index = primitive.attributes["NORMAL"].clone();
//     let normals_accessor = &root.accessor(normals_accessor_index);
//     let normal_buffer_view = &root.buffer_view(normals_accessor.buffer_view.clone());
//     let normal_data = &buffer_contents[
//         normal_buffer_view.byte_offset as usize .. (normal_buffer_view.byte_offset + normal_buffer_view.byte_length) as usize
//     ];

//     println!("pos len: {}", position_data.len());
//     println!("idx len: {}", index_data.len());
//     println!("nml len: {}", normal_data.len());
// }

// pub fn load_box_2(root: &Root) {
//     let buffer_data = root.load_buffer(0);

//     let mesh = &root.meshes()[0];
//     let primitive = &mesh.primitives[0];
//     let position_data = root.get_attribute_data(&buffer_data, primitive, "POSITION");
// }

trait DataAccessor<'a> {
    fn load_buffer(&self, index: usize) -> Vec<u8>;
    fn get_buffer_view_data(&self, buffer_data: &'a [u8], buffer_view_index: usize) -> &'a [u8];
    fn get_attribute_data(&self, buffer_data: &'a [u8], primitive: &Primitive, attribute_name: &str) -> &'a [u8];
    // fn get_index_data(&self, primitive: &Primitive);
}

// impl <'a> DataAccessor<'a> for Root<'a> {
//     fn load_buffer(&self, index: usize) -> Vec<u8> {
//         let buffer = &self.buffers()[index];
//         // TODO!: determine base directory... / handle non-file URIs
//         let mut file = std::fs::File::open(format!("src/data/{}", buffer.uri)).unwrap();
//         let mut buffer_data = Vec::with_capacity(buffer.byte_length as usize);
//         file.read_to_end(&mut buffer_data).unwrap();
//         assert_eq!(buffer_data.len(), buffer.byte_length as usize);
//         buffer_data
//     }

//     fn get_buffer_view_data(&self, buffer_data: &'a [u8], buffer_view_index: usize) -> &'a [u8] {
//         let buffer_view = &self.buffer_views()[buffer_view_index];
//         // TODO!!!: must take into account buffer_view.buffer!!
//          &buffer_data[buffer_view.byte_offset as usize .. (buffer_view.byte_offset + buffer_view.byte_length) as usize]
//     }

//     fn get_attribute_data(&self, buffer_data: &'a [u8], primitive: &Primitive, attribute_name: &str) -> &'a [u8] {
//         // TODO!: handle non-existing attr names
//         let accessor_index = primitive.attributes[attribute_name].clone();
//         let accessor = &self.accessor(accessor_index);
//         self.get_buffer_view_data(buffer_data, accessor.buffer_view.value() as usize)
//     }

// }
<commit_msg>WIP gltf loading<commit_after>// use std::io::Read;
use gltf;
// use gltf::root::Root;
// use gltf::accessor::{ Accessor };
// use gltf::buffer::{ Target };
use gltf::mesh::{ /*Mode, */Primitive };

use gltf::import::Source;

pub fn load_file(path: &str) {
    let mut importer = gltf::Importer::new();
    let gltf = importer.import_from_path(path);
    match gltf {
        Ok(gltf) => {
            println!("glTF version 2.0");
            println!("{:#?}", gltf);
            load_box(&gltf);
            // load_box_2(&root);
        }
        Err(err) => {
            println!("Error: {:#?}", err);
        }
    }
}

// struct PrimitiveData<'a> {
//     accessor: &'a Accessor<'a>,
//     buffer_view: &'a BufferView,
//     data: &'a [u8],
// }

pub fn load_box(gltf: &Gltf) {
    // let buffer = &root.buffers()[0];
    let buffer = &gltf.buffers().nth(0);

    let mut buffer_contents = buffer;

    let mesh = &gltf.meshes().nth(0).unwrap();
    let primitive = &mesh.primitives().nth(0).unwrap();
    assert_eq!(primitive.mode(), Mode::Triangles);

//     let pos_accessor_index = primitive.attributes["POSITION"].value() as usize;
//     let pos_accessor = &root.accessors()[pos_accessor_index];
//     let pos_buffer_view = &root.buffer_views()[pos_accessor.buffer_view.value() as usize];

//     let position_data = &buffer_contents[
//         pos_buffer_view.byte_offset as usize .. (pos_buffer_view.byte_offset + pos_buffer_view.byte_length) as usize];

//     // TODO!: deal with no index case
//     let index_accessor_index = primitive.indices.as_ref().unwrap().clone();
//     let index_accessor = &root.accessor(index_accessor_index);
//     let index_buffer_view = &root.buffer_view(index_accessor.buffer_view.clone());
//     let index_data = &buffer_contents[
//         index_buffer_view.byte_offset as usize .. (index_buffer_view.byte_offset + index_buffer_view.byte_length) as usize];

//     let normals_accessor_index = primitive.attributes["NORMAL"].clone();
//     let normals_accessor = &root.accessor(normals_accessor_index);
//     let normal_buffer_view = &root.buffer_view(normals_accessor.buffer_view.clone());
//     let normal_data = &buffer_contents[
//         normal_buffer_view.byte_offset as usize .. (normal_buffer_view.byte_offset + normal_buffer_view.byte_length) as usize
//     ];

//     println!("pos len: {}", position_data.len());
//     println!("idx len: {}", index_data.len());
//     println!("nml len: {}", normal_data.len());
}

// pub fn load_box_2(root: &Root) {
//     let buffer_data = root.load_buffer(0);

//     let mesh = &root.meshes()[0];
//     let primitive = &mesh.primitives[0];
//     let position_data = root.get_attribute_data(&buffer_data, primitive, "POSITION");
// }

trait DataAccessor<'a> {
    fn load_buffer(&self, index: usize) -> Vec<u8>;
    fn get_buffer_view_data(&self, buffer_data: &'a [u8], buffer_view_index: usize) -> &'a [u8];
    fn get_attribute_data(&self, buffer_data: &'a [u8], primitive: &Primitive, attribute_name: &str) -> &'a [u8];
    // fn get_index_data(&self, primitive: &Primitive);
}

// impl <'a> DataAccessor<'a> for Root<'a> {
//     fn load_buffer(&self, index: usize) -> Vec<u8> {
//         let buffer = &self.buffers()[index];
//         // TODO!: determine base directory... / handle non-file URIs
//         let mut file = std::fs::File::open(format!("src/data/{}", buffer.uri)).unwrap();
//         let mut buffer_data = Vec::with_capacity(buffer.byte_length as usize);
//         file.read_to_end(&mut buffer_data).unwrap();
//         assert_eq!(buffer_data.len(), buffer.byte_length as usize);
//         buffer_data
//     }

//     fn get_buffer_view_data(&self, buffer_data: &'a [u8], buffer_view_index: usize) -> &'a [u8] {
//         let buffer_view = &self.buffer_views()[buffer_view_index];
//         // TODO!!!: must take into account buffer_view.buffer!!
//          &buffer_data[buffer_view.byte_offset as usize .. (buffer_view.byte_offset + buffer_view.byte_length) as usize]
//     }

//     fn get_attribute_data(&self, buffer_data: &'a [u8], primitive: &Primitive, attribute_name: &str) -> &'a [u8] {
//         // TODO!: handle non-existing attr names
//         let accessor_index = primitive.attributes[attribute_name].clone();
//         let accessor = &self.accessor(accessor_index);
//         self.get_buffer_view_data(buffer_data, accessor.buffer_view.value() as usize)
//     }

// }
<|endoftext|>


<commit_before>
//! Glyph caching

use error::Error;
use freetype;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use graphics;

use Texture;

/// The type alias for the font size.
pub type FontSize = u32;

/// The type alias for font characters.
pub type Character = graphics::character::Character<Texture>;

/// A struct used for caching rendered font.
pub struct GlyphCache {
    /// The font face.
    pub face: freetype::Face,
    data: HashMap<FontSize, HashMap<char, Character>>,
}

impl GlyphCache {

    /// Constructor for a GlyphCache.
    pub fn new(font: &Path) -> Result<GlyphCache, Error> {
        let freetype = match freetype::Library::init() {
            Ok(freetype) => freetype,
            Err(why) => return Err(Error::FreetypeError(why)),
        };
        let face = match freetype.new_face(font, 0) {
            Ok(face) => face,
            Err(why) => return Err(Error::FreetypeError(why)),
        };
        Ok(GlyphCache {
            face: face,
            data: HashMap::new(),
        })
    }

    /// Load a `Character` from a given `FontSize` and `char`.
    fn load_character(&mut self, size: FontSize, ch: char) {
        // Don't load glyph twice
        if self.data.get(&size)
            .map(|entry| entry.contains_key(&ch))
            .unwrap_or(false) { return; }

        self.face.set_pixel_sizes(0, size).unwrap();
        self.face.load_char(ch as usize, freetype::face::DEFAULT).unwrap();
        let glyph = self.face.glyph().get_glyph().unwrap();
        let bitmap_glyph = glyph.to_bitmap(freetype::render_mode::RenderMode::Normal, None)
            .unwrap();
        let bitmap = bitmap_glyph.bitmap();
        let texture = Texture::from_memory_alpha(bitmap.buffer(),
                                                 bitmap.width() as u32,
                                                 bitmap.rows() as u32).unwrap();
        let glyph_size = glyph.advance();
        self.data[size].insert(ch, Character {
            offset: [
                    bitmap_glyph.left() as f64,
                    bitmap_glyph.top() as f64
                ],
            size: [
                    (glyph_size.x >> 16) as f64,
                    (glyph_size.y >> 16) as f64
                ],
            texture: texture,
        });
    }

    /// Load all characters in the `chars` iterator for `size`
    pub fn preload_chars<I: Iterator<Item = char>>(&mut self, size: FontSize, mut chars: I) {
        for ch in chars {
            self.load_character(size, ch);
        }
    }

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

    /// Return `ch` for `size` if it's already cached. Don't load.
    /// See the `preload_*` functions.
    pub fn opt_character(&self, size: FontSize, ch: char) -> Option<&Character> {
        self.data.get(&size).and_then(|entry| entry.get(&ch))
    }
}

impl graphics::character::CharacterCache for GlyphCache {
    type Texture = Texture;

    fn character(&mut self, size: FontSize, ch: char) -> &Character {
        match {
            match self.data.entry(size) {
                Vacant(entry) => entry.insert(HashMap::new()),
                Occupied(entry) => entry.into_mut(),
            }
        }.contains_key(&ch) {
            true => &self.data[size][ch],
            false => { self.load_character(size, ch); &self.data[size][ch] }
        }
    }
}
<commit_msg>Added from_bytes to GlyphCache so loading fonts with include_bytes! is possible.<commit_after>
//! Glyph caching

use error::Error;
use freetype;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use graphics;

use Texture;

/// The type alias for the font size.
pub type FontSize = u32;

/// The type alias for font characters.
pub type Character = graphics::character::Character<Texture>;

/// A struct used for caching rendered font.
pub struct GlyphCache {
    /// The font face.
    pub face: freetype::Face,
    data: HashMap<FontSize, HashMap<char, Character>>,
}

impl GlyphCache {

    /// Constructor for a GlyphCache.
    pub fn new(font: &Path) -> Result<GlyphCache, Error> {
        let freetype = match freetype::Library::init() {
            Ok(freetype) => freetype,
            Err(why) => return Err(Error::FreetypeError(why)),
        };
        let face = match freetype.new_face(font, 0) {
            Ok(face) => face,
            Err(why) => return Err(Error::FreetypeError(why)),
        };
        Ok(GlyphCache {
            face: face,
            data: HashMap::new(),
        })
    }

    /// Creates a GlyphCache for a font stored in memory.
    pub fn from_bytes(font: &[u8]) -> Result<GlyphCache, Error> {
        let freetype = match freetype::Library::init() {
            Ok(freetype) => freetype,
            Err(why) => return Err(Error::FreetypeError(why))
        };
        let face = match freetype.new_memory_face(font, 0) {
            Ok(face) => face,
            Err(why) => return Err(Error::FreetypeError(why))
        };
        Ok(GlyphCache {
            face: face,
            data: HashMap::new()
        })
    }

    /// Load a `Character` from a given `FontSize` and `char`.
    fn load_character(&mut self, size: FontSize, ch: char) {
        // Don't load glyph twice
        if self.data.get(&size)
            .map(|entry| entry.contains_key(&ch))
            .unwrap_or(false) { return; }

        self.face.set_pixel_sizes(0, size).unwrap();
        self.face.load_char(ch as usize, freetype::face::DEFAULT).unwrap();
        let glyph = self.face.glyph().get_glyph().unwrap();
        let bitmap_glyph = glyph.to_bitmap(freetype::render_mode::RenderMode::Normal, None)
            .unwrap();
        let bitmap = bitmap_glyph.bitmap();
        let texture = Texture::from_memory_alpha(bitmap.buffer(),
                                                 bitmap.width() as u32,
                                                 bitmap.rows() as u32).unwrap();
        let glyph_size = glyph.advance();
        self.data[size].insert(ch, Character {
            offset: [
                    bitmap_glyph.left() as f64,
                    bitmap_glyph.top() as f64
                ],
            size: [
                    (glyph_size.x >> 16) as f64,
                    (glyph_size.y >> 16) as f64
                ],
            texture: texture,
        });
    }

    /// Load all characters in the `chars` iterator for `size`
    pub fn preload_chars<I: Iterator<Item = char>>(&mut self, size: FontSize, mut chars: I) {
        for ch in chars {
            self.load_character(size, ch);
        }
    }

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

    /// Return `ch` for `size` if it's already cached. Don't load.
    /// See the `preload_*` functions.
    pub fn opt_character(&self, size: FontSize, ch: char) -> Option<&Character> {
        self.data.get(&size).and_then(|entry| entry.get(&ch))
    }
}

impl graphics::character::CharacterCache for GlyphCache {
    type Texture = Texture;

    fn character(&mut self, size: FontSize, ch: char) -> &Character {
        match {
            match self.data.entry(size) {
                Vacant(entry) => entry.insert(HashMap::new()),
                Occupied(entry) => entry.into_mut(),
            }
        }.contains_key(&ch) {
            true => &self.data[size][ch],
            false => { self.load_character(size, ch); &self.data[size][ch] }
        }
    }
}
<|endoftext|>


<commit_before>/**
 * s3-file-upload
 */

extern crate rusoto;

use std::env;
use std::fs;
use std::path::Path;
use std::fs::File;
use std::io::{stdin, Read, Write};
use std::clone::Clone;

use rusoto::{ProfileProvider, Region};
use rusoto::s3::{Object, S3Client, ListObjectsV2Request, PutObjectRequest};
use rusoto::default_tls_client;

/**
 * Struct for files
 */
#[derive(Clone)]
struct LocalFile
{
    path: std::path::PathBuf
}

/**
 * Gets a mime type for a local file
 */
fn local_file_get_mime(file: &LocalFile) -> String
{
    let extension = file.path.extension().unwrap().to_str();

    let mime_type = match extension.as_ref()
    {
        Some(&"html") => "text/html",
        Some(&"css")  => "text/css",
        Some(&"png")  => "image/png",
        Some(&"gif")  => "image/gif",
        Some(&"jpg")  => "image/jpeg",
        Some(&"xml")  => "application/xml",
        _             => "application/octet-stream",
    };

    return mime_type.to_owned();
}

/**
 * Get a relative path
 */
fn local_file_get_relative_path(file: &LocalFile, local_path: &String) -> String
{
    let full_path: String = file.path.as_path().to_str().unwrap().to_owned();
    let last_path_section = full_path.split(local_path)
        .last()
        .unwrap()
        .replace("\\", "/");

    return last_path_section.to_owned();
}

/**
 * Upload a local file to an s3 bucket location
 */
fn local_file_upload_to_bucket(file: &LocalFile, local_path: &String, bucket_name: &String)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let file = file.clone();

    print!("Uploading \"{}\" to \"{}/{}\"...", file.path.to_str().unwrap(), bucket_name, local_file_get_relative_path(&file, &local_path));
    Some(std::io::stdout().flush());

    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let request = PutObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: local_file_get_relative_path(&file, &local_path),
                body: Some(contents),
                acl: Some(String::from("public-read")),
                content_type: Some(local_file_get_mime(&file)),
                ..PutObjectRequest::default()
            };

            match client.put_object(&request)
            {
                Ok(_) =>
                {
                    println!(" DONE");
                }
                Err(error) =>
                {
                   println!(" Error: {}", error);
                }
            }
        }
        Err(error) =>
        {
            println!("Error: {}", error);
        }
    }
}

/**
 * Get objects in an s3 bucket
 */
fn get_bucket_objects(bucket_name: &String, objects: &mut Vec<Object>)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let request = ListObjectsV2Request
    {
        bucket: bucket_name.to_owned(),
        ..ListObjectsV2Request::default()
    };

    if let Ok(response) = client.list_objects_v2(&request)
    {
        for object in &response.contents.unwrap()
        {
            objects.push(object.clone());
        }
    }

}

/**
 * Get files from a local path
 */
fn get_local_files(local_path: &String, files: &mut Vec<LocalFile>)
{
    if let Ok(entries) = fs::read_dir(Path::new(&local_path))
    {
        for entry in entries
        {
            let file = entry.unwrap();

            if let Ok(metadata) = file.metadata()
            {
                if metadata.is_dir()
                {
                    get_local_files(&file.path().to_str().unwrap().to_owned(), files);
                }
                else
                {
                    files.push(LocalFile { path: file.path() });
                }
            }
        }
    }
}

/**
 * Main function
 */
fn main()
{
    if env::args().len() < 3
    {
        println!("Usage: s3-file-upload LOCAL_PATH BUCKET_NAME");
        return;
    }

    let local_path  = env::args().nth(1).unwrap();
    let bucket_name = env::args().nth(2).unwrap();

    if !Path::new("credentials").exists()
    {
        println!("credentials file could not be found");
        return;
    }

    let mut files: Vec<LocalFile> = Vec::new();
    get_local_files(&local_path, &mut files);

    // NOTE: bucket_objects is currently not used for anything.
    let mut bucket_objects: Vec<Object> = Vec::new();
    get_bucket_objects(&bucket_name, &mut bucket_objects);

    println!("\nFiles found to be uploaded:\n");

    for file in &files
    {
        println!("{}", file.path.to_str().unwrap());
    }

    let mut input_string = String::new();

    while input_string != "y" && input_string != "n"
    {
        println!("\nConfirm upload? <Y/n>");

        input_string.clear();
        stdin().read_line(&mut input_string).expect("Did not input string");
        input_string = String::from(input_string.trim().to_lowercase());

        if input_string == "" || input_string == "yes"
        {
            input_string = String::from("y");
        }
        else if input_string == "no"
        {
            input_string = String::from("n");
        }
    }

    let confirm_upload = input_string == "y";

    if confirm_upload
    {
        for file in &files
        {
            local_file_upload_to_bucket(&file, &local_path, &bucket_name);
        }
    }
    else
    {
        println!("Upload cancelled")
    }
}
<commit_msg>Confirm upload answer now defaults to no<commit_after>/**
 * s3-file-upload
 */

extern crate rusoto;

use std::env;
use std::fs;
use std::path::Path;
use std::fs::File;
use std::io::{stdin, Read, Write};
use std::clone::Clone;

use rusoto::{ProfileProvider, Region};
use rusoto::s3::{Object, S3Client, ListObjectsV2Request, PutObjectRequest};
use rusoto::default_tls_client;

/**
 * Struct for files
 */
#[derive(Clone)]
struct LocalFile
{
    path: std::path::PathBuf
}

/**
 * Gets a mime type for a local file
 */
fn local_file_get_mime(file: &LocalFile) -> String
{
    let extension = file.path.extension().unwrap().to_str();

    let mime_type = match extension.as_ref()
    {
        Some(&"html") => "text/html",
        Some(&"css")  => "text/css",
        Some(&"png")  => "image/png",
        Some(&"gif")  => "image/gif",
        Some(&"jpg")  => "image/jpeg",
        Some(&"xml")  => "application/xml",
        _             => "application/octet-stream",
    };

    return mime_type.to_owned();
}

/**
 * Get a relative path
 */
fn local_file_get_relative_path(file: &LocalFile, local_path: &String) -> String
{
    let full_path: String = file.path.as_path().to_str().unwrap().to_owned();
    let last_path_section = full_path.split(local_path)
        .last()
        .unwrap()
        .replace("\\", "/");

    return last_path_section.to_owned();
}

/**
 * Upload a local file to an s3 bucket location
 */
fn local_file_upload_to_bucket(file: &LocalFile, local_path: &String, bucket_name: &String)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let file = file.clone();

    print!("Uploading \"{}\" to \"{}/{}\"...", file.path.to_str().unwrap(), bucket_name, local_file_get_relative_path(&file, &local_path));
    Some(std::io::stdout().flush());

    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let request = PutObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: local_file_get_relative_path(&file, &local_path),
                body: Some(contents),
                acl: Some(String::from("public-read")),
                content_type: Some(local_file_get_mime(&file)),
                ..PutObjectRequest::default()
            };

            match client.put_object(&request)
            {
                Ok(_) =>
                {
                    println!(" DONE");
                }
                Err(error) =>
                {
                   println!(" Error: {}", error);
                }
            }
        }
        Err(error) =>
        {
            println!("Error: {}", error);
        }
    }
}

/**
 * Get objects in an s3 bucket
 */
fn get_bucket_objects(bucket_name: &String, objects: &mut Vec<Object>)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let request = ListObjectsV2Request
    {
        bucket: bucket_name.to_owned(),
        ..ListObjectsV2Request::default()
    };

    if let Ok(response) = client.list_objects_v2(&request)
    {
        for object in &response.contents.unwrap()
        {
            objects.push(object.clone());
        }
    }

}

/**
 * Get files from a local path
 */
fn get_local_files(local_path: &String, files: &mut Vec<LocalFile>)
{
    if let Ok(entries) = fs::read_dir(Path::new(&local_path))
    {
        for entry in entries
        {
            let file = entry.unwrap();

            if let Ok(metadata) = file.metadata()
            {
                if metadata.is_dir()
                {
                    get_local_files(&file.path().to_str().unwrap().to_owned(), files);
                }
                else
                {
                    files.push(LocalFile { path: file.path() });
                }
            }
        }
    }
}

/**
 * Main function
 */
fn main()
{
    if env::args().len() < 3
    {
        println!("Usage: s3-file-upload LOCAL_PATH BUCKET_NAME");
        return;
    }

    let local_path  = env::args().nth(1).unwrap();
    let bucket_name = env::args().nth(2).unwrap();

    if !Path::new("credentials").exists()
    {
        println!("credentials file could not be found");
        return;
    }

    let mut files: Vec<LocalFile> = Vec::new();
    get_local_files(&local_path, &mut files);

    // NOTE: bucket_objects is currently not used for anything.
    let mut bucket_objects: Vec<Object> = Vec::new();
    get_bucket_objects(&bucket_name, &mut bucket_objects);

    println!("\nFiles found to be uploaded:\n");

    for file in &files
    {
        println!("{}", file.path.to_str().unwrap());
    }

    let mut input_string = String::new();

    while input_string != "y" && input_string != "n"
    {
        println!("\nConfirm upload? <y/N>");

        input_string.clear();
        stdin().read_line(&mut input_string).expect("Did not input string");
        input_string = String::from(input_string.trim().to_lowercase());

        if input_string == "" || input_string == "no"
        {
            input_string = String::from("n");
        }
        else if input_string == "yes"
        {
            input_string = String::from("y");
        }
    }

    let confirm_upload = input_string == "y";

    if confirm_upload
    {
        for file in &files
        {
            local_file_upload_to_bucket(&file, &local_path, &bucket_name);
        }
    }
    else
    {
        println!("Upload cancelled")
    }
}
<|endoftext|>


<commit_before>extern crate convolution;
extern crate threadpool;
use std::sync::mpsc::channel;
use std::sync::Arc;
use convolution::convolute::*;
use convolution::utils::*;
use std::thread;

use std::env;

// Prints each argument on a separate line

fn main() {

    const x: usize = 4;
    const y: usize = 4;

    //  Getting image path and stencils
    let args = env::args();

    let mut num_cpus = 1;
    let mut image_path = "".to_string();
    let mut stencils = Vec::new();

    for (i, j) in args.enumerate(){
        if i == 1 {
            let cpu_input = j.clone().parse::<usize>().unwrap();
            if cpu_input > 1 {
                num_cpus = cpu_input
            }
        }

        if i == 2  {
            image_path = j.clone()
        }

        if i >= 3 {
            stencils.push(j.clone())
        }
    }

    let loaded_img = img_from_file(image_path);

    let stencil: Vec<Vec<f32>> = stencil_from_file(stencils.first().unwrap());

    let shared_stencil =  Arc::new(stencil);
    let shared_img =  Arc::new(loaded_img);

    static mut output_image: [[(f32, f32, f32);x];y] = [[(0.0,0.0,0.0); x]; y];

    let mut handles = Vec::new();

    for i in 0..num_cpus {
        let stencil_ref = shared_stencil.clone();
        let img_ref = shared_img.clone();
        println!("{:?}", i);

        let handle = thread::spawn(move || {
            unsafe {
                let result = compute_pixel(&stencil_ref, Coordinate(i as isize,i as isize), &img_ref.clone());

                output_image[i as usize][i as usize]= (result.0, result.1, result.2);
                println!("{:?}", result);
            }
        });
        handles.push(handle);
    }

    for h in handles {
        h.join();
    }

    unsafe{
        println!("{:?}", output_image);
    }

    // println!("Pixel value computed: {}", result[0].0);
}
<commit_msg>working convolutions<commit_after>extern crate convolution;
extern crate threadpool;
use std::sync::mpsc::channel;
use std::sync::Arc;
use convolution::convolute::*;
use convolution::utils::*;
use std::thread;

use std::env;

// Prints each argument on a separate line

fn main() {

    const x: usize = 4;
    const y: usize = 4;

    //  Getting image path and stencils
    let args = env::args();

    let mut num_cpus = 1;
    let mut image_path = "".to_string();
    let mut stencils = Vec::new();

    for (i, j) in args.enumerate(){
        if i == 1 {
            let cpu_input = j.clone().parse::<usize>().unwrap();
            if cpu_input > 1 {
                num_cpus = cpu_input
            }
        }

        if i == 2  {
            image_path = j.clone()
        }

        if i >= 3 {
            stencils.push(j.clone())
        }
    }

    let loaded_img = img_from_file(image_path);

    let stencil: Vec<Vec<f32>> = stencil_from_file(stencils.first().unwrap());

    let shared_stencil =  Arc::new(stencil);
    let shared_img =  Arc::new(loaded_img);

    static mut output_image: [[(f32, f32, f32);x];y] = [[(0.0,0.0,0.0); x]; y];

    let mut handles = Vec::new();

    let x_range: Vec<usize> = (0..x).collect();
    let y_range: Vec<usize> = (0..y).collect();

    let mut xs = Vec::new();
    let mut ys = Vec::new();

    if num_cpus % 2 == 0 {
        xs = x_range.chunks(x/(num_cpus/2)).collect();
        ys = y_range.chunks(x/(num_cpus/2)).collect();
    } else {
        xs = x_range.chunks(x/(num_cpus/2 + 1)).collect();
        ys = y_range.chunks(x/(num_cpus/2 + 1)).collect();
    }

    println!("{:?}", xs.clone());
    println!("{:?}", ys.clone());

    let mut is = Vec::new();
    let mut js = Vec::new();

    for i in xs.clone() {
        let mut tmp = Vec::new();
        for n in i {
            tmp.push(n.clone())
        }
        is.push(tmp.clone())
    }

    for j in ys.clone() {
        let mut tmp = Vec::new();
        for n in j {
            tmp.push(n.clone())
        }
        js.push(tmp.clone())
    }

    for i in is.clone() {
        for j in js.clone() {
            let arc_i = Arc::new(i.clone());
            let i_ref = arc_i.clone();

            let arc_j = Arc::new(j.clone());
            let j_ref = arc_j.clone();

            let stencil_ref = shared_stencil.clone();
            let img_ref = shared_img.clone();

            let handle = thread::spawn(move || {

                for a in &i_ref.clone()[..] {
                    for b in &j_ref.clone()[..] {

                         unsafe {
                            let result = compute_pixel(&stencil_ref, Coordinate(*a as isize,*b as isize), &img_ref.clone());

                            output_image[*a as usize][*b as usize]= (result.0, result.1, result.2);

                        }
                    }
                }
            });

            handles.push(handle);
        }
    }

    for h in handles {
        h.join();
    }

    unsafe{
        println!("{:?}", output_image);
    }

}
<|endoftext|>


<commit_before>extern crate sha1;
extern crate ini;
extern crate getopts;

use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::io::{self, Read, Write, BufWriter};
use std::iter::FromIterator;
use std::env;
use ini::{ini as ini2, Ini};

const APP_NAME: &'static str = "mt_media_collector";

type Sha1DigestBytes = [u8; 20];
type ModList = Vec<String>;
type MediaSet = Vec<Asset>;

struct Asset {
	path: PathBuf,
	hash: Sha1DigestBytes,
}

impl Asset {
	pub fn new(pb: PathBuf, h: Sha1DigestBytes) -> Self {
		Asset { path: pb, hash: h }
	}
}

impl PartialEq for Asset {
	fn eq(&self, other: &Self) -> bool {
		self.hash == other.hash
	}
}

enum AssetCopyMode {Symlink, Hardlink, Copy, None}

fn to_hex(input: &[u8]) -> String
{
	String::from_iter(input.iter().map(|b| format!("{:02x}", b)))
}

fn make_absolute(path: &Path) -> PathBuf {
	if path.is_absolute() {
		path.to_path_buf()
	} else {
		std::env::current_dir()
			.and_then(|cd| Ok(cd.join(path)))
			.or_else(|_err| { let x: io::Result<_> = Ok(path.to_path_buf()); x })
			.unwrap()
	}
}

fn hash_file(path: &Path) -> io::Result<Sha1DigestBytes>
{
	let mut buf = [0u8; 8192];
	let mut hash = sha1::Sha1::new();
	let mut file = File::open(&path)?;
	loop {
		match file.read(&mut buf) {
			Ok(0) => break,
			Ok(len) => hash.update(&buf[..len]),
			Err(e) => return Err(e)
		}
	}
	Ok(hash.digest().bytes())
}

fn search_media_dir(ms: &mut MediaSet, path: &Path) -> io::Result<()>
{
	for entry in path.read_dir()? {
		let pb = entry?.path();
		if pb.is_file() {
			let h = hash_file(pb.as_path())?;
			ms.push(Asset::new(pb, h));
		}
	}
	Ok(())
}

fn search_mod_dir(ms: &mut MediaSet, path: &Path) -> io::Result<()>
{
	static MEDIA_DIRS: &'static [&'static str] = &["textures", "models", "sounds"];
	for media_dir in MEDIA_DIRS {
		let media_pb = path.join(media_dir);
		if media_pb.is_dir() {
			search_media_dir(ms, media_pb.as_path())?;
		}
	}
	Ok(())
}

fn search_modpack_dir(ms: &mut MediaSet, path: &Path, mods: Option<&ModList>) -> io::Result<()>
{
	for entry in path.read_dir()? {
		let p = entry?.path();
		if !p.is_dir() {
			continue;
		} else if p.join("modpack.txt").exists() {
			search_modpack_dir(ms, p.as_path(), mods)?;
		} else if p.join("init.lua").exists() {
			if let Some(list) = mods {
				if !list.contains(&p.file_name().expect("Modpack directory has no file name!")
						.to_str().expect("Modpack directory is not valid Unicode")
						.to_string()) {
					continue;
				}
			}
			search_mod_dir(ms, p.as_path())?;
		}
		// Otherwise it's probably a VCS directory or something similar
	}
	Ok(())
}

fn write_index(ms: &MediaSet, path: &Path) -> io::Result<()>
{
	let file = File::create(&path)?;
	let mut writer = BufWriter::new(file);
	writer.write_all(b"MTHS\x00\x01")?;
	for asset in ms {
		writer.write_all(&asset.hash)?;
	}
	Ok(())
}

fn copy_assets(ms: &MediaSet, path: &Path, mode: AssetCopyMode) -> io::Result<()>
{
	fn copy_no_result<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		fs::copy(src, dst).map(|_| ())
	}

	#[cfg(unix)]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		std::os::unix::fs::symlink(src, dst)
	}

	#[cfg(windows)]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		std::os::windows::fs::symlink_file(src, dst)
	}

	#[cfg(not(any(unix, windows)))]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		Err(io::Error::new(io::ErrorKind::Other,
			"Symlinking not supported on this platform!"))
	}

	let copy_func = match mode {
		AssetCopyMode::Symlink => symlink_file,
		AssetCopyMode::Hardlink => fs::hard_link,
		AssetCopyMode::Copy => copy_no_result,
		AssetCopyMode::None => return Ok(())
	};

	for asset in ms {
		copy_func(&asset.path, path.join(to_hex(&asset.hash)))?;
	}
	Ok(())
}

fn get_mod_list(path: &Path) -> Result<ModList, ini2::Error>
{
	let world_mt = match Ini::load_from_file(path.join("world.mt")
			.to_str().expect("World config path is not valid Unicode!")) {
		Ok(ini) => ini,
		Err(e) => return Err(e)
	};
	let main_sec = world_mt.general_section();

	let mut list: ModList = vec![];
	for (key, value) in main_sec {
		if !key.starts_with("load_mod_") || value != "true" {
			continue;
		}
		let (_, mod_name) = key.split_at(9);
		list.push(mod_name.to_string());
	}
	Ok(list)
}

fn handle_args() -> Option<getopts::Matches>
{
	let mut args = env::args();
	let cmd_name = args.next()
		.and_then(|opt| Path::new(&opt).file_name()
			.and_then(|os_fn| os_fn.to_str())
			.and_then(|s| Some(s.to_string()))
		).unwrap_or(APP_NAME.to_string());

	let mut opts = getopts::Options::new();
	opts.optflag("h", "help", "Print this help menu.");
	opts.reqopt("o", "out", "Path to the output directory.", "PATH");
	opts.reqopt("w", "world", "Path to the world directory.", "PATH");
	opts.reqopt("g", "game", "Path to the game directory.", "PATH");
	opts.optflagopt("c", "copy", "Copy assets to folder. \
		Takes optional copy method: one of 'symlink', 'hardlink', \
			'copy' (default)", "METHOD");

	let usage = || {
		opts.usage(&format!("{} [mod paths]\n\n\
			Collects assets for a Minetest HTTP asset server and \
			creates an index.mth file for those assets.",
			opts.short_usage(cmd_name.as_str())
		))
	};

	let matches = match opts.parse(args) {
		Ok(matches) => matches,
		Err(fail) => {
			print!("Error: {}\n\n{}", fail.to_string(), usage());
			return None;
		}
	};

	if matches.opt_present("h") {
		print!("{}", usage());
		return None;
	}

	return Some(matches);
}

fn int_main() -> i32 {
	macro_rules! handle_result {
		( $x:expr, $m:expr ) => {
			match $x {
				Ok(val) => val,
				Err(e) => {
					println!($m, e);
					return 1;
				}
			};
		}
	}

	let args = match handle_args() {
		Some(a) => a,
		None => return 1
	};

	let out_opt = args.opt_str("out").unwrap();
	let out_path = Path::new(&out_opt);
	let world_opt = args.opt_str("world").unwrap();
	let world_path = Path::new(&world_opt);
	let game_opt = args.opt_str("game").unwrap();
	let game_path = Path::new(&game_opt);

	let abs_out_path = make_absolute(out_path);
	let out_parent = abs_out_path.parent();
	if out_parent.is_some() && !out_parent.unwrap().exists() {
		println!("Error: Out path parent should exist.");
		return 1;
	}

	let copy_type = match args.opt_default("copy", "copy") {
		None => AssetCopyMode::None,
		Some(t) => {
			if t == "symlink" {
				AssetCopyMode::Symlink
			} else if t == "hardlink" {
				AssetCopyMode::Hardlink
			} else if t == "copy" {
				AssetCopyMode::Copy
			} else {
				println!("Error: Invalid value for copy argument.");
				return 1;
			}
		}
	};

	let mut ms = MediaSet::new();
	let mods = handle_result!(get_mod_list(world_path), "Error getting mod list: {}");

	// Search world mods.
	let worldmods_path = world_path.join("worldmods");
	if worldmods_path.exists() {
		handle_result!(search_modpack_dir(&mut ms, worldmods_path.as_path(), Some(&mods)),
				"Error searching world mods: {}");
	}

	// Search game mods.
	// Note: Game mods can not currently be disabled.
	handle_result!(search_modpack_dir(&mut ms, game_path.join("mods").as_path(), None),
				"Error searching game mods: {}");

	for mod_path in args.free {
		handle_result!(search_modpack_dir(&mut ms, Path::new(&mod_path), Some(&mods)),
				"Error searching other mods: {}");
	}

	// Deduplicate list.  Otherwise linking will fail and the index will
	// be unnecessarily large.
	ms.sort_by(|a, b| a.hash.cmp(&b.hash));
	ms.dedup();

	if !out_path.exists() {
		handle_result!(fs::create_dir(out_path), "{}");
	}

	handle_result!(write_index(&ms, out_path.join("index.mth").as_path()),
			"Error writing asset index: {}");

	handle_result!(copy_assets(&ms, out_path, copy_type),
			"Error linking assets: {}");

	0
}

fn main() {
	// Rust (stable) only supports setting the exit code by calling this
	// function, but it doesn't do cleanup, so it can't be run until all
	// resources have already been destroyed.
	std::process::exit(int_main())
}
<commit_msg>Get package name from cargo<commit_after>extern crate sha1;
extern crate ini;
extern crate getopts;

use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::io::{self, Read, Write, BufWriter};
use std::iter::FromIterator;
use std::env;
use ini::{ini as ini2, Ini};

type Sha1DigestBytes = [u8; 20];
type ModList = Vec<String>;
type MediaSet = Vec<Asset>;

struct Asset {
	path: PathBuf,
	hash: Sha1DigestBytes,
}

impl Asset {
	pub fn new(pb: PathBuf, h: Sha1DigestBytes) -> Self {
		Asset { path: pb, hash: h }
	}
}

impl PartialEq for Asset {
	fn eq(&self, other: &Self) -> bool {
		self.hash == other.hash
	}
}

enum AssetCopyMode {Symlink, Hardlink, Copy, None}

fn to_hex(input: &[u8]) -> String
{
	String::from_iter(input.iter().map(|b| format!("{:02x}", b)))
}

fn make_absolute(path: &Path) -> PathBuf {
	if path.is_absolute() {
		path.to_path_buf()
	} else {
		std::env::current_dir()
			.and_then(|cd| Ok(cd.join(path)))
			.or_else(|_err| { let x: io::Result<_> = Ok(path.to_path_buf()); x })
			.unwrap()
	}
}

fn hash_file(path: &Path) -> io::Result<Sha1DigestBytes>
{
	let mut buf = [0u8; 8192];
	let mut hash = sha1::Sha1::new();
	let mut file = File::open(&path)?;
	loop {
		match file.read(&mut buf) {
			Ok(0) => break,
			Ok(len) => hash.update(&buf[..len]),
			Err(e) => return Err(e)
		}
	}
	Ok(hash.digest().bytes())
}

fn search_media_dir(ms: &mut MediaSet, path: &Path) -> io::Result<()>
{
	for entry in path.read_dir()? {
		let pb = entry?.path();
		if pb.is_file() {
			let h = hash_file(pb.as_path())?;
			ms.push(Asset::new(pb, h));
		}
	}
	Ok(())
}

fn search_mod_dir(ms: &mut MediaSet, path: &Path) -> io::Result<()>
{
	static MEDIA_DIRS: &'static [&'static str] = &["textures", "models", "sounds"];
	for media_dir in MEDIA_DIRS {
		let media_pb = path.join(media_dir);
		if media_pb.is_dir() {
			search_media_dir(ms, media_pb.as_path())?;
		}
	}
	Ok(())
}

fn search_modpack_dir(ms: &mut MediaSet, path: &Path, mods: Option<&ModList>) -> io::Result<()>
{
	for entry in path.read_dir()? {
		let p = entry?.path();
		if !p.is_dir() {
			continue;
		} else if p.join("modpack.txt").exists() {
			search_modpack_dir(ms, p.as_path(), mods)?;
		} else if p.join("init.lua").exists() {
			if let Some(list) = mods {
				if !list.contains(&p.file_name().expect("Modpack directory has no file name!")
						.to_str().expect("Modpack directory is not valid Unicode")
						.to_string()) {
					continue;
				}
			}
			search_mod_dir(ms, p.as_path())?;
		}
		// Otherwise it's probably a VCS directory or something similar
	}
	Ok(())
}

fn write_index(ms: &MediaSet, path: &Path) -> io::Result<()>
{
	let file = File::create(&path)?;
	let mut writer = BufWriter::new(file);
	writer.write_all(b"MTHS\x00\x01")?;
	for asset in ms {
		writer.write_all(&asset.hash)?;
	}
	Ok(())
}

fn copy_assets(ms: &MediaSet, path: &Path, mode: AssetCopyMode) -> io::Result<()>
{
	fn copy_no_result<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		fs::copy(src, dst).map(|_| ())
	}

	#[cfg(unix)]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		std::os::unix::fs::symlink(src, dst)
	}

	#[cfg(windows)]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		std::os::windows::fs::symlink_file(src, dst)
	}

	#[cfg(not(any(unix, windows)))]
	fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
		Err(io::Error::new(io::ErrorKind::Other,
			"Symlinking not supported on this platform!"))
	}

	let copy_func = match mode {
		AssetCopyMode::Symlink => symlink_file,
		AssetCopyMode::Hardlink => fs::hard_link,
		AssetCopyMode::Copy => copy_no_result,
		AssetCopyMode::None => return Ok(())
	};

	for asset in ms {
		copy_func(&asset.path, path.join(to_hex(&asset.hash)))?;
	}
	Ok(())
}

fn get_mod_list(path: &Path) -> Result<ModList, ini2::Error>
{
	let world_mt = match Ini::load_from_file(path.join("world.mt")
			.to_str().expect("World config path is not valid Unicode!")) {
		Ok(ini) => ini,
		Err(e) => return Err(e)
	};
	let main_sec = world_mt.general_section();

	let mut list: ModList = vec![];
	for (key, value) in main_sec {
		if !key.starts_with("load_mod_") || value != "true" {
			continue;
		}
		let (_, mod_name) = key.split_at(9);
		list.push(mod_name.to_string());
	}
	Ok(list)
}

fn handle_args() -> Option<getopts::Matches>
{
	let mut args = env::args();
	let cmd_name = args.next()
		.and_then(|opt| Path::new(&opt).file_name()
			.and_then(|os_fn| os_fn.to_str())
			.and_then(|s| Some(s.to_string()))
		).unwrap_or(env!("CARGO_PKG_NAME").to_string());

	let mut opts = getopts::Options::new();
	opts.optflag("h", "help", "Print this help menu.");
	opts.reqopt("o", "out", "Path to the output directory.", "PATH");
	opts.reqopt("w", "world", "Path to the world directory.", "PATH");
	opts.reqopt("g", "game", "Path to the game directory.", "PATH");
	opts.optflagopt("c", "copy", "Copy assets to folder. \
		Takes optional copy method: one of 'symlink', 'hardlink', \
			'copy' (default)", "METHOD");

	let usage = || {
		opts.usage(&format!("{} [mod paths]\n\n\
			Collects assets for a Minetest HTTP asset server and \
			creates an index.mth file for those assets.",
			opts.short_usage(cmd_name.as_str())
		))
	};

	let matches = match opts.parse(args) {
		Ok(matches) => matches,
		Err(fail) => {
			print!("Error: {}\n\n{}", fail.to_string(), usage());
			return None;
		}
	};

	if matches.opt_present("h") {
		print!("{}", usage());
		return None;
	}

	return Some(matches);
}

fn int_main() -> i32 {
	macro_rules! handle_result {
		( $x:expr, $m:expr ) => {
			match $x {
				Ok(val) => val,
				Err(e) => {
					println!($m, e);
					return 1;
				}
			};
		}
	}

	let args = match handle_args() {
		Some(a) => a,
		None => return 1
	};

	let out_opt = args.opt_str("out").unwrap();
	let out_path = Path::new(&out_opt);
	let world_opt = args.opt_str("world").unwrap();
	let world_path = Path::new(&world_opt);
	let game_opt = args.opt_str("game").unwrap();
	let game_path = Path::new(&game_opt);

	let abs_out_path = make_absolute(out_path);
	let out_parent = abs_out_path.parent();
	if out_parent.is_some() && !out_parent.unwrap().exists() {
		println!("Error: Out path parent should exist.");
		return 1;
	}

	let copy_type = match args.opt_default("copy", "copy") {
		None => AssetCopyMode::None,
		Some(t) => {
			if t == "symlink" {
				AssetCopyMode::Symlink
			} else if t == "hardlink" {
				AssetCopyMode::Hardlink
			} else if t == "copy" {
				AssetCopyMode::Copy
			} else {
				println!("Error: Invalid value for copy argument.");
				return 1;
			}
		}
	};

	let mut ms = MediaSet::new();
	let mods = handle_result!(get_mod_list(world_path), "Error getting mod list: {}");

	// Search world mods.
	let worldmods_path = world_path.join("worldmods");
	if worldmods_path.exists() {
		handle_result!(search_modpack_dir(&mut ms, worldmods_path.as_path(), Some(&mods)),
				"Error searching world mods: {}");
	}

	// Search game mods.
	// Note: Game mods can not currently be disabled.
	handle_result!(search_modpack_dir(&mut ms, game_path.join("mods").as_path(), None),
				"Error searching game mods: {}");

	for mod_path in args.free {
		handle_result!(search_modpack_dir(&mut ms, Path::new(&mod_path), Some(&mods)),
				"Error searching other mods: {}");
	}

	// Deduplicate list.  Otherwise linking will fail and the index will
	// be unnecessarily large.
	ms.sort_by(|a, b| a.hash.cmp(&b.hash));
	ms.dedup();

	if !out_path.exists() {
		handle_result!(fs::create_dir(out_path), "{}");
	}

	handle_result!(write_index(&ms, out_path.join("index.mth").as_path()),
			"Error writing asset index: {}");

	handle_result!(copy_assets(&ms, out_path, copy_type),
			"Error linking assets: {}");

	0
}

fn main() {
	// Rust (stable) only supports setting the exit code by calling this
	// function, but it doesn't do cleanup, so it can't be run until all
	// resources have already been destroyed.
	std::process::exit(int_main())
}
<|endoftext|>


<commit_before>/*
 * File   : main.rs
 * Purpose: reimplementation of the classic `ed` in Rust
 * Program: red
 * About  : command-line text editor
 * Authors: Tommy Lincoln <pajamapants3000@gmail.com>
 * License: MIT; See LICENSE!
 * Notes  : Notes on successful compilation
 * Created: 10/16/2016
 */

// TODO: Change all instances of `unwrap` to proper error handling

//! A re-implementation of the classic `ed` program in Rust
//!
//! Current functionality will be to simply open the file
//! passed on invocation, and allow the user to execute print
//! commands. These commands will output specified lines from
//! the opened file.

// Bring in to namespace {{{
//extern crate clap;

use std::env;
use std::fs::{File, OpenOptions};
use::std::io::prelude::*;
// Use LineWriter instead of, or in addition to, BufWriter?
use::std::io::{self,BufReader,BufWriter,Error};

// }}}

// *** Constants *** {{{
// Define messages
// Some of these may be removed if builtin error descriptions work
const S_FOPEN: &'static str = "successfully opened file!";
//const E_FOPEN: &'static str = "unable to open file";

// Additional string constants
const LINE_CONT: &'static str = "\\\n";
const PROMPT: &'static str = "%";
const PROMPT_CONT: &'static str = ">";

// ^^^ Constants ^^^ }}}

// *** Data Structures *** {{{
#[derive(Default)]
struct FileMode {
    f_write:        bool,
    f_read:         bool,
    f_append:       bool,
    f_truncate:     bool,
    f_create:       bool,
    f_create_new:   bool,
}

struct Command<'a> {
    address_initial: u32,
    address_final: u32,
    operation: char,
    parameters: &'a str,
}

// ^^^ Data Structures ^^^ }}}

// Main {{{
fn main() {
    // quick'n''dirty - will process one by one later; clap?
    let args: Vec<String> = env::args().collect();

    // take as direct arg; will later be arg to flag
    let file_name: &str = &args[0];
    let file_mode = FileMode { f_read: true, ..Default::default() };
    let file_opened: File;

    match file_opener( file_name, file_mode ) {
        Ok(f) => {
            file_opened = f;
            println!( "{}", S_FOPEN );
            println!( "our file is: {:?}", file_opened );
        },
        Err(e) => {
            println!( "error: {}", e );

        },
    };

    //let mut file_buffer = BufReader::new(file_opened);
    //let mut file_writer = LineWriter::new(file_opened);
    let mut cli_reader = BufReader::new(io::stdin());
    let mut cli_writer = BufWriter::new(io::stdout());
    let mut cmd_input = String::new();
    let mut prompt = PROMPT.to_string();

    cli_writer.write(format!("{}", prompt).as_bytes()).unwrap();
    cli_writer.flush().unwrap();
    // Main interaction loop {{{
    loop {
        cli_reader.read_line(&mut cmd_input).unwrap();

        if cmd_input.ends_with(LINE_CONT) {  // continue
            prompt = PROMPT_CONT.to_string();
        } else {
            {                                            // Execute command {{{
                let command: Command = parse_command( &cmd_input );
                // just some test output
                cli_writer.write(command.parameters.as_bytes()).unwrap();
                cli_writer.write(command.address_initial.to_string()
                                 .as_bytes()).unwrap();
                cli_writer.write(b"\n").unwrap();
                cli_writer.write(command.address_final.to_string()
                                 .as_bytes()).unwrap();
                cli_writer.write(b"\n").unwrap();
                cli_writer.write(command.operation.to_string()
                                 .as_bytes()).unwrap();
                cli_writer.write(b"\n").unwrap();

            }                                           // Done executing }}}
            // ready for a new command
            cmd_input.clear();
            // in case of continuation, return prompt to standard
            prompt = PROMPT.to_string();
        }

        // prompt for the next round
        cli_writer.write(format!("{}", prompt).as_bytes()).unwrap();
        // put it all to the screen
        cli_writer.flush().unwrap();
    }
    //}}}

}
//}}}

/// Opens file with user-specified name and mode {{{
///
/// Uses global definitions of mode flags in this file
///
/// Returns direct result of call to OpenOptions::new()
/// This is of type Result<File, Error>
fn file_opener( name: &str, mode: FileMode ) -> Result<File, Error> {

    // let's introduce OpenOptions now, though we don't need it
    // until we introduce more functionality
    OpenOptions::new()
        .read(mode.f_read)
        .write(mode.f_write)
        .append(mode.f_append)
        .truncate(mode.f_truncate)
        .create(mode.f_create)
        .create_new(mode.f_create_new)
        .open( name )
}
//}}}

/// Parses command-mode input {{{
///
fn parse_command( _cmd_input: &str ) -> Command {
    Command {
        address_initial: 1,
        address_final: 1,
        operation: 'p',
        parameters: _cmd_input,
    }
}
//]]]
<commit_msg>get opchar index in cmd_input<commit_after>/*
 * File   : main.rs
 * Purpose: reimplementation of the classic `ed` in Rust
 * Program: red
 * About  : command-line text editor
 * Authors: Tommy Lincoln <pajamapants3000@gmail.com>
 * License: MIT; See LICENSE!
 * Notes  : Notes on successful compilation
 * Created: 10/16/2016
 */

// TODO: Change all instances of `unwrap` to proper error handling

//! A re-implementation of the classic `ed` program in Rust
//!
//! Current functionality will be to simply open the file
//! passed on invocation, and allow the user to execute print
//! commands. These commands will output specified lines from
//! the opened file.

// Bring in to namespace {{{
//extern crate clap;

use std::env;
use std::fs::{File, OpenOptions};
use std::str::Chars;
use::std::io::prelude::*;
// Use LineWriter instead of, or in addition to, BufWriter?
use::std::io::{self,BufReader,BufWriter};

// }}}

// Crate Attributes {{{
//}}}

// *** Constants *** {{{
// Define messages
// Some of these may be removed if builtin error descriptions work
const S_FOPEN_MSG: &'static str = "successfully opened file!";
//const E_FOPEN_MSG: &'static str = "unable to open file";

// Additional string constants
const LINE_CONT: &'static str = "\\\n";
const PROMPT: &'static str = "%";
const PROMPT_CONT: &'static str = ">";

// ^^^ Constants ^^^ }}}

// *** Data Structures *** {{{
#[derive(Default)]
struct FileMode {
    f_write:        bool,
    f_read:         bool,
    f_append:       bool,
    f_truncate:     bool,
    f_create:       bool,
    f_create_new:   bool,
}

struct Command<'a> {
    address_initial: u32,
    address_final: u32,
    operation: char,
    parameters: &'a str,
}

enum RedError {
    FileOpen,
    FileClose,
}

// ^^^ Data Structures ^^^ }}}

// Main {{{
fn main() {
    // quick'n''dirty - will process one by one later; clap?
    let args: Vec<String> = env::args().collect();

    // take as direct arg; will later be arg to flag
    let file_name: &str = &args[0];
    let file_mode = FileMode { f_read: true, ..Default::default() };
    let file_opened: File;

    match file_opener( file_name, file_mode ) {
        Ok(f) => {
            file_opened = f;
            println!( "{}", S_FOPEN_MSG );
            println!( "our file is: {:?}", file_opened );
        },
        Err(e) => {
            println!( "error: {}", e );
            std::process::exit( error_code( RedError::FileOpen ) as i32 );

        },
    };

    //let mut file_buffer = BufReader::new(file_opened);
    //let mut file_writer = LineWriter::new(file_opened);
    let mut cli_reader = BufReader::new(io::stdin());
    let mut cli_writer = BufWriter::new(io::stdout());
    let mut cmd_input = String::new();
    let mut prompt = PROMPT.to_string();
    let mut user_quit: bool = false;

    cli_writer.write(format!("{}", prompt).as_bytes()).unwrap();
    cli_writer.flush().unwrap();
    // Main interaction loop {{{
    loop {
        cli_reader.read_line(&mut cmd_input).unwrap();

        if cmd_input.ends_with(LINE_CONT) {  // continue
            prompt = PROMPT_CONT.to_string();
        } else {
            {                                            // Execute command {{{
                let command: Command =
                        parse_command( &cmd_input, &file_opened );
                // just some test output
                cli_writer.write(command.parameters.as_bytes()).unwrap();
                cli_writer.write(command.address_initial.to_string()
                                 .as_bytes()).unwrap();
                cli_writer.write(b"\n").unwrap();
                cli_writer.write(command.address_final.to_string()
                                 .as_bytes()).unwrap();
                cli_writer.write(b"\n").unwrap();
                cli_writer.write(command.operation.to_string()
                                 .as_bytes()).unwrap();
                cli_writer.write(b"\n").unwrap();

                match command.operation {
                    'q' => user_quit = true,
                    _ => ()
                }

            }                                           // Done executing }}}
            // ready for a new command
            cmd_input.clear();
            // in case of continuation, return prompt to standard
            prompt = PROMPT.to_string();
        }

        if user_quit { break }

        // prompt for the next round
        cli_writer.write(format!("{}", prompt).as_bytes()).unwrap();
        // put it all to the screen
        cli_writer.flush().unwrap();
    }
    //}}}

    std::process::exit( error_code( RedError::FileClose ) as i32 );

}
//}}}

/// Opens file with user-specified name and mode {{{
///
/// Uses global definitions of mode flags in this file
///
/// Returns direct result of call to OpenOptions::new()
/// This is of type Result<File, io::Error>
fn file_opener( name: &str, mode: FileMode ) -> Result<File, io::Error> {

    // let's introduce OpenOptions now, though we don't need it
    // until we introduce more functionality
    OpenOptions::new()
        .read(mode.f_read)
        .write(mode.f_write)
        .append(mode.f_append)
        .truncate(mode.f_truncate)
        .create(mode.f_create)
        .create_new(mode.f_create_new)
        .open( name )
}
//}}}

/// Parses command-mode input {{{
///
fn parse_command<'a>( _cmd_input: &'a str, file_opened: &File ) -> Command<'a> {
    // MUST initialize?
    let mut _address_initial: u32 = 1;
    let mut _address_final: u32 = 1;
    let mut _operation: char = 'p';
    let mut _parameters: &str = _cmd_input;

    let op_indx: usize;
    op_indx = get_opchar_index( _cmd_input ).unwrap_or( 0usize );
    _address_final = op_indx as u32;

    match get_address( "", file_opened ) {
        ( x, y ) => {
            _address_initial = x;
            _address_final = y;
        }
    }

    Command {
        address_initial: _address_initial,
        address_final: _address_final,
        operation: _operation,
        parameters: _parameters,
    }
}
//}}}

/// Identify address or address range {{{
///
#[allow(unused_variables)]
fn get_address( address_string: &str, file_opened: &File ) -> ( u32, u32 ) {
    // start with simple auto-return of ( 1u32, 2u32 )
    ( 1u32, 2u32 )
}
//}}}

/// Return error code for given error type {{{
///
fn error_code( _error: RedError ) -> u32 {
    match _error {
        RedError::FileOpen => 280,
        RedError::FileClose => 281,
    }
}
//}}}

/// Find index of operation code in string
///
/// Parses full command and finds the index of the operation character;
/// The trick here is that the operation character may appear any number
/// of times as part of a regular expression used to specify an address
/// range that matches;
/// What this function does is simply locates the first alphabetic character
/// that is not wrapped in either /.../ or ?...?
///
/// Trims white space on the left as well - does not count these characters
///
/// # Examples
///
/// No address given
/// ```rust
/// let _in: &str = "e myfile.txt";
/// assert_eq!( get_opchar_index( _in ), 0 );
/// ```
///
/// No address given, with spaces
/// ```rust
/// let _in: &str = "       e myfile.txt";
/// assert_eq!( get_opchar_index( _in ), 0 );
/// ```
///
/// No address given, with spaces and tabs
/// ```rust
/// let _in: &str = "  		  	e myfile.txt";
/// assert_eq!( get_opchar_index( _in ), 0 );
/// ```
///
/// Most basic address value types
/// ```rust
/// let _in: &str = ".a";
/// assert_eq!( get_opchar_index( _in ), 1 );
/// ```
///
/// ```rust
/// let _in: &str = ".,.p";
/// assert_eq!( get_opchar_index( _in ), 3 );
/// ```
///
/// Slightly more complicated
/// ```rust
/// let _in: &str = ".-2,.+2p";
/// assert_eq!( get_opchar_index( _in ), 3 );
/// ```
///
/// Regular expression match line search forward
/// ```rust
/// let _in: &str = "/^Beginning with.*$/;/.* at the end$/s_mytest_yourtest_g";
/// assert_eq!( get_opchar_index( _in ), 37 );
/// ```
///
/// Regular expression match line search forward with spaces and tabs
/// ```rust
/// let _in: &str =
/// "		  	/^Beginning with.*$/;/.* at the end$/s_mytest_yourtest_g";
/// assert_eq!( get_opchar_index( _in ), 37 );
/// ```
///
/// Regular expression match line search backward
/// ```rust
/// let _in: &str = "?^Beginning with.*$?,?.* at the end$?s_mytest_yourtest_g";
/// assert_eq!( get_opchar_index( _in ), 37 );
/// ```
///
fn get_opchar_index( _cmd_input: &str ) -> Result<usize, RedError> {
    let mut result: i32 = -1;

    struct Interpret {
        index: u16,         // index of character in string
        pattern_char: char, // stores '/' or '?' while reading pattern
        escape: bool,       // last character read was '\'
        expect: bool,       // we expect next char to be ',', ';', or alpha
    }
    let mut current_state: Interpret = Interpret {
        index: 0u16,
        pattern_char: '\0',
        escape: false,
        expect: false,
    };

    let mut ch_iter: Chars = _cmd_input.trim_left().chars();

    loop {
        match ch_iter.next() {
            None => break,
            Some( x ) => {
                if current_state.pattern_char == '\0' {
                    if current_state.expect {
                        match x {
                            'a'...'z' | 'A'...'Z' => {
                                result = current_state.index as i32;
                                break;
                            },
                            ',' | ';' => {
                                current_state.escape= false;
                                current_state.expect = false
                            },
                            '\\' => current_state.escape = true,
                            _ => break,
                        }
                    }
                    match x {
                        '/' | '?' => {
                            if !current_state.escape {
                                current_state.pattern_char = x;
                            }
                        }
                        'a'...'z' | 'A'...'Z' => {
                            result = current_state.index as i32;
                            break;
                        },
                        '\\' => current_state.escape = true,
                        _ => {
                            current_state.escape= false;
                            current_state.expect = false;
                        },
                    }
                } else {
                    match x {
                        '/' => {
                            if '/' == current_state.pattern_char {
                                if !current_state.escape {
                                    current_state.pattern_char = '\0';
                                    current_state.expect = true;
                                }
                            }
                        }
                        '?' => {
                            if '?' == current_state.pattern_char {
                                if !current_state.escape {
                                    current_state.pattern_char = '\0';
                                    current_state.expect = true;
                                }
                            }
                        }
                        '\\' => current_state.escape = true,
                        _ => {},
                    }
                }
            }
        }
        current_state.index += 1;
    }
    if result < 0 {
        Result::Err( RedError::FileClose )
    } else {
        println!("result {:?}", result);
        Result::Ok( result as usize )   // result will be positive here
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;

use docopt::Docopt;
use commit_analyzer::CommitType;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use url::Url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w, --write            Run with writing the changes afterwards.
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr(format!($fmt));
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: bool,
    flag_version: bool,
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn user_repo_from_url(url: Url) -> Result<(String, String), String> {
    let path = match url.path() {
        Some(path) => path,
        None => return Err("URL should contain user and repository".into()),
    };

    let user = path[0].clone();
    let repo = match path[1].rfind(".git") {
        None => path[1].clone(),
        Some(suffix_pos) => {
            let valid_pos = path[1].len() - 4;
            if valid_pos == suffix_pos {
                let path = &path[1][0..suffix_pos];
                path.into()
            } else {
                return Err(".git suffix not at the end of URL".into())
            }
        }
    };

    Ok((user, repo))
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let is_dry_run = if ci_env_set() {
        false
    }
    else {
        !args.flag_write
    };

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    let _signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
        Err(e) => {
            logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
            logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
            process::exit(1);
        }
    };

    let remote_url = match repo.find_remote("origin") {
        Err(e) => print_exit!("Could not determine the origin remote url: {:?}", e),
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8");
            Url::parse(&url).expect("Remote URL can't be parsed")
        }
    };

    // TODO: Save those and re-use them later
    let (_user, _repo) = user_repo_from_url(remote_url)
        .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));

    let version = toml_file::read_from_file(repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(repository_path);
    if is_dry_run {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    else {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if is_dry_run {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    }
    else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        logger::stdout(format!("Writing Changelog"));
        changelog::write(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));

        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }

        logger::stdout("Package crate");
        if !cargo::package(repository_path) {
            print_exit!("`cargo package` failed. See above for the cargo error message.");
        }

        git::commit_files(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(repository_path, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        logger::stdout("Pushing new commit and tag");
        git::push(repository_path, &tag_name)
            .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

        logger::stdout("Creating GitHub release");
        github::release(&tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
    }
}
<commit_msg>feat: Fetch tokens<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;

use docopt::Docopt;
use commit_analyzer::CommitType;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use url::Url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w, --write            Run with writing the changes afterwards.
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr(format!($fmt));
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: bool,
    flag_version: bool,
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn user_repo_from_url(url: Url) -> Result<(String, String), String> {
    let path = match url.path() {
        Some(path) => path,
        None => return Err("URL should contain user and repository".into()),
    };

    let user = path[0].clone();
    let repo = match path[1].rfind(".git") {
        None => path[1].clone(),
        Some(suffix_pos) => {
            let valid_pos = path[1].len() - 4;
            if valid_pos == suffix_pos {
                let path = &path[1][0..suffix_pos];
                path.into()
            } else {
                return Err(".git suffix not at the end of URL".into())
            }
        }
    };

    Ok((user, repo))
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let is_dry_run = if ci_env_set() {
        false
    }
    else {
        !args.flag_write
    };

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    let _signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
        Err(e) => {
            logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
            logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
            process::exit(1);
        }
    };

    let remote_url = match repo.find_remote("origin") {
        Err(e) => print_exit!("Could not determine the origin remote url: {:?}", e),
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8");
            Url::parse(&url).expect("Remote URL can't be parsed")
        }
    };

    // TODO: Save those and re-use them later
    let (_user, _repo) = user_repo_from_url(remote_url)
        .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));

    let gh_token = env::var("GH_TOKEN")
        .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

    let travis_token = env::var("TRAVIS_TOKEN")
        .unwrap_or_else(|err| print_exit!("TRAVIS_TOKEN not set: {:?}", err));

    let version = toml_file::read_from_file(repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(repository_path);
    if is_dry_run {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    else {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if is_dry_run {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    }
    else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        logger::stdout(format!("Writing Changelog"));
        changelog::write(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));

        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }

        logger::stdout("Package crate");
        if !cargo::package(repository_path) {
            print_exit!("`cargo package` failed. See above for the cargo error message.");
        }

        git::commit_files(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(repository_path, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        logger::stdout("Pushing new commit and tag");
        git::push(repository_path, &tag_name)
            .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

        logger::stdout("Creating GitHub release");
        github::release(&tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
    }
}
<|endoftext|>