<commit_before>#[macro_use]
extern crate glium;

extern crate core;
extern crate cgmath;
extern crate rand;
extern crate num;
extern crate time;

mod world;
mod draw;

use core::convert::Into;
use world::terrain::Terrain;
use draw::shaders::ShaderManager;
use draw::display_object::{Drawable};
use cgmath::prelude::*;
use cgmath::{Matrix4,Point3,Vector3,Basis3, Deg, Euler, Quaternion};

fn main() {
	use glium::{DisplayBuild, Surface};
	// can not do multisampling due to glutin bug with_multisampling(16)
	let display = glium::glutin::WindowBuilder::new().with_vsync().with_multisampling(16).with_depth_buffer(24).build_glium().unwrap();

	// window reference
	let window = display.get_window().unwrap();

	// get window size
	let mut display_size = window.get_inner_size_pixels().unwrap();

	// hide the cursor
	window.set_cursor(glium::glutin::MouseCursor::NoneCursor);

	// grab cursor
	// TODO: cursor grabbing doesn't give me access to mouse events, so disabled for now and recentering the cursor instead
	//window.set_cursor_state(glium::glutin::CursorState::Grab).ok().expect("Could not grab mouse cursor.");
	window.set_cursor_position(display_size.0 as i32/2,display_size.1 as i32/2);

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

	// perspective_matrix
	let mut perspective_matrix:Matrix4<f32> = cgmath::perspective(cgmath::deg(45.0),display_size.0 as f32/display_size.1 as f32,0.0001,100.0);

	// view matrix
	let mut camera_position:Vector3<f32> = Vector3::new(0.0, 15.0, 0.0);
	let camera_target:Point3<f32> = Point3::new(0.0, 0.0, 0.0);
	let camera_up:Vector3<f32> = Vector3::unit_y();

	// setup the shaders
	let mut shader_manager = ShaderManager::new();

	// create terrain
	let terrain = Terrain::new(&display,&mut shader_manager,5);

    // listen for events produced in the window and wait to be received
    let mut old_time:u64 = time::precise_time_ns();
    let mut starting_time:u64 = old_time;
    let mut camera_rotation = 0f32;
    let mut camera_height = 1f32;
    // render loop
    loop {
    	let new_time = time::precise_time_ns();
    	let delta_time = new_time - old_time;
    	old_time = new_time;
    	let total_time = new_time - starting_time;
		let seconds_passed = total_time as f32/1600000000.0;

		// stores the camera rotation
		let rotation = Quaternion::from(Euler {
			x:Deg::new(camera_height),
			y:Deg::new(camera_rotation),
			z:Deg::new(0.0),
		});
		let rotated_position = rotation * camera_position;
    	let view_matrix:Matrix4<f32> = cgmath::Matrix4::look_at(Point3::<f32>::from((rotated_position.x,rotated_position.y,rotated_position.z)), camera_target, camera_up);

    	// get reference to the frame
    	let mut target = display.draw();

    	// clear the background
    	target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0),1.0);

    	// draw the terrain
    	let mvp_matrix = perspective_matrix * view_matrix;
    	terrain.draw(&mut target,&params,&mvp_matrix);

    	// finish drawing and destroy frame object
    	target.finish().unwrap();

    	// event loop
	    for ev in display.poll_events(){
	    	println!("{:?}", ev);
	    	match ev {
	    		glium::glutin::Event::Closed => return,
	    		glium::glutin::Event::Focused(focus) => {
	    			if focus {
	    				// TODO: regrabbing doesn't seems to work
	    				window.set_cursor_state(glium::glutin::CursorState::Grab).ok().expect("Could not grab mouse cursor.");
	    			}
	    		}
	    		glium::glutin::Event::Resized(_, _) => {
	    			display_size = window.get_inner_size_pixels().unwrap();
	    			perspective_matrix = cgmath::perspective(cgmath::deg(45.0),display_size.0 as f32/display_size.1 as f32,0.0001,100.0);
	    		},
	    		glium::glutin::Event::MouseMoved(x, y) => {
	    			let x = x - display_size.0 as i32/2;
	    			camera_rotation += x as f32 / 25f32;
	    			let y = y - display_size.1 as i32/2;
	    			camera_height += y as f32 / 25f32;
	    			if camera_height > 170.0 { camera_height = 170.0; }
	    			if camera_height < 1.0 { camera_height = 1.0; }
            	},
	    		_ => ()
	    	}
	    }

	    // reset the cursor position
	    window.set_cursor_position(display_size.0 as i32/2,display_size.1 as i32/2);
    }
}<commit_msg>Allow zooming<commit_after>#[macro_use]
extern crate glium;

extern crate core;
extern crate cgmath;
extern crate rand;
extern crate num;
extern crate time;

mod world;
mod draw;

use core::convert::Into;
use world::terrain::Terrain;
use draw::shaders::ShaderManager;
use draw::display_object::{Drawable};
use cgmath::prelude::*;
use cgmath::{Matrix4,Point3,Vector3,Basis3, Deg, Euler, Quaternion};

fn main() {
	use glium::{DisplayBuild, Surface};
	// can not do multisampling due to glutin bug with_multisampling(16)
	let display = glium::glutin::WindowBuilder::new().with_vsync().with_multisampling(16).with_depth_buffer(24).build_glium().unwrap();

	// window reference
	let window = display.get_window().unwrap();

	// get window size
	let mut display_size = window.get_inner_size_pixels().unwrap();

	// hide the cursor
	window.set_cursor(glium::glutin::MouseCursor::NoneCursor);

	// grab cursor
	// TODO: cursor grabbing doesn't give me access to mouse events, so disabled for now and recentering the cursor instead
	//window.set_cursor_state(glium::glutin::CursorState::Grab).ok().expect("Could not grab mouse cursor.");
	window.set_cursor_position(display_size.0 as i32/2,display_size.1 as i32/2);

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

	// perspective_matrix
	let mut perspective_matrix:Matrix4<f32> = cgmath::perspective(cgmath::deg(45.0),display_size.0 as f32/display_size.1 as f32,0.0001,100.0);

	// view matrix
    let mut camera_rotation = 0f32;
    let mut camera_height = 1f32;

	let mut camera_position:Vector3<f32> = Vector3::new(0.0, 15.0, 0.0);
	let camera_target:Point3<f32> = Point3::new(0.0, 0.0, 0.0);
	let camera_up:Vector3<f32> = Vector3::unit_y();

	// setup the shaders
	let mut shader_manager = ShaderManager::new();

	// create terrain
	let terrain = Terrain::new(&display,&mut shader_manager,5);

    // listen for events produced in the window and wait to be received
    let mut old_time:u64 = time::precise_time_ns();
    let mut starting_time:u64 = old_time;
    // render loop
    loop {
    	let new_time = time::precise_time_ns();
    	let delta_time = new_time - old_time;
    	old_time = new_time;
    	let total_time = new_time - starting_time;
		let seconds_passed = total_time as f32/1600000000.0;

		// stores the camera rotation
		let rotation = Quaternion::from(Euler {
			x:Deg::new(camera_height),
			y:Deg::new(camera_rotation),
			z:Deg::new(0.0),
		});
		let rotated_position = rotation * camera_position;
    	let view_matrix:Matrix4<f32> = cgmath::Matrix4::look_at(Point3::<f32>::from((rotated_position.x,rotated_position.y,rotated_position.z)), camera_target, camera_up);

    	// get reference to the frame
    	let mut target = display.draw();

    	// clear the background
    	target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0),1.0);

    	// draw the terrain
    	let mvp_matrix = perspective_matrix * view_matrix;
    	terrain.draw(&mut target,&params,&mvp_matrix);

    	// finish drawing and destroy frame object
    	target.finish().unwrap();

    	// event loop
	    for ev in display.poll_events(){
	    	//println!("{:?}", ev);
	    	match ev {
	    		glium::glutin::Event::Closed => return,
	    		glium::glutin::Event::Focused(focus) => {
	    			if focus {
	    				// TODO: regrabbing doesn't seems to work
	    				window.set_cursor_state(glium::glutin::CursorState::Grab).ok().expect("Could not grab mouse cursor.");
	    			}
	    		}
	    		glium::glutin::Event::Resized(_, _) => {
	    			display_size = window.get_inner_size_pixels().unwrap();
	    			perspective_matrix = cgmath::perspective(cgmath::deg(45.0),display_size.0 as f32/display_size.1 as f32,0.0001,100.0);
	    		},
	    		glium::glutin::Event::MouseMoved(x, y) => {
	    			let x = x - display_size.0 as i32/2;
	    			camera_rotation += x as f32 / 25f32;
	    			let y = y - display_size.1 as i32/2;
	    			camera_height += y as f32 / 25f32;
	    			if camera_height > 170.0 { camera_height = 170.0; }
	    			if camera_height < 1.0 { camera_height = 1.0; }
            	},
            	glium::glutin::Event::MouseWheel(delta, _) => {
            		match delta {
            			glium::glutin::MouseScrollDelta::LineDelta(x, y) => {
            				camera_position.y -= y * 3.0;
            				if camera_position.y < 5.0 {
            					camera_position.y = 5.0;
            				}
            			}
            			_ => ()
            		}
            	},
	    		_ => ()
	    	}
	    }

	    // reset the cursor position
	    window.set_cursor_position(display_size.0 as i32/2,display_size.1 as i32/2);
    }
}<|endoftext|>


<commit_before>#[macro_use]
extern crate futures;
extern crate tokio_core;

use std::env;
use std::net::SocketAddr;

use futures::{Async, Poll, Future};
use futures::stream::Stream;
use tokio_core::io::{read_exact, Io};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;

struct ReadStream<R> where R: ::std::io::Read {
    reader: R,
    buffer: [u8; 256],
    pos: usize,
    frame_end: u8,
}

impl <R> Stream for ReadStream<R> where R: ::std::io::Read {
    type Item = Vec<u8>;
    type Error = ::std::io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        unimplemented!()
    }
}

pub fn main() {
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let mut l = Core::new().unwrap();
    let handle = l.handle();

    // Create a TCP listener which will listen for incoming connections
    let socket = TcpListener::bind(&addr, &handle).unwrap();

    // Once we've got the TCP listener, inform that we have it
    println!("Listening on: {}", addr);

    let done = socket.incoming().for_each(move |(socket, addr)| {
        // what's the spec?
        // first byte: 0 means publisher, 1 means subscriber.

        let mut header = [0u8; 1];
        let future = read_exact(socket, header).and_then(|(socket, header)| {
            match header[0] {
                0 => {
                    // publisher
                    // hm... is this framing going to be tricky?
                    // When done receiving message, write back the number received,
                    // as a little-endian u64.
                }
                1 => {
                    //subscriber
                }
                _ => {
                    // error
                }
            }

            println!("OK {:?}", header);
            Ok(())
        }).map_err(|e| {
            println!("error: {}", e);
        });
        handle.spawn(future);

        // frame format: first byte is length of body. Then there is body.
        Ok(())
    });

    l.run(done).unwrap();
}
<commit_msg>fill in more ReadStream stuff<commit_after>#[macro_use]
extern crate futures;

#[macro_use]
extern crate tokio_core;

use std::env;
use std::net::SocketAddr;

use futures::{Async, Poll, Future};
use futures::stream::Stream;
use tokio_core::io::{read_exact};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;

struct ReadStream<R> where R: ::std::io::Read {
    reader: R,
    buffer: Vec<u8>,
    pos: usize,
    frame_end: Option<u8>,
}

impl <R> ReadStream<R> where R: ::std::io::Read {
    fn new(reader: R) -> ReadStream<R> {
        ReadStream {
            reader: reader,
            buffer: Vec::new(),
            pos: 0,
            frame_end: None,
        }
    }
}

impl <R> Stream for ReadStream<R> where R: ::std::io::Read {
    type Item = Vec<u8>;
    type Error = ::std::io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            if let Some(frame_end) = self.frame_end {
                let n = try_nb!(self.reader.read(&mut self.buffer[self.pos..frame_end as usize]));
                self.pos += n;
                if self.pos == frame_end as usize {
                    self.pos = 0;
                    let result = ::std::mem::replace(&mut self.buffer, Vec::new());
                    return Ok(Async::Ready(Some(result)))
                }
            } else {
                let mut buf = [0u8];
                let n = try_nb!(self.reader.read(&mut buf));
                if n == 0 { // EOF
                    return Ok(Async::Ready(None))
                }

                self.frame_end = Some(buf[0]);
                self.buffer = vec![0; buf[0] as usize];
            }
        }
    }
}

pub fn main() {
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let mut l = Core::new().unwrap();
    let handle = l.handle();

    // Create a TCP listener which will listen for incoming connections
    let socket = TcpListener::bind(&addr, &handle).unwrap();

    // Once we've got the TCP listener, inform that we have it
    println!("Listening on: {}", addr);

    let done = socket.incoming().for_each(move |(socket, _addr)| {
        // what's the spec?
        // first byte: 0 means publisher, 1 means subscriber.

        let header = [0u8; 1];
        let future = read_exact(socket, header).and_then(|(socket, header)| {
            println!("OK {:?}", header);
            match header[0] {
                0 => {
                    // publisher
                    let done = ReadStream::new(socket).for_each(|buf| {
                        println!("buf {:?}", buf);
                        Ok(())
                    });

                    // When this stream is done, I want the socket back.
                    done

                    // When done receiving message, write back the number received,
                    // as a little-endian u64.
                }
                1 => {
                    //subscriber
                    unimplemented!()
                }
                _ => {
                    // error
                    unimplemented!()
                }
            }
        }).map_err(|e| {
            println!("error: {}", e);
        });
        handle.spawn(future);

        // frame format: first byte is length of body. Then there is body.
        Ok(())
    });

    l.run(done).unwrap();
}
<|endoftext|>


<commit_before>extern crate actix_web;
extern crate cookie;
extern crate get_if_addrs;
extern crate hyper;
extern crate itertools;
extern crate json;
extern crate log4rs;
#[macro_use]
extern crate log;
extern crate mustache;
extern crate reqwest_mock;
#[macro_use]
extern crate serde_derive;
extern crate time;
extern crate url;
#[macro_use]
extern crate lazy_static;

use actix_web::{http, http::Method, server, test, App, HttpMessage, HttpRequest, HttpResponse};
use std::env;

mod arrivals;
mod common;
mod favourite;
mod id;
mod nearby;
mod search;

fn root_handler(request: HttpRequest) -> HttpResponse {
    let data = common::mustache_favourites(&request);
    common::render_to_response("resources/templates/root.mustache", &data)
}

fn app() -> App {
    return App::new()
        .route("/", Method::GET, root_handler)
        .route("/search", Method::GET, search::search_handler)
        .route("/id/{id}", Method::GET, id::id_handler)
        .route("/nearby", Method::GET, nearby::nearby_handler)
        .route("/favourites", Method::GET, favourite::list_favourites)
        .route("/favourites", Method::POST, favourite::add_favourite)
        .route("/favourite-remove", Method::POST, favourite::remove_favourite)
        .route("/arrivals/{stopid}", Method::GET, arrivals::arrivals_handler);
}

fn main() {
    log4rs::init_file("log.yaml", Default::default()).unwrap();
    let port = env::var("PORT")
        .unwrap_or("8000".to_string())
        .parse::<u16>()
        .unwrap();
    let mut server = server::new(|| app().finish());
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        let ip = iface.ip();
        info!("Listening on {}:{} for {}", ip, port, iface.name);
        server = server.bind((ip, port)).unwrap();
    }
    server.run();
}

#[test]
fn simple_search() {
    let mut srv = test::TestServer::with_factory(app);
    common::set_client(common::ClientType::TESTING);
    let request = srv
        .client(http::Method::GET, "/search?query=foo")
        .finish()
        .unwrap();
    let response = srv.execute(request.send()).unwrap();
    let body: String = String::from_utf8(srv.execute(response.body()).unwrap().to_vec()).unwrap();
    assert!(body.find("<title>Search: foo</title>").is_some());
    assert!(response.status().is_success());
}
<commit_msg>Provide better output on failure<commit_after>extern crate actix_web;
extern crate cookie;
extern crate get_if_addrs;
extern crate hyper;
extern crate itertools;
extern crate json;
extern crate log4rs;
#[macro_use]
extern crate log;
extern crate mustache;
extern crate reqwest_mock;
#[macro_use]
extern crate serde_derive;
extern crate time;
extern crate url;
#[macro_use]
extern crate lazy_static;

use actix_web::{http, http::Method, server, test, App, HttpMessage, HttpRequest, HttpResponse};
use std::env;

mod arrivals;
mod common;
mod favourite;
mod id;
mod nearby;
mod search;

fn root_handler(request: HttpRequest) -> HttpResponse {
    let data = common::mustache_favourites(&request);
    common::render_to_response("resources/templates/root.mustache", &data)
}

fn app() -> App {
    return App::new()
        .route("/", Method::GET, root_handler)
        .route("/search", Method::GET, search::search_handler)
        .route("/id/{id}", Method::GET, id::id_handler)
        .route("/nearby", Method::GET, nearby::nearby_handler)
        .route("/favourites", Method::GET, favourite::list_favourites)
        .route("/favourites", Method::POST, favourite::add_favourite)
        .route("/favourite-remove", Method::POST, favourite::remove_favourite)
        .route("/arrivals/{stopid}", Method::GET, arrivals::arrivals_handler);
}

fn main() {
    log4rs::init_file("log.yaml", Default::default()).unwrap();
    let port = env::var("PORT")
        .unwrap_or("8000".to_string())
        .parse::<u16>()
        .unwrap();
    let mut server = server::new(|| app().finish());
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        let ip = iface.ip();
        info!("Listening on {}:{} for {}", ip, port, iface.name);
        server = server.bind((ip, port)).unwrap();
    }
    server.run();
}

#[test]
fn simple_search() {
    let mut srv = test::TestServer::with_factory(app);
    common::set_client(common::ClientType::TESTING);
    let request = srv
        .client(http::Method::GET, "/search?query=foo")
        .finish()
        .unwrap();
    let response = srv.execute(request.send()).unwrap();
    let body: String = String::from_utf8(srv.execute(response.body()).unwrap().to_vec()).unwrap();
    assert!(body.find("<title>Search: foo</title>").is_some(), body);
    assert!(response.status().is_success(), response.status());
}
<|endoftext|>


<commit_before>#![allow(
    // We support older compilers
    clippy::manual_strip,
    clippy::match_like_matches_macro,
)]

mod cmd;
mod config;
mod edit;
mod error;
mod fmt;
mod opts;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::io::{self, BufRead, Write};
use std::path::{Path, PathBuf};
use std::process::{self, Command, Stdio};
use std::str::FromStr;

use atty::Stream::{Stderr, Stdout};
use bat::assets::HighlightingAssets;
use bat::PrettyPrinter;
use quote::quote;
use structopt::StructOpt;
use termcolor::{Color::Green, ColorChoice, ColorSpec, StandardStream, WriteColor};

use crate::cmd::Line;
use crate::config::Config;
use crate::error::Result;
use crate::opts::Coloring::*;
use crate::opts::{Args, Coloring, Opts};

fn main() {
    let result = cargo_expand_or_run_nightly();
    process::exit(match result {
        Ok(code) => code,
        Err(err) => {
            let _ = writeln!(io::stderr(), "{}", err);
            1
        }
    });
}

fn cargo_expand_or_run_nightly() -> Result<i32> {
    const NO_RUN_NIGHTLY: &str = "CARGO_EXPAND_NO_RUN_NIGHTLY";

    if env::var_os(NO_RUN_NIGHTLY).is_some() || maybe_nightly() || !can_run_plus_nightly() {
        return cargo_expand();
    }

    let mut nightly = Command::new("cargo");
    nightly.arg("+nightly");
    nightly.arg("expand");

    let mut args = env::args_os().peekable();
    args.next().unwrap(); // cargo
    if args.peek().map_or(false, |arg| arg == "expand") {
        args.next().unwrap(); // expand
    }
    nightly.args(args);

    // Hopefully prevent infinite re-run loop.
    nightly.env(NO_RUN_NIGHTLY, "");

    let status = nightly.status()?;

    Ok(match status.code() {
        Some(code) => code,
        None => {
            if status.success() {
                0
            } else {
                1
            }
        }
    })
}

fn maybe_nightly() -> bool {
    !definitely_not_nightly()
}

fn definitely_not_nightly() -> bool {
    let mut cmd = Command::new(cargo_binary());
    cmd.arg("--version");

    let output = match cmd.output() {
        Ok(output) => output,
        Err(_) => return false,
    };

    let version = match String::from_utf8(output.stdout) {
        Ok(version) => version,
        Err(_) => return false,
    };

    version.starts_with("cargo 1") && !version.contains("nightly")
}

fn can_run_plus_nightly() -> bool {
    Command::new("cargo")
        .arg("+nightly")
        .arg("--version")
        .output()
        .map_or(false, |output| output.status.success())
}

fn cargo_binary() -> OsString {
    env::var_os("CARGO").unwrap_or_else(|| "cargo".to_owned().into())
}

fn cargo_expand() -> Result<i32> {
    let Opts::Expand(args) = Opts::from_args();
    let config = config::deserialize();

    if args.themes {
        for theme in HighlightingAssets::from_binary().themes() {
            let _ = writeln!(io::stdout(), "{}", theme);
        }
        return Ok(0);
    }

    let rustfmt;
    match (&args.item, args.ugly) {
        (Some(item), true) => {
            let _ = writeln!(
                io::stderr(),
                "ERROR: cannot expand single item ({}) in ugly mode.",
                item,
            );
            return Ok(1);
        }
        (Some(item), false) => {
            rustfmt = which_rustfmt();
            if rustfmt.is_none() {
                let _ = writeln!(
                    io::stderr(),
                    "ERROR: cannot expand single item ({}) without rustfmt.",
                    item,
                );
                let _ = writeln!(
                    io::stderr(),
                    "Install rustfmt by running `rustup component add rustfmt --toolchain nightly`.",
                );
                return Ok(1);
            }
        }
        (None, true) => rustfmt = None,
        (None, false) => rustfmt = which_rustfmt(),
    }

    let mut builder = tempfile::Builder::new();
    builder.prefix("cargo-expand");
    let outdir = builder.tempdir().expect("failed to create tmp file");
    let outfile_path = outdir.path().join("expanded");
    let color = get_color(&args, &config);

    // Run cargo
    let mut cmd = Command::new(cargo_binary());
    apply_args(&mut cmd, &args, &color, &outfile_path);
    let code = filter_err(&mut cmd, ignore_cargo_err)?;

    if !outfile_path.exists() {
        return Ok(1);
    }

    let mut content = fs::read_to_string(&outfile_path)?;
    if content.is_empty() {
        let _ = writeln!(io::stderr(), "ERROR: rustc produced no expanded output");
        return Ok(if code == 0 { 1 } else { code });
    }

    // Run rustfmt
    if let Some(rustfmt) = rustfmt {
        // Work around rustfmt not being able to parse paths containing $crate.
        // This placeholder should be the same width as $crate to preserve
        // alignments.
        const DOLLAR_CRATE_PLACEHOLDER: &str = "Ξcrate";
        content = content.replace("$crate", DOLLAR_CRATE_PLACEHOLDER);

        // Support cargo-expand built with panic=abort, as otherwise proc-macro2
        // ends up using a catch_unwind.
        proc_macro2::fallback::force();

        // Discard comments, which are misplaced by the compiler
        if let Ok(mut syntax_tree) = syn::parse_file(&content) {
            edit::sanitize(&mut syntax_tree);
            if let Some(filter) = args.item {
                syntax_tree.shebang = None;
                syntax_tree.attrs.clear();
                syntax_tree.items = filter.apply_to(&syntax_tree);
                if syntax_tree.items.is_empty() {
                    let _ = writeln!(io::stderr(), "WARNING: no such item: {}", filter);
                    return Ok(1);
                }
            }
            content = quote!(#syntax_tree).to_string();
        }
        fs::write(&outfile_path, content)?;

        fmt::write_rustfmt_config(&outdir)?;

        let output = Command::new(&rustfmt)
            .arg("--edition=2018")
            .arg(&outfile_path)
            .stderr(Stdio::null())
            .output();
        if let Ok(output) = output {
            if !output.status.success() {
                // Probably was the wrong edition.
                let _status = Command::new(&rustfmt)
                    .arg("--edition=2015")
                    .arg(&outfile_path)
                    .stderr(Stdio::null())
                    .status();
            }
        }

        content = fs::read_to_string(&outfile_path)?;
        content = content.replace(DOLLAR_CRATE_PLACEHOLDER, "$crate");
    }

    // Run pretty printer
    let theme = args.theme.or(config.theme);
    let none_theme = theme.as_deref() == Some("none");
    let do_color = match color {
        Always => true,
        Never => false,
        Auto => !none_theme && atty::is(Stdout),
    };
    let _ = writeln!(io::stderr());
    if do_color {
        let mut pretty_printer = PrettyPrinter::new();
        pretty_printer
            .input_from_bytes(content.as_bytes())
            .language("rust")
            .tab_width(Some(4))
            .true_color(false)
            .header(false)
            .line_numbers(false)
            .grid(false);
        if let Some(theme) = theme {
            pretty_printer.theme(theme);
        }

        // Ignore any errors.
        let _ = pretty_printer.print();
    } else {
        let _ = write!(io::stdout(), "{}", content);
    }

    Ok(0)
}

fn which_rustfmt() -> Option<PathBuf> {
    match env::var_os("RUSTFMT") {
        Some(which) => {
            if which.is_empty() {
                None
            } else {
                Some(PathBuf::from(which))
            }
        }
        None => toolchain_find::find_installed_component("rustfmt"),
    }
}

// Based on https://github.com/rsolomo/cargo-check
fn apply_args(cmd: &mut Command, args: &Args, color: &Coloring, outfile: &Path) {
    let mut line = Line::new("cargo");

    line.arg("rustc");

    if args.tests && args.test.is_none() {
        line.arg("--profile=test");
    } else {
        line.arg("--profile=check");
    }

    if args.release {
        line.arg("--release");
    }

    if let Some(features) = &args.features {
        line.arg("--features");
        line.arg(features);
    }

    if args.all_features {
        line.arg("--all-features");
    }

    if args.no_default_features {
        line.arg("--no-default-features");
    }

    if args.lib {
        line.arg("--lib");
    }

    if let Some(bin) = &args.bin {
        line.arg("--bin");
        line.arg(bin);
    }

    if let Some(example) = &args.example {
        line.arg("--example");
        line.arg(example);
    }

    if let Some(test) = &args.test {
        line.arg("--test");
        line.arg(test);
    }

    if let Some(bench) = &args.bench {
        line.arg("--bench");
        line.arg(bench);
    }

    if let Some(target) = &args.target {
        line.arg("--target");
        line.arg(target);
    }

    if let Some(target_dir) = &args.target_dir {
        line.arg("--target-dir");
        line.arg(target_dir);
    }

    if let Some(manifest_path) = &args.manifest_path {
        line.arg("--manifest-path");
        line.arg(manifest_path);
    }

    if let Some(package) = &args.package {
        line.arg("--package");
        line.arg(package);
    }

    if let Some(jobs) = args.jobs {
        line.arg("--jobs");
        line.arg(jobs.to_string());
    }

    if args.verbose {
        line.arg("--verbose");
    }

    line.arg("--color");
    match color {
        Coloring::Auto => line.arg(if atty::is(Stderr) { "always" } else { "never" }),
        color => line.arg(color.to_string()),
    }

    if args.frozen {
        line.arg("--frozen");
    }

    if args.locked {
        line.arg("--locked");
    }

    for unstable_flag in &args.unstable_flags {
        line.arg("-Z");
        line.arg(unstable_flag);
    }

    line.arg("--");

    line.arg("-o");
    line.arg(outfile);
    line.arg("-Zunstable-options");
    line.arg("--pretty=expanded");

    if args.verbose {
        let mut display = line.clone();
        display.insert(0, "+nightly");
        print_command(display, color);
    }

    cmd.args(line);
}

fn print_command(line: Line, color: &Coloring) {
    let color_choice = match color {
        Coloring::Auto => ColorChoice::Auto,
        Coloring::Always => ColorChoice::Always,
        Coloring::Never => ColorChoice::Never,
    };

    let mut stream = StandardStream::stderr(color_choice);
    let _ = stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Green)));
    let _ = write!(stream, "{:>12}", "Running");
    let _ = stream.reset();
    let _ = writeln!(stream, " `{}`", line);
}

fn filter_err(cmd: &mut Command, ignore: fn(&str) -> bool) -> io::Result<i32> {
    let mut child = cmd.stderr(Stdio::piped()).spawn()?;
    let mut stderr = io::BufReader::new(child.stderr.take().unwrap());
    let mut line = String::new();
    while let Ok(n) = stderr.read_line(&mut line) {
        if n == 0 {
            break;
        }
        if !ignore(&line) {
            let _ = write!(io::stderr(), "{}", line);
        }
        line.clear();
    }
    let code = child.wait()?.code().unwrap_or(1);
    Ok(code)
}

fn ignore_cargo_err(line: &str) -> bool {
    if line.trim().is_empty() {
        return true;
    }

    let discarded_lines = [
        "ignoring specified output filename because multiple outputs were \
         requested",
        "ignoring specified output filename for 'link' output because multiple \
         outputs were requested",
        "ignoring --out-dir flag due to -o flag",
        "ignoring -C extra-filename flag due to -o flag",
        "due to multiple output types requested, the explicitly specified \
         output file name will be adapted for each output type",
        "warning emitted",
        "warnings emitted",
    ];
    for s in &discarded_lines {
        if line.contains(s) {
            return true;
        }
    }

    false
}

fn get_color(args: &Args, config: &Config) -> Coloring {
    if let Some(value) = args.color {
        return value;
    }

    if let Some(value) = config.color.as_ref() {
        match Coloring::from_str(value.as_str()) {
            Ok(color) => return color,
            Err(err) => {
                let _ = writeln!(
                    io::stderr(),
                    "WARNING: invalid color in cargo config: {}",
                    err
                );
            }
        }
    }

    Coloring::Auto // default
}
<commit_msg>Color never by default on windows<commit_after>#![allow(
    // We support older compilers
    clippy::manual_strip,
    clippy::match_like_matches_macro,
)]

mod cmd;
mod config;
mod edit;
mod error;
mod fmt;
mod opts;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::io::{self, BufRead, Write};
use std::path::{Path, PathBuf};
use std::process::{self, Command, Stdio};
use std::str::FromStr;

use atty::Stream::{Stderr, Stdout};
use bat::assets::HighlightingAssets;
use bat::PrettyPrinter;
use quote::quote;
use structopt::StructOpt;
use termcolor::{Color::Green, ColorChoice, ColorSpec, StandardStream, WriteColor};

use crate::cmd::Line;
use crate::config::Config;
use crate::error::Result;
use crate::opts::Coloring::*;
use crate::opts::{Args, Coloring, Opts};

fn main() {
    let result = cargo_expand_or_run_nightly();
    process::exit(match result {
        Ok(code) => code,
        Err(err) => {
            let _ = writeln!(io::stderr(), "{}", err);
            1
        }
    });
}

fn cargo_expand_or_run_nightly() -> Result<i32> {
    const NO_RUN_NIGHTLY: &str = "CARGO_EXPAND_NO_RUN_NIGHTLY";

    if env::var_os(NO_RUN_NIGHTLY).is_some() || maybe_nightly() || !can_run_plus_nightly() {
        return cargo_expand();
    }

    let mut nightly = Command::new("cargo");
    nightly.arg("+nightly");
    nightly.arg("expand");

    let mut args = env::args_os().peekable();
    args.next().unwrap(); // cargo
    if args.peek().map_or(false, |arg| arg == "expand") {
        args.next().unwrap(); // expand
    }
    nightly.args(args);

    // Hopefully prevent infinite re-run loop.
    nightly.env(NO_RUN_NIGHTLY, "");

    let status = nightly.status()?;

    Ok(match status.code() {
        Some(code) => code,
        None => {
            if status.success() {
                0
            } else {
                1
            }
        }
    })
}

fn maybe_nightly() -> bool {
    !definitely_not_nightly()
}

fn definitely_not_nightly() -> bool {
    let mut cmd = Command::new(cargo_binary());
    cmd.arg("--version");

    let output = match cmd.output() {
        Ok(output) => output,
        Err(_) => return false,
    };

    let version = match String::from_utf8(output.stdout) {
        Ok(version) => version,
        Err(_) => return false,
    };

    version.starts_with("cargo 1") && !version.contains("nightly")
}

fn can_run_plus_nightly() -> bool {
    Command::new("cargo")
        .arg("+nightly")
        .arg("--version")
        .output()
        .map_or(false, |output| output.status.success())
}

fn cargo_binary() -> OsString {
    env::var_os("CARGO").unwrap_or_else(|| "cargo".to_owned().into())
}

fn cargo_expand() -> Result<i32> {
    let Opts::Expand(args) = Opts::from_args();
    let config = config::deserialize();

    if args.themes {
        for theme in HighlightingAssets::from_binary().themes() {
            let _ = writeln!(io::stdout(), "{}", theme);
        }
        return Ok(0);
    }

    let rustfmt;
    match (&args.item, args.ugly) {
        (Some(item), true) => {
            let _ = writeln!(
                io::stderr(),
                "ERROR: cannot expand single item ({}) in ugly mode.",
                item,
            );
            return Ok(1);
        }
        (Some(item), false) => {
            rustfmt = which_rustfmt();
            if rustfmt.is_none() {
                let _ = writeln!(
                    io::stderr(),
                    "ERROR: cannot expand single item ({}) without rustfmt.",
                    item,
                );
                let _ = writeln!(
                    io::stderr(),
                    "Install rustfmt by running `rustup component add rustfmt --toolchain nightly`.",
                );
                return Ok(1);
            }
        }
        (None, true) => rustfmt = None,
        (None, false) => rustfmt = which_rustfmt(),
    }

    let mut builder = tempfile::Builder::new();
    builder.prefix("cargo-expand");
    let outdir = builder.tempdir().expect("failed to create tmp file");
    let outfile_path = outdir.path().join("expanded");
    let color = get_color(&args, &config);

    // Run cargo
    let mut cmd = Command::new(cargo_binary());
    apply_args(&mut cmd, &args, &color, &outfile_path);
    let code = filter_err(&mut cmd, ignore_cargo_err)?;

    if !outfile_path.exists() {
        return Ok(1);
    }

    let mut content = fs::read_to_string(&outfile_path)?;
    if content.is_empty() {
        let _ = writeln!(io::stderr(), "ERROR: rustc produced no expanded output");
        return Ok(if code == 0 { 1 } else { code });
    }

    // Run rustfmt
    if let Some(rustfmt) = rustfmt {
        // Work around rustfmt not being able to parse paths containing $crate.
        // This placeholder should be the same width as $crate to preserve
        // alignments.
        const DOLLAR_CRATE_PLACEHOLDER: &str = "Ξcrate";
        content = content.replace("$crate", DOLLAR_CRATE_PLACEHOLDER);

        // Support cargo-expand built with panic=abort, as otherwise proc-macro2
        // ends up using a catch_unwind.
        proc_macro2::fallback::force();

        // Discard comments, which are misplaced by the compiler
        if let Ok(mut syntax_tree) = syn::parse_file(&content) {
            edit::sanitize(&mut syntax_tree);
            if let Some(filter) = args.item {
                syntax_tree.shebang = None;
                syntax_tree.attrs.clear();
                syntax_tree.items = filter.apply_to(&syntax_tree);
                if syntax_tree.items.is_empty() {
                    let _ = writeln!(io::stderr(), "WARNING: no such item: {}", filter);
                    return Ok(1);
                }
            }
            content = quote!(#syntax_tree).to_string();
        }
        fs::write(&outfile_path, content)?;

        fmt::write_rustfmt_config(&outdir)?;

        let output = Command::new(&rustfmt)
            .arg("--edition=2018")
            .arg(&outfile_path)
            .stderr(Stdio::null())
            .output();
        if let Ok(output) = output {
            if !output.status.success() {
                // Probably was the wrong edition.
                let _status = Command::new(&rustfmt)
                    .arg("--edition=2015")
                    .arg(&outfile_path)
                    .stderr(Stdio::null())
                    .status();
            }
        }

        content = fs::read_to_string(&outfile_path)?;
        content = content.replace(DOLLAR_CRATE_PLACEHOLDER, "$crate");
    }

    // Run pretty printer
    let theme = args.theme.or(config.theme);
    let none_theme = theme.as_deref() == Some("none");
    let do_color = match color {
        Always => true,
        Never => false,
        Auto => !none_theme && atty::is(Stdout),
    };
    let _ = writeln!(io::stderr());
    if do_color {
        let mut pretty_printer = PrettyPrinter::new();
        pretty_printer
            .input_from_bytes(content.as_bytes())
            .language("rust")
            .tab_width(Some(4))
            .true_color(false)
            .header(false)
            .line_numbers(false)
            .grid(false);
        if let Some(theme) = theme {
            pretty_printer.theme(theme);
        }

        // Ignore any errors.
        let _ = pretty_printer.print();
    } else {
        let _ = write!(io::stdout(), "{}", content);
    }

    Ok(0)
}

fn which_rustfmt() -> Option<PathBuf> {
    match env::var_os("RUSTFMT") {
        Some(which) => {
            if which.is_empty() {
                None
            } else {
                Some(PathBuf::from(which))
            }
        }
        None => toolchain_find::find_installed_component("rustfmt"),
    }
}

// Based on https://github.com/rsolomo/cargo-check
fn apply_args(cmd: &mut Command, args: &Args, color: &Coloring, outfile: &Path) {
    let mut line = Line::new("cargo");

    line.arg("rustc");

    if args.tests && args.test.is_none() {
        line.arg("--profile=test");
    } else {
        line.arg("--profile=check");
    }

    if args.release {
        line.arg("--release");
    }

    if let Some(features) = &args.features {
        line.arg("--features");
        line.arg(features);
    }

    if args.all_features {
        line.arg("--all-features");
    }

    if args.no_default_features {
        line.arg("--no-default-features");
    }

    if args.lib {
        line.arg("--lib");
    }

    if let Some(bin) = &args.bin {
        line.arg("--bin");
        line.arg(bin);
    }

    if let Some(example) = &args.example {
        line.arg("--example");
        line.arg(example);
    }

    if let Some(test) = &args.test {
        line.arg("--test");
        line.arg(test);
    }

    if let Some(bench) = &args.bench {
        line.arg("--bench");
        line.arg(bench);
    }

    if let Some(target) = &args.target {
        line.arg("--target");
        line.arg(target);
    }

    if let Some(target_dir) = &args.target_dir {
        line.arg("--target-dir");
        line.arg(target_dir);
    }

    if let Some(manifest_path) = &args.manifest_path {
        line.arg("--manifest-path");
        line.arg(manifest_path);
    }

    if let Some(package) = &args.package {
        line.arg("--package");
        line.arg(package);
    }

    if let Some(jobs) = args.jobs {
        line.arg("--jobs");
        line.arg(jobs.to_string());
    }

    if args.verbose {
        line.arg("--verbose");
    }

    line.arg("--color");
    match color {
        Coloring::Auto => line.arg(if cfg!(not(windows)) && atty::is(Stderr) {
            "always"
        } else {
            "never"
        }),
        color => line.arg(color.to_string()),
    }

    if args.frozen {
        line.arg("--frozen");
    }

    if args.locked {
        line.arg("--locked");
    }

    for unstable_flag in &args.unstable_flags {
        line.arg("-Z");
        line.arg(unstable_flag);
    }

    line.arg("--");

    line.arg("-o");
    line.arg(outfile);
    line.arg("-Zunstable-options");
    line.arg("--pretty=expanded");

    if args.verbose {
        let mut display = line.clone();
        display.insert(0, "+nightly");
        print_command(display, color);
    }

    cmd.args(line);
}

fn print_command(line: Line, color: &Coloring) {
    let color_choice = match color {
        Coloring::Auto => ColorChoice::Auto,
        Coloring::Always => ColorChoice::Always,
        Coloring::Never => ColorChoice::Never,
    };

    let mut stream = StandardStream::stderr(color_choice);
    let _ = stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Green)));
    let _ = write!(stream, "{:>12}", "Running");
    let _ = stream.reset();
    let _ = writeln!(stream, " `{}`", line);
}

fn filter_err(cmd: &mut Command, ignore: fn(&str) -> bool) -> io::Result<i32> {
    let mut child = cmd.stderr(Stdio::piped()).spawn()?;
    let mut stderr = io::BufReader::new(child.stderr.take().unwrap());
    let mut line = String::new();
    while let Ok(n) = stderr.read_line(&mut line) {
        if n == 0 {
            break;
        }
        if !ignore(&line) {
            let _ = write!(io::stderr(), "{}", line);
        }
        line.clear();
    }
    let code = child.wait()?.code().unwrap_or(1);
    Ok(code)
}

fn ignore_cargo_err(line: &str) -> bool {
    if line.trim().is_empty() {
        return true;
    }

    let discarded_lines = [
        "ignoring specified output filename because multiple outputs were \
         requested",
        "ignoring specified output filename for 'link' output because multiple \
         outputs were requested",
        "ignoring --out-dir flag due to -o flag",
        "ignoring -C extra-filename flag due to -o flag",
        "due to multiple output types requested, the explicitly specified \
         output file name will be adapted for each output type",
        "warning emitted",
        "warnings emitted",
    ];
    for s in &discarded_lines {
        if line.contains(s) {
            return true;
        }
    }

    false
}

fn get_color(args: &Args, config: &Config) -> Coloring {
    if let Some(value) = args.color {
        return value;
    }

    if let Some(value) = config.color.as_ref() {
        match Coloring::from_str(value.as_str()) {
            Ok(color) => return color,
            Err(err) => {
                let _ = writeln!(
                    io::stderr(),
                    "WARNING: invalid color in cargo config: {}",
                    err
                );
            }
        }
    }

    Coloring::Auto // default
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, ArgGroup, App};

extern crate isbfc;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
            .short("S")
            .help("Assemble but do not link"))
        .arg(Arg::with_name("dump_ir")
            .long("dumpir")
            .help("Dump intermediate representation; for debugging"))
        .group(ArgGroup::with_name("actions").args(&["output_asm", "dump_ir"]))
        .arg(Arg::with_name("debugging_symbols")
            .short("g")
            .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
            .short("o")
            .help("Output file name")
            .takes_value(true)
            .empty_values(false)
            .value_name("file"))
        .arg(Arg::with_name("tape_size")
            .long("tape-size")
            .help("Size of tape; defaults to 8192")
            .takes_value(true)
            .empty_values(false)
            .value_name("bytes"))
        .arg(Arg::with_name("level")
             .short("O")
             .help("Optimization level")
             .takes_value(true)
             .default_value("1"))
        .arg(Arg::with_name("FILENAME")
            .help("Source file to compile")
            .required(true)
            .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let level = matches.value_of("level").unwrap().parse::<u32>().unwrap();

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let mut ir = isbfc::parse(&code);
    if level > 0 {
        ir = ir.optimize();
    }

    if matches.is_present("dump_ir") {
        if let Some(out_name) = matches.value_of("out_name") {
            let mut irfile = File::create(out_name).unwrap();
            writeln!(irfile, "{:#?}", ir).unwrap();
        } else {
            println!("{:#?}", ir);
        }
    } else if matches.is_present("output_asm") {
        println!("Compiling...");
        let output = ir.compile(tape_size);
        let def_name = format!("{}.s", name);
        let out_name = matches.value_of("out_name").unwrap_or(&def_name);
        let mut asmfile = File::create(out_name).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        println!("Compiling...");
        let output = ir.compile(tape_size);
        let out_name = matches.value_of("out_name").unwrap_or(name);
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn()
            .unwrap();
    }
}
<commit_msg>Use clap functionality to handle default value<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, ArgGroup, App};

extern crate isbfc;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
            .short("S")
            .help("Assemble but do not link"))
        .arg(Arg::with_name("dump_ir")
            .long("dumpir")
            .help("Dump intermediate representation; for debugging"))
        .group(ArgGroup::with_name("actions").args(&["output_asm", "dump_ir"]))
        .arg(Arg::with_name("debugging_symbols")
            .short("g")
            .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
            .short("o")
            .help("Output file name")
            .takes_value(true)
            .empty_values(false)
            .value_name("file"))
        .arg(Arg::with_name("tape_size")
            .long("tape-size")
            .help("Size of tape")
            .takes_value(true)
            .empty_values(false)
            .default_value("8192")
            .value_name("bytes"))
        .arg(Arg::with_name("level")
             .short("O")
             .help("Optimization level")
             .takes_value(true)
             .empty_values(false)
             .default_value("1"))
        .arg(Arg::with_name("FILENAME")
            .help("Source file to compile")
            .required(true)
            .index(1))
        .get_matches();

    let tape_size = matches.value_of("tape_size").unwrap()
        .parse::<i32>().unwrap();

    let level = matches.value_of("level").unwrap()
        .parse::<u32>().unwrap();

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let mut ir = isbfc::parse(&code);
    if level > 0 {
        ir = ir.optimize();
    }

    if matches.is_present("dump_ir") {
        if let Some(out_name) = matches.value_of("out_name") {
            let mut irfile = File::create(out_name).unwrap();
            writeln!(irfile, "{:#?}", ir).unwrap();
        } else {
            println!("{:#?}", ir);
        }
    } else if matches.is_present("output_asm") {
        println!("Compiling...");
        let output = ir.compile(tape_size);
        let def_name = format!("{}.s", name);
        let out_name = matches.value_of("out_name").unwrap_or(&def_name);
        let mut asmfile = File::create(out_name).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        println!("Compiling...");
        let output = ir.compile(tape_size);
        let out_name = matches.value_of("out_name").unwrap_or(name);
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn()
            .unwrap();
    }
}
<|endoftext|>


<commit_before>#![feature(rust_2018_preview)]
#![feature(rust_2018_idioms)]
#![feature(nll)]
#![feature(euclidean_division)]

mod aabb;
mod bvh;
mod camera;
mod color;
mod gl_renderer;
mod input;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod scene;
mod triangle;
mod vertex;

use std::collections::HashMap;
use std::fmt::Write as FmtWrite;
use std::fs::File;
use std::io::Write as IoWrite;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Instant;

use cgmath::Vector3;

use chrono::Local;

use glium::Surface;
use glium::backend::Facade;
use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode};

use crate::camera::Camera;
use crate::gl_renderer::GLRenderer;
use crate::input::InputState;
use crate::pt_renderer::PTRenderer;
use crate::scene::{Scene, GPUScene};

fn load_offline_scene(path: &Path) -> (Arc<Scene>, Camera) {
    let scene = Scene::new(path);
    let mut camera = Camera::new(scene.center() + scene.size() * Vector3::new(0.0, 0.0, 1.0f32),
                                 Vector3::new(0.0, 0.0, -1.0f32));
    camera.set_scale(scene.size());
    (Arc::new(scene), camera)
}

fn load_scene<F: Facade>(path: &Path, facade: &F) -> (Arc<Scene>, GPUScene, Camera) {
    let (scene, camera) = load_offline_scene(path);
    let gpu_scene = scene.upload_data(facade);
    (scene, gpu_scene, camera)
}

fn main() {
    if std::env::args().count() > 1 {
        offline_render();
    } else {
        online_render();
    }
}

fn offline_render() {
    let root_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let scenes: Vec<PathBuf> =
        [ root_path.join("scenes/plane.obj"),
          root_path.join("scenes/cornell-box/CornellBox-Original.obj"),
          root_path.join("scenes/cornell-box/CornellBox-Glossy.obj"),
          root_path.join("scenes/cornell-box/CornellBox-Water.obj"), ]
        .to_vec();

    let mut pt_renderer = PTRenderer::new();
    let save_path = root_path.join("results");
    if !save_path.exists() {
        std::fs::create_dir_all(save_path.clone()).unwrap();
    }
    let mut combined_info = String::new();
    for scene_path in scenes {
        let file_name = scene_path.file_name().unwrap().to_str().unwrap();
        let scene_name = file_name.split('.').next().unwrap();

        let load_start = Instant::now();
        let (scene, mut camera) = load_offline_scene(&scene_path);
        let load_duration = load_start.elapsed();

        camera.update_viewport((600, 400));
        let render_start = Instant::now();
        pt_renderer.offline_render(&scene, &camera, 2);
        let render_duration = render_start.elapsed();
        let ray_count = pt_renderer.get_ray_count();

        let mut info_string = String::new();
        let float_time = render_duration.as_secs() as f64
            + f64::from(render_duration.subsec_nanos()) / 1_000_000_000.0;
        let ray_speed = ray_count as f64 / float_time;
        write!(&mut info_string, "\n{}:\n", scene_name).unwrap();
        write!(&mut info_string, "    loaded in {:#?}\n", load_duration).unwrap();
        write!(&mut info_string, "    rendered in {:#?}\n", render_duration).unwrap();
        write!(&mut info_string, "    {} total rays\n", ray_count).unwrap();
        write!(&mut info_string, "    {:.0} rays / sec\n", ray_speed).unwrap();
        println!("{}", info_string);
        combined_info.push_str(&info_string);

        let mut save_file = String::from(scene_name);
        save_file.push_str(".png");
        pt_renderer.save_image(&save_path.join(save_file));
    }
    let timing_path = save_path.join(Local::now().format("timing_%F_%H%M%S.txt").to_string());
    let mut timing_file = File::create(timing_path).unwrap();
    timing_file.write_all(&combined_info.into_bytes()).unwrap();
}

fn online_render() {
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new();
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display = glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let root_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    // TODO: Enable use of arbitrary scene
    let scenes: HashMap<VirtualKeyCode, PathBuf> =
        [(VirtualKeyCode::Key1, root_path.join("scenes/plane.obj")),
         (VirtualKeyCode::Key2, root_path.join("scenes/cornell/cornell.obj")),
         (VirtualKeyCode::Key3, root_path.join("scenes/cornell/cornell_chesterfield.obj")),
         (VirtualKeyCode::Key4, root_path.join("scenes/cornell-box/CornellBox-Original.obj")),
         (VirtualKeyCode::Key5, root_path.join("scenes/cornell-box/CornellBox-Glossy.obj")),
         (VirtualKeyCode::Key6, root_path.join("scenes/cornell-box/CornellBox-Water.obj")),
         (VirtualKeyCode::Key7, root_path.join("scenes/nanosuit/nanosuit.obj")),
         (VirtualKeyCode::Key8, root_path.join("scenes/sibenik/sibenik.obj"))]
        .iter().cloned().collect();

    let (mut scene, mut gpu_scene, mut camera) =
        load_scene(&scenes[&VirtualKeyCode::Key1], &display);
    let gl_renderer = GLRenderer::new(&display);
    let mut pt_renderer = PTRenderer::new();

    let mut input = InputState::new();
    let mut trace = false;
    let mut quit = false;

    loop {
        let mut target = display.draw();

        camera.update_viewport(target.get_dimensions());
        // Don't draw if the window is minimized
        if camera.width != 0 && camera.height != 0 {
            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            if trace {
                pt_renderer.render(&mut target);
            } else {
                gl_renderer.render(&mut target, &gpu_scene, &camera);
            }
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent{event: WindowEvent::KeyboardInput{input, ..}, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Space), ..} => {
                            trace = !trace;
                            if trace {
                                pt_renderer.online_render(&display, &scene, &camera);
                            } else {
                                pt_renderer.stop_threads();
                            }
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(ref keycode), ..} => {
                            if let Some(scene_to_load) = scenes.get(keycode) {
                                let res = load_scene(scene_to_load, &display);
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        },
                        _ => ()
                    }
                }
                Event::WindowEvent{event: WindowEvent::Closed, ..} => quit = true,
                _ => ()
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            pt_renderer.stop_threads();
            return;
        }
    }
}
<commit_msg>Update benchmark scenes<commit_after>#![feature(rust_2018_preview)]
#![feature(rust_2018_idioms)]
#![feature(nll)]
#![feature(euclidean_division)]

mod aabb;
mod bvh;
mod camera;
mod color;
mod gl_renderer;
mod input;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod scene;
mod triangle;
mod vertex;

use std::collections::HashMap;
use std::fmt::Write as FmtWrite;
use std::fs::File;
use std::io::Write as IoWrite;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Instant;

use cgmath::Vector3;

use chrono::Local;

use glium::Surface;
use glium::backend::Facade;
use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode};

use crate::camera::Camera;
use crate::gl_renderer::GLRenderer;
use crate::input::InputState;
use crate::pt_renderer::PTRenderer;
use crate::scene::{Scene, GPUScene};

fn load_offline_scene(path: &Path) -> (Arc<Scene>, Camera) {
    let scene = Scene::new(path);
    let mut camera = Camera::new(scene.center() + scene.size() * Vector3::new(0.0, 0.0, 1.0f32),
                                 Vector3::new(0.0, 0.0, -1.0f32));
    camera.set_scale(scene.size());
    (Arc::new(scene), camera)
}

fn load_scene<F: Facade>(path: &Path, facade: &F) -> (Arc<Scene>, GPUScene, Camera) {
    let (scene, camera) = load_offline_scene(path);
    let gpu_scene = scene.upload_data(facade);
    (scene, gpu_scene, camera)
}

fn main() {
    if std::env::args().count() > 1 {
        offline_render();
    } else {
        online_render();
    }
}

fn offline_render() {
    let root_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let scenes: Vec<PathBuf> =
        [ root_path.join("scenes/plane.obj"),
          root_path.join("scenes/cornell-box/CornellBox-Glossy.obj"),
          root_path.join("scenes/cornell-box/CornellBox-Water.obj"),
          root_path.join("scenes/nanosuit/nanosuit.obj"),
          root_path.join("scenes/sibenik/sibenik.obj"),
        ].to_vec();

    let mut pt_renderer = PTRenderer::new();
    let save_path = root_path.join("results");
    if !save_path.exists() {
        std::fs::create_dir_all(save_path.clone()).unwrap();
    }
    let mut combined_info = String::new();
    for scene_path in scenes {
        let file_name = scene_path.file_name().unwrap().to_str().unwrap();
        let scene_name = file_name.split('.').next().unwrap();

        let load_start = Instant::now();
        let (scene, mut camera) = load_offline_scene(&scene_path);
        let load_duration = load_start.elapsed();

        camera.update_viewport((600, 400));
        let render_start = Instant::now();
        pt_renderer.offline_render(&scene, &camera, 2);
        let render_duration = render_start.elapsed();
        let ray_count = pt_renderer.get_ray_count();

        let mut info_string = String::new();
        let float_time = render_duration.as_secs() as f64
            + f64::from(render_duration.subsec_nanos()) / 1_000_000_000.0;
        let ray_speed = ray_count as f64 / float_time;
        write!(&mut info_string, "\n{}:\n", scene_name).unwrap();
        write!(&mut info_string, "    loaded in {:#?}\n", load_duration).unwrap();
        write!(&mut info_string, "    rendered in {:#?}\n", render_duration).unwrap();
        write!(&mut info_string, "    {} total rays\n", ray_count).unwrap();
        write!(&mut info_string, "    {:.0} rays / sec\n", ray_speed).unwrap();
        println!("{}", info_string);
        combined_info.push_str(&info_string);

        let mut save_file = String::from(scene_name);
        save_file.push_str(".png");
        pt_renderer.save_image(&save_path.join(save_file));
    }
    let timing_path = save_path.join(Local::now().format("timing_%F_%H%M%S.txt").to_string());
    let mut timing_file = File::create(timing_path).unwrap();
    timing_file.write_all(&combined_info.into_bytes()).unwrap();
}

fn online_render() {
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new();
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display = glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let root_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    // TODO: Enable use of arbitrary scene
    let scenes: HashMap<VirtualKeyCode, PathBuf> =
        [(VirtualKeyCode::Key1, root_path.join("scenes/plane.obj")),
         (VirtualKeyCode::Key2, root_path.join("scenes/cornell/cornell.obj")),
         (VirtualKeyCode::Key3, root_path.join("scenes/cornell/cornell_chesterfield.obj")),
         (VirtualKeyCode::Key4, root_path.join("scenes/cornell-box/CornellBox-Original.obj")),
         (VirtualKeyCode::Key5, root_path.join("scenes/cornell-box/CornellBox-Glossy.obj")),
         (VirtualKeyCode::Key6, root_path.join("scenes/cornell-box/CornellBox-Water.obj")),
         (VirtualKeyCode::Key7, root_path.join("scenes/nanosuit/nanosuit.obj")),
         (VirtualKeyCode::Key8, root_path.join("scenes/sibenik/sibenik.obj"))]
        .iter().cloned().collect();

    let (mut scene, mut gpu_scene, mut camera) =
        load_scene(&scenes[&VirtualKeyCode::Key1], &display);
    let gl_renderer = GLRenderer::new(&display);
    let mut pt_renderer = PTRenderer::new();

    let mut input = InputState::new();
    let mut trace = false;
    let mut quit = false;

    loop {
        let mut target = display.draw();

        camera.update_viewport(target.get_dimensions());
        // Don't draw if the window is minimized
        if camera.width != 0 && camera.height != 0 {
            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            if trace {
                pt_renderer.render(&mut target);
            } else {
                gl_renderer.render(&mut target, &gpu_scene, &camera);
            }
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent{event: WindowEvent::KeyboardInput{input, ..}, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Space), ..} => {
                            trace = !trace;
                            if trace {
                                pt_renderer.online_render(&display, &scene, &camera);
                            } else {
                                pt_renderer.stop_threads();
                            }
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(ref keycode), ..} => {
                            if let Some(scene_to_load) = scenes.get(keycode) {
                                let res = load_scene(scene_to_load, &display);
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        },
                        _ => ()
                    }
                }
                Event::WindowEvent{event: WindowEvent::Closed, ..} => quit = true,
                _ => ()
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            pt_renderer.stop_threads();
            return;
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

extern crate clap;
use clap::{Arg, App, SubCommand};
extern crate toml;

extern crate reqwest;
use reqwest::header::Authorization;

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate glob;
use glob::glob;

extern crate libc;
#[derive(Deserialize, Debug)]
struct Config {
    token: String,
    org: String,
    team: String,
    #[serde(default="default_endpoint")]
    endpoint: String,
    #[serde(default="default_home")]
    home: String,
    #[serde(default="default_gid")]
    gid: u64,
    #[serde(default="default_sh")]
    sh: String,
    group: Option<String>,
    #[serde(default="default_cache_duration")]
    cache_duration: u64,
    #[serde(default="default_cert_path")]
    cert_path: String
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_gid() -> u64 { 2000 }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Team {
    id: u64,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    id: u64,
    login: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct PublicKey {
    id: u64,
    key: String,
}

#[derive(Debug)]
enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error),
}

impl From<serde_json::Error> for CliError {fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }}
impl From<reqwest::Error> for CliError {fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }}
impl From<std::io::Error> for CliError {fn from(err: std::io::Error) -> CliError { CliError::Io(err) }}

fn main() {

    let matches = App::new("ghteam-auth")
                      .version("0.1")
                      .author("Yasuyuki YAMADA <yasuyuki.ymd@gmail.com>")
                      .about("")
                      .arg(Arg::with_name("config")
                               .short("c")
                               .long("config")
                               .value_name("FILE")
                               .help("Sets a custom config file (toml)")
                               .takes_value(true))
                      .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                      .subcommand(SubCommand::with_name("key")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(true)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("pam")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(false)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("passwd")
                                             .about("get passwd"))
                      .subcommand(SubCommand::with_name("shadow")
                                             .about("get shadow"))
                      .subcommand(SubCommand::with_name("group")
                                             .about("get group"))
                      .subcommand(SubCommand::with_name("refresh")
                                             .about("refresh cache"))
                      .get_matches();

    let configpath = matches.value_of("config").unwrap_or("/etc/ghteam-auth.conf");
    let client = create_github_client(configpath).unwrap();

    if let Some(matches) = matches.subcommand_matches("key") {
        client.print_user_public_key(matches.value_of("USER").unwrap()).unwrap();
    } else if let Some(_) = matches.subcommand_matches("passwd") {
        client.print_passwd().unwrap();
    } else if let Some(_) = matches.subcommand_matches("shadow") {
        client.print_shadow().unwrap();
    } else if let Some(_) = matches.subcommand_matches("group") {
        client.print_group().unwrap();
    } else if let Some(_) = matches.subcommand_matches("refresh") {
        client.clear_all_caches().unwrap();
    } else if let Some(_) = matches.subcommand_matches("pam") {
        match std::env::var("PAM_USER") {
            Ok(user) => {
                if client.check_pam(&user).unwrap() { std::process::exit(0); }
                else { std::process::exit(1) }
            }
            Err(e) => println!("couldn't interpret PAM_USER: {}", e),
        }
    }

}

fn create_github_client( configpath: &str ) -> Result<GithubClient, CliError> {
    let mut file = File::open(configpath)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let config = toml::from_str::<Config>(contents.as_str()).unwrap();
    match std::env::var("SSL_CERT_FILE") {
        Ok(_) => (),
        Err(_) => std::env::set_var("SSL_CERT_FILE", config.cert_path.clone()),
    }
    let client = reqwest::Client::new()?;
    Ok(GithubClient::new(client, config))
}

struct GithubClient {
    client: reqwest::Client,
    conf: Config
}

impl GithubClient {
    fn new(client:reqwest::Client, conf:Config) -> GithubClient {
        GithubClient {client:client, conf:conf}
    }

    fn load_content_from_cache(&self, url:&String) -> Result<(std::fs::Metadata,String),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata,content))
    }

    fn store_content_to_cache(&self, url:&String, content:&String) -> Result<(),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let mut dirpath = path.clone(); dirpath.pop();
        std::fs::create_dir_all(dirpath)?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url:&String) -> Result<String,CliError> {
        let token = String::from("token ") + self.conf.token.clone().as_str();
        let res = self.client.get(url.as_str()).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url,&content)?;
        Ok(content)
    }

    fn get_content(&self, url:&String) -> Result<String,CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata,cache_content)) => {
                match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                    Ok(caching_duration) => {
                        if caching_duration.as_secs() > self.conf.cache_duration {
                            match self.get_content_from_url(url) {
                                Ok(content_from_url) => Ok(content_from_url),
                                Err(_) => Ok(cache_content)
                            }
                        } else {
                            Ok(cache_content)
                        }
                    },
                    Err(_) => {
                        Ok(cache_content)
                    }
                }
            },
            Err(_) => self.get_content_from_url(url)
        }
    }

    fn print_user_public_key(&self, user:&str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user:&str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url);
        let keys = serde_json::from_str::<Vec<PublicKey>>(content?.as_str())?;
        Ok(keys.iter().map(|k|{k.key.clone()}).collect::<Vec<String>>().join("\n"))
    }

    fn check_pam(&self, user:&String) -> Result<bool, CliError> {
        let (_,members) = self.get_team_members()?;
        Ok(members.contains_key(user))
    }

    fn print_passwd(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_passwd_line(&member));
        }
        Ok(())
    }

    fn print_shadow(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_shadow_line(&member));
        }
        Ok(())
    }

    fn print_group(&self) -> Result<(), CliError> {
        let (team,members) = self.get_team_members()?;
        println!("{}", self.create_group_line(&team.name, self.conf.gid, &members));
        Ok(())
    }

    fn create_passwd_line(&self, member:&Member) -> String {
        format!("{login}:x:{uid}:{gid}:user@{org}:{home}:{sh}",
                login=member.login,
                uid=member.id,
                gid=self.conf.gid,
                org=self.conf.org,
                home=self.conf.home.replace("{}",member.login.as_str()),
                sh=self.conf.sh,
                )
    }

    fn create_shadow_line(&self, member:&Member) -> String {
        format!("{login}:!!:::::::", login=member.login )
    }

    fn create_group_line(&self, name:&String, id:u64, members:&HashMap<String,Member>) -> String {
        format!("{name}:x:{id}:{members}", name=name, id=id,
                members=members.values().map(|m|{m.login.clone()}).collect::<Vec<String>>().join(","))
    }

    fn get_team_members(&self) -> Result<(Team,HashMap<String,Member>),CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            Ok((team.clone(),self.get_members(team.id)?))
        } else {
            Err(CliError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "Team not found")))
        }
    }

    fn get_teams(&self) -> Result<HashMap<String,Team>, CliError> {
        let url = format!("{}/orgs/{}/teams",self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(content.as_str())?;
        let mut team_map = HashMap::new();
        for team in teams { team_map.insert(team.name.clone(), team); }
        Ok(team_map)
    }

    fn get_members(&self, mid:u64) -> Result<HashMap<String,Member>, CliError> {
        let url = format!("{}/teams/{}/members",self.conf.endpoint.clone(), mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(content.as_str())?;
        let mut member_map = HashMap::new();
        for member in members { member_map.insert(member.login.clone(), member); }
        Ok(member_map)
    }

    fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push("**");
        path.push("*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {if path.is_file() {std::fs::remove_file(path)?}},
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }

}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct Passwd {
    name:   *mut libc::c_char,
    pass:   *mut libc::c_char,
    uid:    libc::uid_t,
    gid:    libc::gid_t,
    gecos:  *mut libc::c_char,
    home:   *mut libc::c_char,
    sh:     *mut libc::c_char,
}
<commit_msg>Fix help message<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

extern crate clap;
use clap::{Arg, App, SubCommand};
extern crate toml;

extern crate reqwest;
use reqwest::header::Authorization;

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate glob;
use glob::glob;

extern crate libc;
#[derive(Deserialize, Debug)]
struct Config {
    token: String,
    org: String,
    team: String,
    #[serde(default="default_endpoint")]
    endpoint: String,
    #[serde(default="default_home")]
    home: String,
    #[serde(default="default_gid")]
    gid: u64,
    #[serde(default="default_sh")]
    sh: String,
    group: Option<String>,
    #[serde(default="default_cache_duration")]
    cache_duration: u64,
    #[serde(default="default_cert_path")]
    cert_path: String
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_gid() -> u64 { 2000 }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Team {
    id: u64,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    id: u64,
    login: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct PublicKey {
    id: u64,
    key: String,
}

#[derive(Debug)]
enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error),
}

impl From<serde_json::Error> for CliError {fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }}
impl From<reqwest::Error> for CliError {fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }}
impl From<std::io::Error> for CliError {fn from(err: std::io::Error) -> CliError { CliError::Io(err) }}

fn main() {

    let matches = App::new("ghteam-auth")
                      .version("0.1")
                      .author("Yasuyuki YAMADA <yasuyuki.ymd@gmail.com>")
                      .about("")
                      .arg(Arg::with_name("config")
                               .short("c")
                               .long("config")
                               .value_name("FILE")
                               .help("Sets a custom config file (toml)")
                               .takes_value(true))
                      .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                      .subcommand(SubCommand::with_name("key")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(true)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("pam")
                                             .about("execute pam check")
                                             .arg(Arg::with_name("USER")
                                                      .required(false)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("passwd")
                                             .about("get passwd"))
                      .subcommand(SubCommand::with_name("shadow")
                                             .about("get shadow"))
                      .subcommand(SubCommand::with_name("group")
                                             .about("get group"))
                      .subcommand(SubCommand::with_name("refresh")
                                             .about("refresh cache"))
                      .get_matches();

    let configpath = matches.value_of("config").unwrap_or("/etc/ghteam-auth.conf");
    let client = create_github_client(configpath).unwrap();

    if let Some(matches) = matches.subcommand_matches("key") {
        client.print_user_public_key(matches.value_of("USER").unwrap()).unwrap();
    } else if let Some(_) = matches.subcommand_matches("passwd") {
        client.print_passwd().unwrap();
    } else if let Some(_) = matches.subcommand_matches("shadow") {
        client.print_shadow().unwrap();
    } else if let Some(_) = matches.subcommand_matches("group") {
        client.print_group().unwrap();
    } else if let Some(_) = matches.subcommand_matches("refresh") {
        client.clear_all_caches().unwrap();
    } else if let Some(_) = matches.subcommand_matches("pam") {
        match std::env::var("PAM_USER") {
            Ok(user) => {
                if client.check_pam(&user).unwrap() { std::process::exit(0); }
                else { std::process::exit(1) }
            }
            Err(e) => println!("couldn't interpret PAM_USER: {}", e),
        }
    }

}

fn create_github_client( configpath: &str ) -> Result<GithubClient, CliError> {
    let mut file = File::open(configpath)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let config = toml::from_str::<Config>(contents.as_str()).unwrap();
    match std::env::var("SSL_CERT_FILE") {
        Ok(_) => (),
        Err(_) => std::env::set_var("SSL_CERT_FILE", config.cert_path.clone()),
    }
    let client = reqwest::Client::new()?;
    Ok(GithubClient::new(client, config))
}

struct GithubClient {
    client: reqwest::Client,
    conf: Config
}

impl GithubClient {
    fn new(client:reqwest::Client, conf:Config) -> GithubClient {
        GithubClient {client:client, conf:conf}
    }

    fn load_content_from_cache(&self, url:&String) -> Result<(std::fs::Metadata,String),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata,content))
    }

    fn store_content_to_cache(&self, url:&String, content:&String) -> Result<(),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let mut dirpath = path.clone(); dirpath.pop();
        std::fs::create_dir_all(dirpath)?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url:&String) -> Result<String,CliError> {
        let token = String::from("token ") + self.conf.token.clone().as_str();
        let res = self.client.get(url.as_str()).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url,&content)?;
        Ok(content)
    }

    fn get_content(&self, url:&String) -> Result<String,CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata,cache_content)) => {
                match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                    Ok(caching_duration) => {
                        if caching_duration.as_secs() > self.conf.cache_duration {
                            match self.get_content_from_url(url) {
                                Ok(content_from_url) => Ok(content_from_url),
                                Err(_) => Ok(cache_content)
                            }
                        } else {
                            Ok(cache_content)
                        }
                    },
                    Err(_) => {
                        Ok(cache_content)
                    }
                }
            },
            Err(_) => self.get_content_from_url(url)
        }
    }

    fn print_user_public_key(&self, user:&str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user:&str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url);
        let keys = serde_json::from_str::<Vec<PublicKey>>(content?.as_str())?;
        Ok(keys.iter().map(|k|{k.key.clone()}).collect::<Vec<String>>().join("\n"))
    }

    fn check_pam(&self, user:&String) -> Result<bool, CliError> {
        let (_,members) = self.get_team_members()?;
        Ok(members.contains_key(user))
    }

    fn print_passwd(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_passwd_line(&member));
        }
        Ok(())
    }

    fn print_shadow(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_shadow_line(&member));
        }
        Ok(())
    }

    fn print_group(&self) -> Result<(), CliError> {
        let (team,members) = self.get_team_members()?;
        println!("{}", self.create_group_line(&team.name, self.conf.gid, &members));
        Ok(())
    }

    fn create_passwd_line(&self, member:&Member) -> String {
        format!("{login}:x:{uid}:{gid}:user@{org}:{home}:{sh}",
                login=member.login,
                uid=member.id,
                gid=self.conf.gid,
                org=self.conf.org,
                home=self.conf.home.replace("{}",member.login.as_str()),
                sh=self.conf.sh,
                )
    }

    fn create_shadow_line(&self, member:&Member) -> String {
        format!("{login}:!!:::::::", login=member.login )
    }

    fn create_group_line(&self, name:&String, id:u64, members:&HashMap<String,Member>) -> String {
        format!("{name}:x:{id}:{members}", name=name, id=id,
                members=members.values().map(|m|{m.login.clone()}).collect::<Vec<String>>().join(","))
    }

    fn get_team_members(&self) -> Result<(Team,HashMap<String,Member>),CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            Ok((team.clone(),self.get_members(team.id)?))
        } else {
            Err(CliError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "Team not found")))
        }
    }

    fn get_teams(&self) -> Result<HashMap<String,Team>, CliError> {
        let url = format!("{}/orgs/{}/teams",self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(content.as_str())?;
        let mut team_map = HashMap::new();
        for team in teams { team_map.insert(team.name.clone(), team); }
        Ok(team_map)
    }

    fn get_members(&self, mid:u64) -> Result<HashMap<String,Member>, CliError> {
        let url = format!("{}/teams/{}/members",self.conf.endpoint.clone(), mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(content.as_str())?;
        let mut member_map = HashMap::new();
        for member in members { member_map.insert(member.login.clone(), member); }
        Ok(member_map)
    }

    fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push("**");
        path.push("*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {if path.is_file() {std::fs::remove_file(path)?}},
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }

}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct Passwd {
    name:   *mut libc::c_char,
    pass:   *mut libc::c_char,
    uid:    libc::uid_t,
    gid:    libc::gid_t,
    gecos:  *mut libc::c_char,
    home:   *mut libc::c_char,
    sh:     *mut libc::c_char,
}
<|endoftext|>