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

extern crate phosphorus;
extern crate gfx;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;
extern crate glutin;

use std::env;
use std::cell::RefCell;
use std::rc::Rc;
use gfx::traits::*;
use phosphorus::Gui;
use phosphorus::widget::{ButtonBuilder, Layout, LayoutBuilder, TextBuilder};

struct SharedData {
    canceled: bool,
    queued_layout: Option<Layout<gfx_device_gl::Resources>>
}

fn main() {
    // This is all a bunch of placeholder code that needs to be refactored into something coherent

    // Get the target path to open the editor at
    let path = match env::args().nth(1) {
        Some(v) => v,
        None => {
            display_error("No path given!");
            return;
        }
    };

    // Set up our Phosphorus UI
    let data = Rc::new(RefCell::new(SharedData { canceled: false, queued_layout: None }));
    let layout = generate_view(data.clone(), 0);

    data.borrow_mut().queued_layout = Some(layout);

    display_gui(data);
}

fn generate_view(data: Rc<RefCell<SharedData>>, num: i32) -> Layout<gfx_device_gl::Resources> {
    LayoutBuilder::<gfx_device_gl::Resources>::new()
        .with_background_color([21, 23, 24])
        .with_widget(TextBuilder::new()
            .with_text(&format!("Num: {}", num))
            .build_boxed())
        .with_widget(ButtonBuilder::new()
            .with_text("Add One")
            .with_callback(Box::new(move || {
                data.borrow_mut().queued_layout = Some(generate_view(data.clone(), num + 1))
            }))
            .build_boxed())
        .build()
}

fn display_error(text: &str) {
    let data = Rc::new(RefCell::new(SharedData { canceled: false, queued_layout: None }));
    let data_clone = data.clone();
    let layout = LayoutBuilder::<gfx_device_gl::Resources>::new()
        .with_background_color([21, 23, 24])
        .with_widget(TextBuilder::new()
            .with_text(text)
            .build_boxed())
        .with_widget(ButtonBuilder::new()
            .with_text("Ok")
            .with_callback(Box::new(move || data_clone.borrow_mut().canceled = true))
            .build_boxed())
        .build();

    data.borrow_mut().queued_layout = Some(layout);

    display_gui(data);
}

fn display_gui(data: Rc<RefCell<SharedData>>)
{
    // Set up our window
    let (mut stream, mut device, mut factory) = {
        let window = glutin::WindowBuilder::new()
            .with_vsync()
            .with_dimensions(600, 500)
            .with_title(String::from("Phosphorus Widgets"))
            .build_strict().unwrap();
        gfx_window_glutin::init(window)
    };

    let mut gui = Gui::new(
        &mut device,
        data.borrow_mut().queued_layout.take().unwrap(),
        |d: &mut gfx_device_gl::Device| d.spawn_factory());

    // Run our actual UI loop
    'main: loop {
        {
            let mut d = data.borrow_mut();

            if d.canceled {
                break 'main;
            }

            if let Some(layout) = d.queued_layout.take() {
                gui.set_root(layout);
            }
        }

        // Quit when the window is closed
        for event in stream.out.window.poll_events() {
            match event {
                glutin::Event::Closed => break 'main,
                glutin::Event::MouseMoved((x, y)) =>
                    gui.raise_event(&stream, phosphorus::Event::MouseMoved([x, y])),
                glutin::Event::MouseInput(glutin::ElementState::Released, _) =>
                    gui.raise_event(&stream, phosphorus::Event::MouseClick),
                _ => (),
            }
        }

        stream.clear(gfx::ClearData {color: [1.0, 1.0, 1.0, 1.0], depth: 1.0, stencil: 0});

        // Render our actual GUI
        gui.render(&mut factory, &mut stream);

        // Show the rendered to buffer on the screen
        //stream.present(&mut device); ICE!
        stream.flush(&mut device);
        stream.out.window.swap_buffers();
        device.cleanup();
    }
}
<commit_msg>Add very basic mockup UI<commit_after>// Copyright 2015 The Athena Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate phosphorus;
extern crate gfx;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;
extern crate glutin;

use std::env;
use std::cell::RefCell;
use std::rc::Rc;
use gfx::traits::*;
use phosphorus::Gui;
use phosphorus::widget::{ButtonBuilder, Layout, LayoutBuilder, TextBuilder};

struct SharedData {
    canceled: bool,
    queued_layout: Option<Layout<gfx_device_gl::Resources>>
}

struct EntityEntry;

struct WorldModel {
    entities: Vec<EntityEntry>
}

struct Model {
    worlds: Vec<WorldModel>
}

fn main() {
    // This is all a bunch of placeholder code that needs to be refactored into something coherent

    // Get the target path to open the editor at
    let path = match env::args().nth(1) {
        Some(v) => v,
        None => {
            display_error("No path given!");
            return;
        }
    };

    // Set up our Phosphorus UI
    let data = Rc::new(RefCell::new(SharedData { canceled: false, queued_layout: None }));
    let layout = generate_view(data.clone(), Rc::new(RefCell::new(Model { worlds: vec![] })));

    data.borrow_mut().queued_layout = Some(layout);

    display_gui(data);
}

fn generate_view(data: Rc<RefCell<SharedData>>, model: Rc<RefCell<Model>>) -> Layout<gfx_device_gl::Resources> {
    let mut builder = LayoutBuilder::<gfx_device_gl::Resources>::new()
        .with_background_color([21, 23, 24]);

    let mut wnum = 0;
    for world in &model.borrow_mut().worlds {
        builder = builder
            .with_widget(TextBuilder::new()
                .with_text(&format!("== World #{} ==", wnum))
                .build_boxed());

        let mut ennum = 0;
        for entity in &world.entities {
            builder = builder
                .with_widget(TextBuilder::new()
                    .with_text(&format!("Entity #{}", ennum))
                    .build_boxed());
            ennum += 1;
        }

        let tmp_model = model.clone();
        let tmp_data = data.clone();
        builder = builder
            .with_widget(ButtonBuilder::new()
                .with_text("Add Entity")
                .with_callback(Box::new(move || {
                    {
                        let mut m = tmp_model.borrow_mut();
                        let w = m.worlds.get_mut(wnum).unwrap();
                        w.entities.push(EntityEntry);
                    }

                    tmp_data.borrow_mut().queued_layout = Some(generate_view(tmp_data.clone(), tmp_model.clone()));
                }))
            .build_boxed());
        wnum += 1;

        // Placeholder text as padding
        builder = builder.with_widget(TextBuilder::new().build_boxed());
    }

    builder
        .with_widget(ButtonBuilder::new()
            .with_text("Add World")
            .with_callback(Box::new(move || {
                {
                    let mut m = model.borrow_mut();
                    m.worlds.push(WorldModel { entities: vec![] });
                }

                data.borrow_mut().queued_layout = Some(generate_view(data.clone(), model.clone()));
            }))
            .build_boxed())
        .build()
}

fn display_error(text: &str) {
    let data = Rc::new(RefCell::new(SharedData { canceled: false, queued_layout: None }));
    let data_clone = data.clone();
    let layout = LayoutBuilder::<gfx_device_gl::Resources>::new()
        .with_background_color([21, 23, 24])
        .with_widget(TextBuilder::new()
            .with_text(text)
            .build_boxed())
        .with_widget(ButtonBuilder::new()
            .with_text("Ok")
            .with_callback(Box::new(move || data_clone.borrow_mut().canceled = true))
            .build_boxed())
        .build();

    data.borrow_mut().queued_layout = Some(layout);

    display_gui(data);
}

fn display_gui(data: Rc<RefCell<SharedData>>)
{
    // Set up our window
    let (mut stream, mut device, mut factory) = {
        let window = glutin::WindowBuilder::new()
            .with_vsync()
            .with_dimensions(600, 500)
            .with_title(String::from("Phosphorus Widgets"))
            .build_strict().unwrap();
        gfx_window_glutin::init(window)
    };

    let mut gui = Gui::new(
        &mut device,
        data.borrow_mut().queued_layout.take().unwrap(),
        |d: &mut gfx_device_gl::Device| d.spawn_factory());

    // Run our actual UI loop
    'main: loop {
        {
            let mut d = data.borrow_mut();

            if d.canceled {
                break 'main;
            }

            if let Some(layout) = d.queued_layout.take() {
                gui.set_root(layout);
            }
        }

        // Quit when the window is closed
        for event in stream.out.window.poll_events() {
            match event {
                glutin::Event::Closed => break 'main,
                glutin::Event::MouseMoved((x, y)) =>
                    gui.raise_event(&stream, phosphorus::Event::MouseMoved([x, y])),
                glutin::Event::MouseInput(glutin::ElementState::Released, _) =>
                    gui.raise_event(&stream, phosphorus::Event::MouseClick),
                _ => (),
            }
        }

        stream.clear(gfx::ClearData {color: [1.0, 1.0, 1.0, 1.0], depth: 1.0, stencil: 0});

        // Render our actual GUI
        gui.render(&mut factory, &mut stream);

        // Show the rendered to buffer on the screen
        //stream.present(&mut device); ICE!
        stream.flush(&mut device);
        stream.out.window.swap_buffers();
        device.cleanup();
    }
}
<|endoftext|>


<commit_before>#![feature(scoped)]

extern crate cucache;
use cucache::memcache;

extern crate getopts;
extern crate time;
extern crate byteorder;
use byteorder::{BigEndian, ByteOrder};

#[macro_use]
extern crate log;
use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata};

use std::io::Write;
use std::net;
use std::thread;
use std::env;
use std::str::FromStr;
use std::process;
use std::sync;
use std::sync::mpsc;
use std::io;
use std::str;

extern crate num;
use num::traits::FromPrimitive;

fn from_mctime(i : u32) -> i64 {
    if i > 0 && i < 30*24*60*60 {
        time::get_time().sec + i as i64
    } else {
        i as i64
    }
}

fn main() {
    let _ = log::set_logger(|max_log_level| {
        max_log_level.set(LogLevelFilter::Debug);
        Box::new(SimpleLogger)
    });

    let mut opts = getopts::Options::new();
    opts.optflagopt("p", "", "Listen on TCP port <num>, the default is port 11211.", "<num>");
    opts.optflagopt("U", "", "Listen on UDP port <num>, the default is port 11211, 0 is off.", "<num>");
    opts.optflag("h", "help", "display this help and exit");

    let args: Vec<String> = env::args().collect();
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            println!("{}", f);
            process::exit(1);
        }
    };

    if matches.opt_present("help") {
        let brief = format!("Usage: {} [options]", args[0]);
        println!("{}", opts.usage(&brief));
        return;
    }

    let host = "127.0.0.1";
    let mut tcp_port = 11211;
    if let Some(port) = matches.opt_str("p") {
        if let Ok(portn) = u16::from_str(&port) {
            tcp_port = portn;
        }
    }

    info!("listening on {}:tcp:{}", host, tcp_port);
    let listener = net::TcpListener::bind((host, tcp_port)).unwrap();

    info!("allocating map");
    let m = cucache::new(1 << 10);

    info!("spinning up worker pool");
    let (tx, rx) = mpsc::channel();
    let rxmx = sync::Mutex::new(rx);
    let pool : Vec<thread::JoinGuard<()>> = (1..100).map(|_| {
        let map = &m;
        let rxl = &rxmx;
        thread::scoped(move || {
            handle_clients(map, rxl);
        })
    }).collect();

    info!("accepting connections");
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                if let Err(e) = tx.send(stream) {
                    panic!("failed to queue incoming request {}", e);
                }
            }
            Err(e) => {
                warn!("{}", e);
            }
        }
    }

    drop(pool);
}

fn read_full(r : &mut io::Read, to : &mut [u8]) -> Result<(), io::Error> {
    let mut nread = 0 as usize;
    while nread < to.len() {
        match r.read(&mut to[nread..]) {
            Ok(n) => { nread += n; }
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {panic!("XXX")},
            Err(e) => return Err(From::from(e))
        }
    }
    Ok(())
}

fn execute(m : &cucache::Map, req : &memcache::Request, c : &mut net::TcpStream) {
    let mut rh = memcache::ResponseHeader::from_req(req);
    match memcache::Command::from_u8(req.op) {
        Some(op) => {
            let key = req.key();
            let extras = req.extras();
            let body = req.body();

            match op.noq() {
                memcache::Command::FLUSH if extras.len() == 4 => {
                    let mut tm = from_mctime(BigEndian::read_u32(extras));
                    if tm == 0 {
                        tm = time::get_time().sec;
                    }
                    m.touchall(tm);

                    rh.status = memcache::Status::SUCCESS as u16;
                    rh.construct(&[], &[], &[]).transmit(c);
                }
                memcache::Command::SET
                | memcache::Command::ADD
                | memcache::Command::REPLACE
                if extras.len() == 8 => {
                    let flags = BigEndian::read_u32(&extras[0..4]);
                    let expires = from_mctime(BigEndian::read_u32(&extras[4..8]));

                    // XXX: unfortunate copy...
                    let b = body.to_vec();

                    let res = match op.noq() {
                        memcache::Command::SET if req.cas == 0 => m.set(key, b, flags, expires),
                        memcache::Command::REPLACE if req.cas == 0 => m.replace(key, b, flags, expires),
                        memcache::Command::ADD => m.add(key, b, flags, expires),
                        memcache::Command::SET | memcache::Command::REPLACE | _ => {
                            m.cas(key, b, flags, expires, req.cas)
                        }
                    };

                    rh.status = res.0 as u16;
                    if let (memcache::Status::SUCCESS, Ok(v)) = res {
                        rh.cas = v.val.casid;
                    }

                    rh.construct(&[], &[], &[]).transmit(c);
                }
                memcache::Command::DELETE => {
                    rh.status = m.delete(key, req.cas).0 as u16;
                    rh.construct(&[], &[], &[]).transmit(c);
                }
                memcache::Command::INCREMENT
                | memcache::Command::DECREMENT
                if extras.len() == 20 => {
                    let by = BigEndian::read_u64(&extras[0..8]);
                    let def = BigEndian::read_u64(&extras[8..16]);
                    let expires = from_mctime(BigEndian::read_u32(&extras[16..20]));

                    let v = match op.noq() {
                        memcache::Command::INCREMENT => m.incr(key, by, def, expires),
                        memcache::Command::DECREMENT | _ => m.decr(key, by, def, expires),
                    };

                    rh.status = v.0 as u16;
                    if let (memcache::Status::SUCCESS, Ok(v)) = v {
                        let extras = &mut [0u8; 8];
                        BigEndian::write_u64(&mut extras[..], u64::from_str(str::from_utf8(&v.val.bytes[..]).unwrap()).unwrap());

                        rh.cas = v.val.casid;
                        rh.construct(&extras[..], &[], &[]).transmit(c);
                    } else {
                        rh.construct(&[], &[], &[]).transmit(c);
                    }
                }
                memcache::Command::GET | memcache::Command::GETK => {
                    let v = m.get(key);
                    rh.status = v.0 as u16;

                    if let (memcache::Status::SUCCESS, Ok(v)) = v {
                        rh.cas = v.val.casid;
                        let extras = &mut [0u8; 4];
                        BigEndian::write_u32(&mut extras[..], v.val.flags);
                        rh.construct(
                            &extras[..],
                            if op.noq() == memcache::Command::GETK {key} else {&[]},
                            &v.val.bytes[..]
                        ).transmit(c);
                    } else {
                        rh.construct(&[], &[], &[]).transmit(c);
                    };
                }
                memcache::Command::APPEND | memcache::Command::PREPEND => {
                    // XXX: unfortunate copy...
                    let b = body.to_vec();

                    let res = match op.noq() {
                        memcache::Command::PREPEND => m.prepend(key, b, req.cas),
                        memcache::Command::APPEND | _ => m.append(key, b, req.cas),
                    };

                    rh.status = res.0 as u16;
                    if let (memcache::Status::SUCCESS, Ok(v)) = res {
                        rh.cas = v.val.casid;
                    }

                    rh.construct(&[], &[], &[]).transmit(c);
                }
                memcache::Command::VERSION => {
                    rh.status = memcache::Status::SUCCESS as u16;
                    rh.construct(&[], &[], "0.1.0".as_bytes()).transmit(c);
                }
                memcache::Command::NOOP => {
                    rh.status = memcache::Status::SUCCESS as u16;
                    rh.construct(&[], &[], &[]).transmit(c);
                }
                _ => {
                    rh.status = memcache::Status::EINVAL as u16;
                    rh.construct(&[], &[], &[]).transmit(c);
                    panic!("client sent valid (but unhandled) command: {:?}", op);
                }
            }
        }
        None => {
            rh.status = memcache::Status::UNKNOWN_COMMAND as u16;
            rh.construct(&[], &[], &[]).transmit(c);
        }
    };
}

fn handle_client(m : &cucache::Map, mut c : net::TcpStream) {
    let mut magic = [0_u8; 1];
    let mut body = Vec::with_capacity(100);
    loop {
        if let Err(_) = read_full(&mut c, &mut magic) {
            return
        }

        unsafe { body.set_len(100); }
        body.shrink_to_fit();
        body.clear();
        unsafe { body.set_len(24); }
        match magic[0] {
            0x80 => {
                // binary protocol
                // memcache request
                body[0] = magic[0];
                if let Err(_) = read_full(&mut c, &mut body[1..24]) {
                    return
                }

                let blen = BigEndian::read_u32(&body[8..12]) as usize;
                body.reserve_exact(blen);
                unsafe { body.set_len(24 + blen); }

                if let Err(_) = read_full(&mut c, &mut body[24..(24+blen)]) {
                    return
                }

                let req = memcache::Request::parse(&mut body[..]);
                execute(m, req, &mut c);
            }
            0x81 => {
                warn!("expected request from client, got response magic");
                return
            }
            _ => {
                // text protocol
            }
        }
    }
}

fn handle_clients(m : &cucache::Map, rxmx : &sync::Mutex<mpsc::Receiver<net::TcpStream>>) {
    loop {
        if let Ok(rx) = rxmx.lock() {
            if let Ok(c) = rx.recv() {
                drop(rx);
                handle_client(&m, c);
            } else {
                // channel receive error
                return;
            }
        } else {
            // lock poisoned by panic
            return;
        }
    }
}

struct SimpleLogger;
impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Debug
    }

    #[allow(unused_must_use)]
    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            writeln!(&mut std::io::stderr(), "{} - {}", record.level(), record.args());
        }
    }
}

<commit_msg>Don't panic on interrupt<commit_after>#![feature(scoped)]

extern crate cucache;
use cucache::memcache;

extern crate getopts;
extern crate time;
extern crate byteorder;
use byteorder::{BigEndian, ByteOrder};

#[macro_use]
extern crate log;
use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata};

use std::io::Write;
use std::net;
use std::thread;
use std::env;
use std::str::FromStr;
use std::process;
use std::sync;
use std::sync::mpsc;
use std::io;
use std::str;

extern crate num;
use num::traits::FromPrimitive;

fn from_mctime(i : u32) -> i64 {
    if i > 0 && i < 30*24*60*60 {
        time::get_time().sec + i as i64
    } else {
        i as i64
    }
}

fn main() {
    let _ = log::set_logger(|max_log_level| {
        max_log_level.set(LogLevelFilter::Debug);
        Box::new(SimpleLogger)
    });

    let mut opts = getopts::Options::new();
    opts.optflagopt("p", "", "Listen on TCP port <num>, the default is port 11211.", "<num>");
    opts.optflagopt("U", "", "Listen on UDP port <num>, the default is port 11211, 0 is off.", "<num>");
    opts.optflag("h", "help", "display this help and exit");

    let args: Vec<String> = env::args().collect();
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            println!("{}", f);
            process::exit(1);
        }
    };

    if matches.opt_present("help") {
        let brief = format!("Usage: {} [options]", args[0]);
        println!("{}", opts.usage(&brief));
        return;
    }

    let host = "127.0.0.1";
    let mut tcp_port = 11211;
    if let Some(port) = matches.opt_str("p") {
        if let Ok(portn) = u16::from_str(&port) {
            tcp_port = portn;
        }
    }

    info!("listening on {}:tcp:{}", host, tcp_port);
    let listener = net::TcpListener::bind((host, tcp_port)).unwrap();

    info!("allocating map");
    let m = cucache::new(1 << 10);

    info!("spinning up worker pool");
    let (tx, rx) = mpsc::channel();
    let rxmx = sync::Mutex::new(rx);
    let pool : Vec<thread::JoinGuard<()>> = (1..100).map(|_| {
        let map = &m;
        let rxl = &rxmx;
        thread::scoped(move || {
            handle_clients(map, rxl);
        })
    }).collect();

    info!("accepting connections");
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                if let Err(e) = tx.send(stream) {
                    panic!("failed to queue incoming request {}", e);
                }
            }
            Err(e) => {
                warn!("{}", e);
            }
        }
    }

    drop(pool);
}

fn read_full(r : &mut io::Read, to : &mut [u8]) -> Result<(), io::Error> {
    let mut nread = 0 as usize;
    while nread < to.len() {
        match r.read(&mut to[nread..]) {
            Ok(n) => { nread += n; }
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
            Err(e) => return Err(From::from(e))
        }
    }
    Ok(())
}

fn execute(m : &cucache::Map, req : &memcache::Request, c : &mut net::TcpStream) {
    let mut rh = memcache::ResponseHeader::from_req(req);
    match memcache::Command::from_u8(req.op) {
        Some(op) => {
            let key = req.key();
            let extras = req.extras();
            let body = req.body();

            match op.noq() {
                memcache::Command::FLUSH if extras.len() == 4 => {
                    let mut tm = from_mctime(BigEndian::read_u32(extras));
                    if tm == 0 {
                        tm = time::get_time().sec;
                    }
                    m.touchall(tm);

                    rh.status = memcache::Status::SUCCESS as u16;
                    rh.construct(&[], &[], &[]).transmit(c);
                }
                memcache::Command::SET
                | memcache::Command::ADD
                | memcache::Command::REPLACE
                if extras.len() == 8 => {
                    let flags = BigEndian::read_u32(&extras[0..4]);
                    let expires = from_mctime(BigEndian::read_u32(&extras[4..8]));

                    // XXX: unfortunate copy...
                    let b = body.to_vec();

                    let res = match op.noq() {
                        memcache::Command::SET if req.cas == 0 => m.set(key, b, flags, expires),
                        memcache::Command::REPLACE if req.cas == 0 => m.replace(key, b, flags, expires),
                        memcache::Command::ADD => m.add(key, b, flags, expires),
                        memcache::Command::SET | memcache::Command::REPLACE | _ => {
                            m.cas(key, b, flags, expires, req.cas)
                        }
                    };

                    rh.status = res.0 as u16;
                    if let (memcache::Status::SUCCESS, Ok(v)) = res {
                        rh.cas = v.val.casid;
                    }

                    rh.construct(&[], &[], &[]).transmit(c);
                }
                memcache::Command::DELETE => {
                    rh.status = m.delete(key, req.cas).0 as u16;
                    rh.construct(&[], &[], &[]).transmit(c);
                }
                memcache::Command::INCREMENT
                | memcache::Command::DECREMENT
                if extras.len() == 20 => {
                    let by = BigEndian::read_u64(&extras[0..8]);
                    let def = BigEndian::read_u64(&extras[8..16]);
                    let expires = from_mctime(BigEndian::read_u32(&extras[16..20]));

                    let v = match op.noq() {
                        memcache::Command::INCREMENT => m.incr(key, by, def, expires),
                        memcache::Command::DECREMENT | _ => m.decr(key, by, def, expires),
                    };

                    rh.status = v.0 as u16;
                    if let (memcache::Status::SUCCESS, Ok(v)) = v {
                        let extras = &mut [0u8; 8];
                        BigEndian::write_u64(&mut extras[..], u64::from_str(str::from_utf8(&v.val.bytes[..]).unwrap()).unwrap());

                        rh.cas = v.val.casid;
                        rh.construct(&extras[..], &[], &[]).transmit(c);
                    } else {
                        rh.construct(&[], &[], &[]).transmit(c);
                    }
                }
                memcache::Command::GET | memcache::Command::GETK => {
                    let v = m.get(key);
                    rh.status = v.0 as u16;

                    if let (memcache::Status::SUCCESS, Ok(v)) = v {
                        rh.cas = v.val.casid;
                        let extras = &mut [0u8; 4];
                        BigEndian::write_u32(&mut extras[..], v.val.flags);
                        rh.construct(
                            &extras[..],
                            if op.noq() == memcache::Command::GETK {key} else {&[]},
                            &v.val.bytes[..]
                        ).transmit(c);
                    } else {
                        rh.construct(&[], &[], &[]).transmit(c);
                    };
                }
                memcache::Command::APPEND | memcache::Command::PREPEND => {
                    // XXX: unfortunate copy...
                    let b = body.to_vec();

                    let res = match op.noq() {
                        memcache::Command::PREPEND => m.prepend(key, b, req.cas),
                        memcache::Command::APPEND | _ => m.append(key, b, req.cas),
                    };

                    rh.status = res.0 as u16;
                    if let (memcache::Status::SUCCESS, Ok(v)) = res {
                        rh.cas = v.val.casid;
                    }

                    rh.construct(&[], &[], &[]).transmit(c);
                }
                memcache::Command::VERSION => {
                    rh.status = memcache::Status::SUCCESS as u16;
                    rh.construct(&[], &[], "0.1.0".as_bytes()).transmit(c);
                }
                memcache::Command::NOOP => {
                    rh.status = memcache::Status::SUCCESS as u16;
                    rh.construct(&[], &[], &[]).transmit(c);
                }
                _ => {
                    rh.status = memcache::Status::EINVAL as u16;
                    rh.construct(&[], &[], &[]).transmit(c);
                    panic!("client sent valid (but unhandled) command: {:?}", op);
                }
            }
        }
        None => {
            rh.status = memcache::Status::UNKNOWN_COMMAND as u16;
            rh.construct(&[], &[], &[]).transmit(c);
        }
    };
}

fn handle_client(m : &cucache::Map, mut c : net::TcpStream) {
    let mut magic = [0_u8; 1];
    let mut body = Vec::with_capacity(100);
    loop {
        if let Err(_) = read_full(&mut c, &mut magic) {
            return
        }

        unsafe { body.set_len(100); }
        body.shrink_to_fit();
        body.clear();
        unsafe { body.set_len(24); }
        match magic[0] {
            0x80 => {
                // binary protocol
                // memcache request
                body[0] = magic[0];
                if let Err(_) = read_full(&mut c, &mut body[1..24]) {
                    return
                }

                let blen = BigEndian::read_u32(&body[8..12]) as usize;
                body.reserve_exact(blen);
                unsafe { body.set_len(24 + blen); }

                if let Err(_) = read_full(&mut c, &mut body[24..(24+blen)]) {
                    return
                }

                let req = memcache::Request::parse(&mut body[..]);
                execute(m, req, &mut c);
            }
            0x81 => {
                warn!("expected request from client, got response magic");
                return
            }
            _ => {
                // text protocol
            }
        }
    }
}

fn handle_clients(m : &cucache::Map, rxmx : &sync::Mutex<mpsc::Receiver<net::TcpStream>>) {
    loop {
        if let Ok(rx) = rxmx.lock() {
            if let Ok(c) = rx.recv() {
                drop(rx);
                handle_client(&m, c);
            } else {
                // channel receive error
                return;
            }
        } else {
            // lock poisoned by panic
            return;
        }
    }
}

struct SimpleLogger;
impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Debug
    }

    #[allow(unused_must_use)]
    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            writeln!(&mut std::io::stderr(), "{} - {}", record.level(), record.args());
        }
    }
}

<|endoftext|>


<commit_before>#![feature(core)]
#![feature(std_misc)]

extern crate time;
extern crate "rustc-serialize" as rustc_serialize;

use std::io::prelude::*;
use std::os::unix::prelude::*;
use std::io::BufReader;
use std::thread;
use std::process::{Command, Stdio};
use std::collections::HashMap;
use std::sync::mpsc::{Receiver, Sender, channel};

use rustc_serialize::json;

#[derive(Debug)]
#[derive(Clone)]
pub enum LogSource {
  ControlSystem,
  StdOut,
  StdErr,
  BuildSystem,
}

impl LogSource {
  pub fn to_string(&self) -> String {
    match *self {
      LogSource::ControlSystem => "Control System",
      LogSource::StdOut => "stdout",
      LogSource::StdErr => "stderr",
      LogSource::BuildSystem => "Log System",
    }.to_string()
  }
}

pub fn to_string_ts(ti: time::Tm) -> String {
    let format = "%Y-%m-%d %T.%f";
    let mut ts = time::strftime(format, &ti).ok().unwrap();
    let l = ts.len();
    ts.truncate(l-6);
    ts
}

#[derive(Debug)]
pub struct TimestampedLine {
  source: LogSource,
  time: time::Tm,
  content: String,
}

impl TimestampedLine {
  pub fn tsl(source: LogSource, time: time::Tm, content: String) -> HashMap<String, String> {
    let mut line = HashMap::new();
    line.insert("source".to_string(), source.to_string());
    line.insert("time".to_string(), to_string_ts(time.to_utc()));
    line.insert("content".to_string(), content);

    line
  }

  pub fn msg(reason: String) -> HashMap<String, String> {
    TimestampedLine::tsl(LogSource::BuildSystem, time::now(), reason)
  }
  pub fn stop_writer() -> HashMap<String, String> {
    TimestampedLine::tsl(LogSource::ControlSystem, time::now(), "stop".to_string())
  }
}

pub struct Writer;

impl Writer {
  fn run(&self, receiver: Receiver<HashMap<String, String>> ) {
    let mut stop = 3;
    while stop != 0 {
      match receiver.recv() {
        Ok(tl) => {
          // This is a stop command, decrement counter. We need to wait for main, stderr, stdout before stopping
          if tl.get("source") == Some(&LogSource::ControlSystem.to_string()) && tl.get("content") == Some(&"stop".to_string()) {
              stop = stop - 1;
          } else {
            let encoded = json::encode(&tl).unwrap();
            println!("{}", encoded);
          }
        }
        Err(e) => println!("Receive error: {}", e),
      }
    }
  }
}

pub struct Runner;

impl Runner {
	fn run(&self, cmd: &str, parms: Vec<String>) {

    let (sender, receiver) = channel();

    let writer = Writer;
    let th = thread::spawn(move || {
      writer.run(receiver);
    });

    sender.send(TimestampedLine::msg(format!("Processing {}", cmd))).unwrap();

    let start = time::now();
    let mut child = match Command::new(&cmd)
      .args(parms.as_slice())
      .current_dir(".")
      .stdin(Stdio::null())
      .stdout(Stdio::piped())
      .stderr(Stdio::piped())
      .spawn() {
        Err(why) => panic!("couldn't spawn {}: {}", &cmd, why.to_string()),
        Ok(child) => child,
    };

    //https://github.com/rust-lang/rust/blob/b83b26bacb6371173cdec6bf68c7ffa69f858c84/src/libstd/process.rs
    fn read_timestamped_lines<T: Read + Send + 'static>(stream: Option<T>, source: LogSource, sender: Sender<HashMap<String, String>>) {
      match stream {
        Some(stream) => {
          thread::spawn(move || {
            let mut br = BufReader::with_capacity(64, stream);
            while {
              let mut line = String::new();
              let ok = match br.read_line(&mut line) {
                Ok(0) => false,
                Ok(_) => true,
                Err(e) => {println!("Something went wrong while reading the data: {}", e.to_string()); false}
              };
              if ok {
                let now = time::now();
                sender.send(TimestampedLine::tsl(source.clone(), now, line[0..line.len()-1].to_string())).unwrap();
              }
              ok
            } {}
            sender.send(TimestampedLine::stop_writer()).unwrap();
          });
        }
        None => sender.send(TimestampedLine::msg("Stream is None, not reading anything.".to_string())).unwrap()
      }
    }
    read_timestamped_lines(child.stdout.take(), LogSource::StdOut, sender.clone());
    read_timestamped_lines(child.stderr.take(), LogSource::StdErr, sender.clone());
    let status = child.wait();
    let end = time::now();

    let duration = (end-start).num_milliseconds().to_string();

    let mut end_line = match status {
    Ok(es) => if es.success() {
        let mut msg = TimestampedLine::msg("Process completed successfully.".to_string());
        msg.insert("completed".to_string(), "0".to_string());
        msg
      } else if let Some(ecode) = es.code() {
        TimestampedLine::msg(format!("Process completed with error code {}.", ecode))
      } else if let Some(esignal) = es.signal() {
        TimestampedLine::msg(format!("Process aborted with signal {}.", esignal))
      } else {
        TimestampedLine::msg("Non-reachable condition reached. Something's wrong".to_string())
      },
      Err(run_error) => TimestampedLine::msg(format!("Something went wrong while getting the command output: {:?}", run_error)),
    };

    end_line.insert("duration".to_string(), duration);
    sender.send(end_line).unwrap();

    sender.send(TimestampedLine::stop_writer()).unwrap();
    let _ = th.join();
	}
}

#[derive(Debug)]
pub struct Invocation {
  command: String,
  parameters: Vec<String>,
  started: time::Tm,
  ended: time::Tm,
  exitstatus: Option<isize>,
  exitsignal: Option<isize>,
  logs: Vec<TimestampedLine>
}

fn main() {
  let r = Runner;

  r.run("ls", Vec::<String>::new());
}
<commit_msg>Writes logs to an ajson file<commit_after>#![feature(core)]
#![feature(std_misc)]

extern crate time;
extern crate "rustc-serialize" as rustc_serialize;

use std::io::prelude::*;
use std::os::unix::prelude::*;
use std::io::BufReader;
use std::fs::File;
use std::fs::create_dir_all;
use std::path::Path;
use std::thread;
use std::process::{Command, Stdio};
use std::collections::HashMap;
use std::sync::mpsc::{Receiver, Sender, channel};

use rustc_serialize::json;

#[derive(Debug)]
#[derive(Clone)]
pub enum LogSource {
  ControlSystem,
  StdOut,
  StdErr,
  BuildSystem,
}

impl LogSource {
  pub fn to_string(&self) -> String {
    match *self {
      LogSource::ControlSystem => "Control System",
      LogSource::StdOut => "stdout",
      LogSource::StdErr => "stderr",
      LogSource::BuildSystem => "Log System",
    }.to_string()
  }
}

pub fn to_string_ts(ti: time::Tm) -> String {
    let format = "%Y-%m-%d %T.%f";
    let mut ts = time::strftime(format, &ti).ok().unwrap();
    let l = ts.len();
    ts.truncate(l-6);
    ts
}

#[derive(Debug)]
pub struct TimestampedLine {
  source: LogSource,
  time: time::Tm,
  content: String,
}

impl TimestampedLine {
  pub fn tsl(source: LogSource, time: time::Tm, content: String) -> HashMap<String, String> {
    let mut line = HashMap::new();
    line.insert("source".to_string(), source.to_string());
    line.insert("time".to_string(), to_string_ts(time.to_utc()));
    line.insert("content".to_string(), content);

    line
  }

  pub fn msg(reason: String) -> HashMap<String, String> {
    TimestampedLine::tsl(LogSource::BuildSystem, time::now(), reason)
  }
  pub fn stop_writer() -> HashMap<String, String> {
    TimestampedLine::tsl(LogSource::ControlSystem, time::now(), "stop".to_string())
  }
}

pub struct Writer;

impl Writer {
  fn run(&self, destination_file:String,  receiver: Receiver<HashMap<String, String>> ) {
    let destination = Path::new(destination_file.as_slice());
    std::fs::create_dir_all(destination.parent().unwrap()).ok();
    println!("Writing to {}.", destination_file);
    match File::create(destination) {
      Ok(mut file) => {
        let mut stop = 3;
        while stop != 0 {
          match receiver.recv() {
            Ok(tl) => {
              // This is a stop command, decrement counter. We need to wait for main, stderr, stdout before stopping
              if tl.get("source") == Some(&LogSource::ControlSystem.to_string()) && tl.get("content") == Some(&"stop".to_string()) {
                  stop = stop - 1;
              } else {
                let encoded = json::encode(&tl).unwrap();
                println!("{:?}", tl);
                file.write_all(&encoded.into_bytes()).ok();
                file.write_all(b"\n").ok();
              }
            }
            Err(e) => println!("Receive error: {}", e),
          }
        }
      },
      Err(e) => panic!("Unable to open output file {}: {}", destination_file, e.to_string())
    }
  }
}

pub struct Runner;

impl Runner {
	fn run(&self, cmd: &str, parms: Vec<String>)  {
    let start = time::now();

    let cmd_path = Path::new(cmd);
    let fname = match cmd_path.file_name() {
      Some(f) => {
        let mut pth = time::strftime("./logs/%Y/%m/%d/", &start).ok().unwrap();
        let postfix = time::strftime("-%T.ajson", &start).ok().unwrap();
        let osname = f.to_string_lossy().into_owned();
        pth.push_str(&osname);
        pth.push_str(&postfix);
        pth
      },
      None => panic!("Unable to find file name for {}", cmd),
    };

    let (sender, receiver) = channel();
    let writer = Writer;
    let th = thread::spawn(move || {
      writer.run(fname, receiver);
    });

    sender.send(TimestampedLine::msg(format!("Processing {}", cmd))).unwrap();

    let mut child = match Command::new(&cmd)
      .args(parms.as_slice())
      .current_dir(".")
      .stdin(Stdio::null())
      .stdout(Stdio::piped())
      .stderr(Stdio::piped())
      .spawn() {
        Err(why) => panic!("couldn't spawn {}: {}", &cmd, why.to_string()),
        Ok(child) => child,
    };

    //https://github.com/rust-lang/rust/blob/b83b26bacb6371173cdec6bf68c7ffa69f858c84/src/libstd/process.rs
    fn read_timestamped_lines<T: Read + Send + 'static>(stream: Option<T>, source: LogSource, sender: Sender<HashMap<String, String>>) {
      match stream {
        Some(stream) => {
          thread::spawn(move || {
            let mut br = BufReader::with_capacity(64, stream);
            while {
              let mut line = String::new();
              let ok = match br.read_line(&mut line) {
                Ok(0) => false,
                Ok(_) => true,
                Err(e) => {println!("Something went wrong while reading the data: {}", e.to_string()); false}
              };
              if ok {
                let now = time::now();
                sender.send(TimestampedLine::tsl(source.clone(), now, line[0..line.len()-1].to_string())).unwrap();
              }
              ok
            } {}
            sender.send(TimestampedLine::stop_writer()).unwrap();
          });
        }
        None => sender.send(TimestampedLine::msg("Stream is None, not reading anything.".to_string())).unwrap()
      }
    }
    read_timestamped_lines(child.stdout.take(), LogSource::StdOut, sender.clone());
    read_timestamped_lines(child.stderr.take(), LogSource::StdErr, sender.clone());
    let status = child.wait();
    let end = time::now();

    let duration = (end-start).num_milliseconds().to_string();

    let mut end_line = match status {
    Ok(es) => if es.success() {
        let mut msg = TimestampedLine::msg("Process completed successfully.".to_string());
        msg.insert("completed".to_string(), "0".to_string());
        msg
      } else if let Some(ecode) = es.code() {
        TimestampedLine::msg(format!("Process completed with error code {}.", ecode))
      } else if let Some(esignal) = es.signal() {
        TimestampedLine::msg(format!("Process aborted with signal {}.", esignal))
      } else {
        TimestampedLine::msg("Non-reachable condition reached. Something's wrong".to_string())
      },
      Err(run_error) => TimestampedLine::msg(format!("Something went wrong while getting the command output: {:?}", run_error)),
    };

    end_line.insert("duration".to_string(), duration);
    sender.send(end_line).unwrap();

    sender.send(TimestampedLine::stop_writer()).unwrap();
    // Wait for writer thread to complete its writes
    let _ = th.join();
	}
}

fn main() {
  let r = Runner;

  r.run("/bin/ls", Vec::<String>::new());
}
<|endoftext|>


<commit_before>//modules

mod config;
mod word;

//extern crates

extern crate rand;
extern crate rustc_serialize;
extern crate regex;
extern crate unicode_segmentation;

//use

use unicode_segmentation::UnicodeSegmentation;
use config::*;
use word::*;
use std::io::prelude::*;
//use rustc_serialize::*;    //commented out as it is only used to emit test configs
use std::fs::File;
use rand::distributions::Weighted;

//static variables

static CONFIG_FILE_NAME: &'static str = "main_config.json";

//functions

fn main() {
    let config: WordGeneratorConfig = load_config(&CONFIG_FILE_NAME);

    //comment other code and uncomment these to emit a new test config
    //let mut file = File::create(&CONFIG_FILE_NAME).unwrap();

    /*match file.write(json::encode(&config).unwrap().as_bytes()) {
        Ok(_) => (),
        Err(err) => panic!("Error writing test config: {}", err),
    }*/

    let mut first_syllable_list : Vec<Weighted<String>> = Vec::new();
    let mut last_syllable_list : Vec<Weighted<String>> = Vec::new();
    let mut normal_syllable_list : Vec<Weighted<String>> = Vec::new();

    //In-memory, syllables are stored as 'first-only', 'last only', and 'rest of the word' syllable lists.
    //Aside from the impracticality of iterating and manipulating the config data directly every time I
    //want to pick a syllable, I chose this structure as it made implementing syllable construction
    //straightforward while allowing syllables to be constrained to first or last positions. This may end
    //up getting removed later.

    for syllable in &config.syllables {
        let syllable_weighted : Weighted<String> = Weighted{
            weight: syllable.weight as u32,
            item: syllable.string.clone()
        };
        if !(syllable.only_first_syllable || syllable.only_last_syllable) {
            normal_syllable_list.push(syllable_weighted.clone());
            first_syllable_list.push(syllable_weighted.clone());
            last_syllable_list.push(syllable_weighted.clone());
        }
        if syllable.only_first_syllable {
            first_syllable_list.push(syllable_weighted.clone());
        }
        if syllable.only_last_syllable {
            last_syllable_list.push(syllable_weighted.clone());
        }

    }

    //transform_graphemes is used to convert Graphemes to Weighted<String>s. This is done because
    let grapheme_groups : Vec<(String, Vec<Weighted<String>>)> = transform_graphemes(&config.graphemes);

    let word_factory : WordFactory = WordFactory {
        first_syllable_list : first_syllable_list,
        normal_syllable_list : normal_syllable_list,
        last_syllable_list : last_syllable_list,
        graphemes : grapheme_groups,
        generate_settings : config.generate_settings.clone(),
        rewrites : config.rewrites.clone(),
        rejects : config.rejects.clone(),
    };

    write_list(&generate_word_list(&config, &word_factory), &config);
}

fn write_list(word_list: &Vec<Word>, config : &WordGeneratorConfig) {
    let mut file = File::create(&config.output_settings.output_file).unwrap();

    for word in word_list {
        let mut result : String = format!("{}{}", "Word: ", get_word_graphemes(&word));

        if config.output_settings.show_syllable_strings {
            result = format!("{} ({} syllable(s) {})", result, &word.syllable_count, get_word_syllables(&word));
        }

        //note: we are specifically continue-ing out of this loop in a sub-if to implement mark vs drop behavior
        if word.syllable_rejects.len() > 0 || word.grapheme_rejects.len() > 0 {
            if config.output_settings.only_mark_rejects {
                result = format!("{}{}", result, format_word_rejects(&word));
            } else {
                continue;
            }
        }

        if config.output_settings.show_word_rewrites {
            let mut show_no_rewrites_message : bool = true;
            if config.output_settings.show_word_rewrites && config.output_settings.show_syllable_strings && word.syllable_rewrite_history.len() > 0 {
                result = format!("{}\n-Syllable rewrites:\n{}", result, format_transforms(&word.syllables, &word.syllable_rewrite_history));
                show_no_rewrites_message = false;
            }
            if config.output_settings.show_word_rewrites && word.grapheme_rewrite_history.len() > 0 {
                result = format!("{}\n-Grapheme rewrites:\n{}", result, format_transforms(&word.graphemes, &word.grapheme_rewrite_history));
                show_no_rewrites_message = false;
            }
            if word.syllable_rewrite_history.len() > 0 || word.grapheme_rewrite_history.len() > 0 {
                result = format!("{}\n", result);
            } else if show_no_rewrites_message { //please note, will not put no rewrites applied if syllable display is off and there are applied syllable rewrites - fix later
                result = format!("{} (No rewrites applied)", result);
            }
        }
        result = format!("{}\n", result);
        match file.write(result.as_bytes()) {
                Err(error) => panic!("error {} writing to file", error),
                Ok(_) => (),
        };
    }
}

fn format_transforms(original : &String, rewrite_history : &Vec<(Rewrite, String)>) -> String {
    let mut result : String = "---------\n".to_string();
    let mut current_previous_word : &String = &original.clone();
    for rewrite in rewrite_history {
        result = format!("-{}\n", format_individual_transform(&rewrite, &current_previous_word));
        current_previous_word = &rewrite.1;
    }
    format!("{}-final: {}\n---------", result, current_previous_word)
}
fn format_individual_transform(transform : &(Rewrite, String), previous : &String) -> String {
    format!("{} --> {} ({} --> {})", previous, transform.1, transform.0.pattern, transform.0.replace)
}

//ultimate goal is to emit formatted strings like this:
//qwe: due to syllable rejects []
//qwe: due to grapheme rejects []
//qwe: due to syllable rejects [] and grapheme rejects []
fn format_word_rejects(word : &Word) -> String {
    if word.syllable_rejects.len() == 0 && word.grapheme_rejects.len() == 0 { return String::new(); }
    let mut result : String = " (rejected due to ".to_string();
    match word.syllable_rejects.len() {
        0 => (),
        1 => {
            result.push_str("syllable reject ");
            result.push_str(&word.syllable_rejects[0]);

        },
        2 => {
            result.push_str("syllable rejects ");
            result.push_str(&word.syllable_rejects[0]);
            result.push_str("and ");
            result.push_str(&word.syllable_rejects[1]);
        },
        _ => {
            result.push_str("syllable rejects ");

            //right now, rust has no easy way to trim arbitrary numbers of characters off the end of a string - so i have to count manually and stop adding commas at the last reject entry, otherwise there will be an errant ", " added to the end
            let mut count : usize = 0;
            let length = word.syllable_rejects.len();
            for reject in &word.syllable_rejects {
                result.push_str(&reject);
                if count < length - 1 {
                    result.push_str(", ");
                }

                count = count + 1;
            }

        },
    }
    if word.syllable_rejects.len() > 0 && word.grapheme_rejects.len() > 0 {
        result.push_str(", and due to ");
    }
    match word.grapheme_rejects.len() {
        0 => (),
        1 => {
            result.push_str("grapheme reject ");
            result.push_str(&word.grapheme_rejects[0]);

        },
        2 => {
            result.push_str("grapheme rejects ");
            result.push_str(&word.grapheme_rejects[0]);
            result.push_str("and ");
            result.push_str(&word.grapheme_rejects[1]);
        },
        _ => {
            result.push_str("grapheme rejects ");

            //right now, rust has no easy way to trim arbitrary numbers of characters off the end of a string - so i have to count manually and stop adding commas at the last reject entry, otherwise there will be an errant ", " added to the end
            let mut count : usize = 0;
            let length = word.grapheme_rejects.len();
            for reject in &word.grapheme_rejects {
                result.push_str(&reject);
                if count < length - 1 {
                    result.push_str(", ");
                }

                count = count + 1;
            }
        },
    }
    result.push_str(")");
    result
}

fn generate_word_list(config : &WordGeneratorConfig, word_factory : &WordFactory) -> Vec<Word> {
    let mut word_list : Vec<Word> = Vec::new();

    while word_list.iter().filter(|word| word.syllable_rejects.len() == 0 && word.grapheme_rejects.len() == 0).count() < config.output_settings.word_count {

        let mut word : Word = Word {
            syllables : String::new(),
            graphemes : String::new(),
            syllable_rewrite_history : Vec::new(),
            grapheme_rewrite_history : Vec::new(),
            syllable_rejects : Vec::new(),
            grapheme_rejects : Vec::new(),
            syllable_count : 0
        };

        word_factory.generate_syllables(&mut word);

        //at first glance this doesnt look like it's doing much - marking rejects operate on the final
        //word string so rewriting before or after will make a difference
        if config.generate_settings.rewrites_before_rejects {
            word_factory.rewrite_syllables(&mut word);
            word_factory.mark_syllable_rejects(&mut word);
        } else {
            word_factory.mark_syllable_rejects(&mut word);
            word_factory.rewrite_syllables(&mut word);
        };

        word_factory.generate_graphemes(&mut word);

        if config.generate_settings.rewrites_before_rejects {
            word_factory.rewrite_graphemes(&mut word);
            word_factory.mark_grapheme_rejects(&mut word);
        } else {
            word_factory.mark_grapheme_rejects(&mut word);
            word_factory.rewrite_graphemes(&mut word);
        };
        word_list.push(word);
    }
    word_list
}

//unwraps a list of GraphemeGroups into a tuple list of String and Vec<Weighted<String>>
//Stored as a separate structure because JSON data would not be stored as desired for config otherwise
fn transform_graphemes(graphemes : &Vec<GraphemeGroup>) -> Vec<(String, Vec<Weighted<String>>)> {

    let mut grapheme_groups : Vec<(String, Vec<Weighted<String>>)> = Vec::new();

    for group in graphemes {
        let mut graphemes_converted : Vec<Weighted<String>> = Vec::new();

        graphemes_converted.extend(
            group.graphemes.iter().map(
                |g: &Grapheme| Weighted{weight: g.weight as u32, item: g.string.clone() }
                )
            );
        grapheme_groups.push((group.name.clone(), graphemes_converted));
    }
    grapheme_groups
}
<commit_msg>cleaned up formatting and comments in main.rs<commit_after>//modules

mod config;
mod word;

//extern crates

extern crate rand;
extern crate rustc_serialize;
extern crate regex;
extern crate unicode_segmentation;

//use

use unicode_segmentation::UnicodeSegmentation;
use config::*;
use word::*;
use std::io::prelude::*;
//use rustc_serialize::*;    //commented out as it is only used to emit test configs
use std::fs::File;
use rand::distributions::Weighted;

//static variables

static CONFIG_FILE_NAME: &'static str = "main_config.json";

//functions

fn main() {
    let config: WordGeneratorConfig = load_config(&CONFIG_FILE_NAME);

    //comment other code and uncomment these to emit a new test config
    //let mut file = File::create(&CONFIG_FILE_NAME).unwrap();

    /*match file.write(json::encode(&config).unwrap().as_bytes()) {
        Ok(_) => (),
        Err(err) => panic!("Error writing test config: {}", err),
    }*/

    let mut first_syllable_list : Vec<Weighted<String>> = Vec::new();
    let mut last_syllable_list : Vec<Weighted<String>> = Vec::new();
    let mut normal_syllable_list : Vec<Weighted<String>> = Vec::new();

    //In-memory, syllables are stored as 'first-only', 'last only', and 'rest of the word' syllable lists.
    //Aside from the impracticality of iterating and manipulating the config data directly every time I
    //want to pick a syllable, I chose this structure as it made implementing syllable construction
    //straightforward while allowing syllables to be constrained to first or last positions. This may end
    //up getting removed later.

    for syllable in &config.syllables {
        let syllable_weighted : Weighted<String> = Weighted{
            weight: syllable.weight as u32,
            item: syllable.string.clone()
        };
        if !(syllable.only_first_syllable || syllable.only_last_syllable) {
            normal_syllable_list.push(syllable_weighted.clone());
            first_syllable_list.push(syllable_weighted.clone());
            last_syllable_list.push(syllable_weighted.clone());
        }
        if syllable.only_first_syllable {
            first_syllable_list.push(syllable_weighted.clone());
        }
        if syllable.only_last_syllable {
            last_syllable_list.push(syllable_weighted.clone());
        }

    }

    //transform_graphemes is used to convert Graphemes to Weighted<String>s. This is done because
    let grapheme_groups : Vec<(String, Vec<Weighted<String>>)> = transform_graphemes(&config.graphemes);

    let word_factory : WordFactory = WordFactory {
        first_syllable_list : first_syllable_list,
        normal_syllable_list : normal_syllable_list,
        last_syllable_list : last_syllable_list,
        graphemes : grapheme_groups,
        generate_settings : config.generate_settings.clone(),
        rewrites : config.rewrites.clone(),
        rejects : config.rejects.clone(),
    };

    write_list(&generate_word_list(&config, &word_factory), &config);
}

fn write_list(word_list: &Vec<Word>, config : &WordGeneratorConfig) {
    let mut file = File::create(&config.output_settings.output_file).unwrap();

    for word in word_list {
        let mut result : String = format!("{}{}", "Word: ", get_word_graphemes(&word));

        if config.output_settings.show_syllable_strings {
            result = format!("{} ({} syllable(s) {})", result, &word.syllable_count, get_word_syllables(&word));
        }

        //note: we are specifically continue-ing out of this loop in a sub-if to implement mark vs drop behavior
        if word.syllable_rejects.len() > 0 || word.grapheme_rejects.len() > 0 {
            if config.output_settings.only_mark_rejects {
                result = format!("{}{}", result, format_word_rejects(&word));
            }
            else {
                continue;
            }
        }

        if config.output_settings.show_word_rewrites {
            let mut show_no_rewrites_message : bool = true;

            if config.output_settings.show_word_rewrites && config.output_settings.show_syllable_strings && word.syllable_rewrite_history.len() > 0 {
                result = format!("{}\n-Syllable rewrites:\n{}", result, format_transforms(&word.syllables, &word.syllable_rewrite_history));
                show_no_rewrites_message = false;
            }

            if config.output_settings.show_word_rewrites && word.grapheme_rewrite_history.len() > 0 {
                result = format!("{}\n-Grapheme rewrites:\n{}", result, format_transforms(&word.graphemes, &word.grapheme_rewrite_history));
                show_no_rewrites_message = false;
            }

            if word.syllable_rewrite_history.len() > 0 || word.grapheme_rewrite_history.len() > 0 {
                result = format!("{}\n", result);
            }
            else if show_no_rewrites_message { //please note, will not put no rewrites applied if syllable display is off and there are applied syllable rewrites - fix later
                result = format!("{} (No rewrites applied)", result);
            }
        }

        result = format!("{}\n", result);

        match file.write(result.as_bytes()) {
                Err(error) => panic!("error {} writing to file", error),
                Ok(_) => (),
        };
    }
}

fn format_transforms(original : &String, rewrite_history : &Vec<(Rewrite, String)>) -> String {
    let mut result : String = "---------\n".to_string();
    let mut current_previous_word : &String = &original.clone();

    for rewrite in rewrite_history {
        result = format!("-{}\n", format_individual_transform(&rewrite, &current_previous_word));
        current_previous_word = &rewrite.1;
    }

    format!("{}-final: {}\n---------", result, current_previous_word)
}

fn format_individual_transform(transform : &(Rewrite, String), previous : &String) -> String {
    format!("{} --> {} ({} --> {})", previous, transform.1, transform.0.pattern, transform.0.replace)
}

fn format_word_rejects(word : &Word) -> String {
    if word.syllable_rejects.len() == 0 && word.grapheme_rejects.len() == 0 { return String::new(); }
    let mut result : String = " (rejected due to ".to_string();

    match word.syllable_rejects.len() {
        0 => (),
        1 => {
            result.push_str("syllable reject ");
            result.push_str(&word.syllable_rejects[0]);
        },
        2 => {
            result.push_str("syllable rejects ");
            result.push_str(&word.syllable_rejects[0]);
            result.push_str("and ");
            result.push_str(&word.syllable_rejects[1]);
        },
        _ => {
            result.push_str("syllable rejects ");

            //right now, rust has no easy way to trim arbitrary numbers of characters off the end of a string - so i have to count manually and stop adding commas at the last reject entry, otherwise there will be an errant ", " added to the end
            let mut count : usize = 0;
            let length = word.syllable_rejects.len();
            for reject in &word.syllable_rejects {
                result.push_str(&reject);
                if count < length - 1 {
                    result.push_str(", ");
                }

                count = count + 1;
            }

        },
    }

    if word.syllable_rejects.len() > 0 && word.grapheme_rejects.len() > 0 {
        result.push_str(", and due to ");
    }

    match word.grapheme_rejects.len() {
        0 => (),
        1 => {
            result.push_str("grapheme reject ");
            result.push_str(&word.grapheme_rejects[0]);

        },
        2 => {
            result.push_str("grapheme rejects ");
            result.push_str(&word.grapheme_rejects[0]);
            result.push_str("and ");
            result.push_str(&word.grapheme_rejects[1]);
        },
        _ => {
            result.push_str("grapheme rejects ");

            //right now, rust has no easy way to trim arbitrary numbers of characters off the end of a string
            //so i have to count manually and stop adding commas at the last reject entry, otherwise there
            //will be an errant ", " added to the end
            let mut count : usize = 0;
            let length = word.grapheme_rejects.len();

            for reject in &word.grapheme_rejects {
                result.push_str(&reject);
                if count < length - 1 {
                    result.push_str(", ");
                }
                count = count + 1;
            }
        },
    }

    result.push_str(")");
    result
}

fn generate_word_list(config : &WordGeneratorConfig, word_factory : &WordFactory) -> Vec<Word> {
    let mut word_list : Vec<Word> = Vec::new();

    while word_list.iter().filter(|word| word.syllable_rejects.len() == 0 && word.grapheme_rejects.len() == 0).count() < config.output_settings.word_count {
        let mut word : Word = Word {
            syllables : String::new(),
            graphemes : String::new(),
            syllable_rewrite_history : Vec::new(),
            grapheme_rewrite_history : Vec::new(),
            syllable_rejects : Vec::new(),
            grapheme_rejects : Vec::new(),
            syllable_count : 0
        };

        word_factory.generate_syllables(&mut word);

        //at first glance this doesnt look like it's doing much - marking rejects operate on the final
        //word string so rewriting before or after will make a difference
        if config.generate_settings.rewrites_before_rejects {
            word_factory.rewrite_syllables(&mut word);
            word_factory.mark_syllable_rejects(&mut word);
        } else {
            word_factory.mark_syllable_rejects(&mut word);
            word_factory.rewrite_syllables(&mut word);
        };

        word_factory.generate_graphemes(&mut word);

        if config.generate_settings.rewrites_before_rejects {
            word_factory.rewrite_graphemes(&mut word);
            word_factory.mark_grapheme_rejects(&mut word);
        } else {
            word_factory.mark_grapheme_rejects(&mut word);
            word_factory.rewrite_graphemes(&mut word);
        };

        word_list.push(word);
    }
    word_list
}

//unwraps a list of GraphemeGroups into a tuple list of String and Vec<Weighted<String>>
//Stored as a separate structure because JSON data would not be stored as desired for config otherwise
fn transform_graphemes(graphemes : &Vec<GraphemeGroup>) -> Vec<(String, Vec<Weighted<String>>)> {
    let mut grapheme_groups : Vec<(String, Vec<Weighted<String>>)> = Vec::new();

    for group in graphemes {
        let mut graphemes_converted : Vec<Weighted<String>> = Vec::new();

        graphemes_converted.extend(
            group.graphemes.iter().map(
                |g: &Grapheme| Weighted{weight: g.weight as u32, item: g.string.clone() }
                )
            );

        grapheme_groups.push((group.name.clone(), graphemes_converted));
    }
    grapheme_groups
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

mod git;

extern crate getopts;
extern crate hyper;
extern crate oauth2;
extern crate queryst;
extern crate rustc_serialize;
extern crate tempdir;
extern crate unicase;
extern crate url;
extern crate uuid;

use getopts::Options;
use hyper::header;
use hyper::net::Openssl;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use oauth2::Config;
use rustc_serialize::Encodable;
use rustc_serialize::json::{self, Json};
use queryst::parse;
use std::collections::{HashMap, HashSet};
use std::env;
use std::error::Error;
use std::io::Read;
use std::string::String;
use std::sync::{Arc, Mutex};
use unicase::UniCase;
use url::{parse_path, Url};
use self::uuid::Uuid;

fn main() {
    let (listen_address, ssl, oauth_config, oauth_redirect_path, whitelisted_domains) = parse_flags();

    let handler = Handler{
        oauth_config: oauth_config,
        oauth_redirect_path: oauth_redirect_path,
        whitelisted_domains: whitelisted_domains,
        redirect_uris: Arc::new(Mutex::new(HashMap::new())),
    };

    println!("Listening on {}", listen_address);
    if ssl.is_some() {
        Server::https(&listen_address[..], ssl.unwrap()).unwrap().handle(handler).unwrap();
    } else {
        Server::http(&listen_address[..]).unwrap().handle(handler).unwrap();
    }
}

fn parse_flags() -> (String, Option<Openssl>, oauth2::Config, String, HashSet<String>) {
    let mut opts = Options::new();

    opts.optopt("p", "port", "Port on which to listen", "3000");
    opts.optopt("i", "interface", "Interface on which to listen", "0.0.0.0");

    opts.optflag("s", "ssl", "Whether to listen for HTTPS or just HTTP");
    opts.optopt("c", "certfile", "SSL certificate file", "/path/to/foo.crt");
    opts.optopt("k", "keyfile", "SSL key file", "/path/to/foo.key");

    opts.optopt("h", "host_and_port", "Hostname and port", "127.0.0.1:443");
    opts.optopt("", "oauth_client_id", "Github oauth client ID", "clientid");
    opts.optopt("", "oauth_client_secret", "Github oauth client secret", "clientsecret");
    opts.optopt("", "oauth_redirect_path", "Github oauth redirect URL", "/oauth/redirect");

    opts.optopt("d", "whitelisted_domains", "Hostname and port of domains to which to allow redirection (comma-separated)", "");

    let args: Vec<String> = env::args().collect();
    let matches = opts.parse(&args[1..]).unwrap();
    let interface = matches.opt_str("i").unwrap_or("0.0.0.0".to_owned());
    let port = matches.opt_str("p").unwrap_or("3000".to_owned());

    let ssl = match matches.opt_present("s") {
        true => Some(Openssl::with_cert_and_key(matches.opt_str("c").unwrap(), matches.opt_str("k").unwrap()).unwrap()),
        false => None,
    };

    let mut oauth_config = oauth2::Config::new(
        &matches.opt_str("oauth_client_id").unwrap(),
        &matches.opt_str("oauth_client_secret").unwrap(),
        "https://github.com/login/oauth/authorize",
        "https://github.com/login/oauth/access_token"
    );
    let oauth_redirect_path = matches.opt_str("oauth_redirect_path").unwrap();
    oauth_config.redirect_url = format!("https://{}{}", matches.opt_str("host_and_port").unwrap(), oauth_redirect_path);

    let whitelisted_domains = matches.opt_str("d").unwrap()
        .split(",")
        .map(|x| x.to_string())
        .collect();

    let host_and_port = format!("{}:{}", interface, port);
    return (host_and_port, ssl, oauth_config, oauth_redirect_path, whitelisted_domains);
}

struct Handler {
    oauth_config: oauth2::Config,
    oauth_redirect_path: String,
    whitelisted_domains: HashSet<String>,
    redirect_uris: Arc<Mutex<HashMap<String, String>>>,
}

impl hyper::server::Handler for Handler {
    fn handle(&self, req: Request, mut res: Response) {
        let (path, qs) = match req.uri {
            AbsolutePath(ref p) => match parse_path(p) {
                Ok((path, qs, _)) => (format!("/{}", path.join("/")), qs),
                Err(err) => { self.error(res, err.description()); return },
            },
            _ => { self.not_found(res); return },
        };

        res.headers_mut().set(header::AccessControlAllowOrigin::Any);
        res.headers_mut().set(header::AccessControlAllowHeaders(vec![UniCase("content-type".to_owned())]));

        if req.method == hyper::method::Method::Options {
            return
        }

        match &path[..] {
            "/squashmerge" => match req.method {
                hyper::Post => { self.handle_request(req, res, Handler::handle_squash_request) },
                _ => self.not_allowed(res)
            },
            "/rewritehistory" => match req.method {
                hyper::Post => { self.handle_request(req, res, Handler::handle_rewrite_history_request) },
                _ => self.not_allowed(res),
            },
            "/oauth/authorize" => match req.method {
                hyper::Get => self.handle_oauth_authorize_request(qs, res),
                _ => self.not_allowed(res),
            },
            _ => {
                if path == &self.oauth_redirect_path[..] {
                    match req.method {
                        hyper::Get => match qs {
                            Some(q) => self.handle_oauth_exchange_request(&q, res),
                            None => self.bad_request(res),
                        },
                        _ => self.not_allowed(res),
                    }
                } else {
                    self.not_found(res);
                }
            },
        };
    }
}

impl Handler {
    fn handle_request<F>(&self, mut req: Request, res: Response, handler: F)
        where F : Fn(&str) -> Result<String, String> {

        let mut body_string = String::new();
        let err = req.read_to_string(&mut body_string);
        if err.is_err() {
            self.error(res, &err.err().unwrap().description());
            return;
        }

        match handler(&body_string) {
            Ok(sha) => self.send_json(res, &ShaResponse { sha: &sha, merged: true }),
            Err(err) => self.error(res, &err),
        };
    }

    fn handle_squash_request(body_string: &str) -> Result<String, String> {
        let body = try_or_string!(Json::from_str(body_string));

        return git::squash_merge(
            get_json_string!(body, "base_repo"),
            get_json_string!(body, "base_branch"),
            get_json_string!(body, "head_repo"),
            get_json_string!(body, "head_branch"),
            get_json_string!(body, "commit_message"),
            get_json_string!(body, "username"),
            get_json_string!(body, "password"),
        );
    }

    fn handle_rewrite_history_request(body_string: &str) -> Result<String, String> {
        let body = try_or_string!(Json::from_str(body_string));

        return git::rewrite_history(
            get_json_string!(body, "repo"),
            get_json_string!(body, "branch"),
            get_json_string!(body, "baseline_repo"),
            get_json_string!(body, "baseline_branch"),
            get_json_string!(body, "commit_message"),
            get_json_string!(body, "username"),
            get_json_string!(body, "password")
        );
    }

    fn handle_oauth_authorize_request(&self, qs: Option<String>, mut res: Response) {
        let uuid = Uuid::new_v4().to_hyphenated_string();

        let redirect_uri = self.get_redirect_uri(&qs);

        if redirect_uri.is_none() {
            self.bad_request(res);
            return;
        }

        // TODO: Implement clone for oauth2::Config
        let mut oauth_config = oauth2::Config::new(
            &self.oauth_config.client_id,
            &self.oauth_config.client_secret,
            &self.oauth_config.auth_url.to_string(),
            &self.oauth_config.token_url.to_string(),
        );
        oauth_config.redirect_url = self.oauth_config.redirect_url.clone();
        oauth_config.scopes.extend(self.get_scopes(&qs));

        let _ = *res.status_mut() = StatusCode::Found;
        res.headers_mut().set(header::Location(oauth_config.authorize_url(uuid).to_string()));
    }

    fn handle_oauth_exchange_request(&self, qs: &str, res: Response) {
        let obj = match parse(qs) {
            Ok(q) => q,
            Err(err) => { self.error(res, &format!("Error parsing querystring: {}", err.message)); return },
        };
        let code = match obj.find("code") {
            Some(c) => match c.as_string() {
                Some(c) => c,
                None => { self.error(res, "code not a string"); return },
            },
            None => { self.error(res, "No code present"); return },
        };
        let state = match obj.find("state") {
            Some(c) => match c.as_string() {
                Some(c) => c,
                None => { self.error(res, "state not a string"); return },
            },
            None => { self.error(res, "No state present"); return },
        };
        let token = self.oauth_config.exchange(code.to_owned());
        match token {
            Ok(tok) => self.respond_with_token(res, state, &tok.access_token),
            Err(tok) => self.error(res, &format!("Error exchanging token: {}", tok)),
        };
    }

    fn respond_with_token(&self, mut res: Response, uuid: &str, token: &str) {
        let mut uris = self.redirect_uris.lock().unwrap();
        match uris.remove(uuid) {
            Some(uri) => {
                let _ = *res.status_mut() = StatusCode::Found;
                // TODO: Form URI better
                res.headers_mut().set(header::Location(format!("{}?token={}", uri, token)));
            },
            None => { self.send_json(res, &TokenResponse { access_token: &token }); }
        };
    }

    fn send_json<T: Encodable>(&self, mut res: Response, obj: &T) {
        match json::encode(obj) {
            Ok(o) => { let _ = res.send(o.as_bytes()); },
            Err(_) => {
                let _ = *res.status_mut() = StatusCode::InternalServerError;
                let _ = res.send("{\"message\": \"error serializing response\"}".as_bytes());
            },
        };
    }

    fn get_redirect_uri(&self, qs: &Option<String>) -> Option<String> {
        let string = self.get_string_qs(qs, "redirect_uri");
        if string.is_none() {
            return string
        }

        let url = Url::parse(&string.clone().unwrap());
        match url {
            Ok(u) => {
                let domain = u.domain();
                if domain.is_none() {
                    return None;
                }
                if !self.is_whitelisted_domain(domain.unwrap()) {
                    return None;
                }
            },
            Err(_) => {return None; },
        };
        return string;
    }

    fn is_whitelisted_domain(&self, domain: &str) -> bool {
        return self.whitelisted_domains.contains(&domain.to_owned());
    }

    fn get_scopes(&self, qs: &Option<String>) -> Vec<String> {
        let val = self.get_string_qs(qs, "scopes")
            .clone()
            .unwrap_or("".to_owned());
        return val
            .split(",")
            .map(|x| x.to_string())
            .collect();
    }

    fn get_string_qs(&self, qs: &Option<String>, key: &str) -> Option<String> {
        let kv = match qs.clone() {
            Some(q) => match parse(&q) {
                Ok(kv) => kv,
                Err(_) => return None,
            },
            None => return None,
        };
        return kv.find(key)
            .and_then(|v| v.as_string())
            .and_then(|v| Some(v.to_owned().clone()));
    }

    fn error(&self, mut res: Response, description: &str) {
        let _ = *res.status_mut() = StatusCode::InternalServerError;
        self.send_json(res, &ErrorResponse { message: description });
    }

    fn bad_request(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::BadRequest;
    }

    fn not_allowed(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::MethodNotAllowed;
    }

    fn not_found(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::NotFound;
    }
}

#[derive(RustcEncodable)]
struct ShaResponse<'a> {
    sha: &'a str,
    merged: bool,
}

#[derive(RustcEncodable)]
struct ErrorResponse<'a> {
    message: &'a str,
}

#[derive(RustcEncodable)]
struct TokenResponse<'a> {
    access_token: &'a str,
}
<commit_msg>Fix responses<commit_after>#[macro_use]
mod macros;

mod git;

extern crate getopts;
extern crate hyper;
extern crate oauth2;
extern crate queryst;
extern crate rustc_serialize;
extern crate tempdir;
extern crate unicase;
extern crate url;
extern crate uuid;

use getopts::Options;
use hyper::header;
use hyper::net::Openssl;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use oauth2::Config;
use rustc_serialize::Encodable;
use rustc_serialize::json::{self, Json};
use queryst::parse;
use std::collections::{HashMap, HashSet};
use std::env;
use std::error::Error;
use std::io::Read;
use std::string::String;
use std::sync::{Arc, Mutex};
use unicase::UniCase;
use url::{parse_path, Url};
use self::uuid::Uuid;

fn main() {
    let (listen_address, ssl, oauth_config, oauth_redirect_path, whitelisted_domains) = parse_flags();

    let handler = Handler{
        oauth_config: oauth_config,
        oauth_redirect_path: oauth_redirect_path,
        whitelisted_domains: whitelisted_domains,
        redirect_uris: Arc::new(Mutex::new(HashMap::new())),
    };

    println!("Listening on {}", listen_address);
    if ssl.is_some() {
        Server::https(&listen_address[..], ssl.unwrap()).unwrap().handle(handler).unwrap();
    } else {
        Server::http(&listen_address[..]).unwrap().handle(handler).unwrap();
    }
}

fn parse_flags() -> (String, Option<Openssl>, oauth2::Config, String, HashSet<String>) {
    let mut opts = Options::new();

    opts.optopt("p", "port", "Port on which to listen", "3000");
    opts.optopt("i", "interface", "Interface on which to listen", "0.0.0.0");

    opts.optflag("s", "ssl", "Whether to listen for HTTPS or just HTTP");
    opts.optopt("c", "certfile", "SSL certificate file", "/path/to/foo.crt");
    opts.optopt("k", "keyfile", "SSL key file", "/path/to/foo.key");

    opts.optopt("h", "host_and_port", "Hostname and port", "127.0.0.1:443");
    opts.optopt("", "oauth_client_id", "Github oauth client ID", "clientid");
    opts.optopt("", "oauth_client_secret", "Github oauth client secret", "clientsecret");
    opts.optopt("", "oauth_redirect_path", "Github oauth redirect URL", "/oauth/redirect");

    opts.optopt("d", "whitelisted_domains", "Hostname and port of domains to which to allow redirection (comma-separated)", "");

    let args: Vec<String> = env::args().collect();
    let matches = opts.parse(&args[1..]).unwrap();
    let interface = matches.opt_str("i").unwrap_or("0.0.0.0".to_owned());
    let port = matches.opt_str("p").unwrap_or("3000".to_owned());

    let ssl = match matches.opt_present("s") {
        true => Some(Openssl::with_cert_and_key(matches.opt_str("c").unwrap(), matches.opt_str("k").unwrap()).unwrap()),
        false => None,
    };

    let mut oauth_config = oauth2::Config::new(
        &matches.opt_str("oauth_client_id").unwrap(),
        &matches.opt_str("oauth_client_secret").unwrap(),
        "https://github.com/login/oauth/authorize",
        "https://github.com/login/oauth/access_token"
    );
    let oauth_redirect_path = matches.opt_str("oauth_redirect_path").unwrap();
    oauth_config.redirect_url = format!("https://{}{}", matches.opt_str("host_and_port").unwrap(), oauth_redirect_path);

    let whitelisted_domains = matches.opt_str("d").unwrap()
        .split(",")
        .map(|x| x.to_string())
        .collect();

    let host_and_port = format!("{}:{}", interface, port);
    return (host_and_port, ssl, oauth_config, oauth_redirect_path, whitelisted_domains);
}

struct Handler {
    oauth_config: oauth2::Config,
    oauth_redirect_path: String,
    whitelisted_domains: HashSet<String>,
    redirect_uris: Arc<Mutex<HashMap<String, String>>>,
}

impl hyper::server::Handler for Handler {
    fn handle(&self, req: Request, mut res: Response) {
        let (path, qs) = match req.uri {
            AbsolutePath(ref p) => match parse_path(p) {
                Ok((path, qs, _)) => (format!("/{}", path.join("/")), qs),
                Err(err) => { self.error(res, err.description()); return },
            },
            _ => { self.not_found(res); return },
        };

        res.headers_mut().set(header::AccessControlAllowOrigin::Any);
        res.headers_mut().set(header::AccessControlAllowHeaders(vec![UniCase("content-type".to_owned())]));

        if req.method == hyper::method::Method::Options {
            return
        }

        match &path[..] {
            "/squashmerge" => match req.method {
                hyper::Post => { self.handle_request(req, res, Handler::handle_squash_request) },
                _ => self.not_allowed(res)
            },
            "/rewritehistory" => match req.method {
                hyper::Post => { self.handle_request(req, res, Handler::handle_rewrite_history_request) },
                _ => self.not_allowed(res),
            },
            "/oauth/authorize" => match req.method {
                hyper::Get => self.handle_oauth_authorize_request(qs, res),
                _ => self.not_allowed(res),
            },
            _ => {
                if path == &self.oauth_redirect_path[..] {
                    match req.method {
                        hyper::Get => match qs {
                            Some(q) => self.handle_oauth_exchange_request(&q, res),
                            None => self.bad_request(res),
                        },
                        _ => self.not_allowed(res),
                    }
                } else {
                    self.not_found(res);
                }
            },
        };
    }
}

impl Handler {
    fn handle_request<F>(&self, mut req: Request, res: Response, handler: F)
        where F : Fn(&str) -> Result<String, String> {

        let mut body_string = String::new();
        let err = req.read_to_string(&mut body_string);
        if err.is_err() {
            self.error(res, &err.err().unwrap().description());
            return;
        }

        match handler(&body_string) {
            Ok(sha) => self.send_json(res, &ShaResponse { sha: &sha, merged: true }),
            Err(err) => self.error(res, &err),
        };
    }

    fn handle_squash_request(body_string: &str) -> Result<String, String> {
        let body = try_or_string!(Json::from_str(body_string));

        return git::squash_merge(
            get_json_string!(body, "base_repo"),
            get_json_string!(body, "base_branch"),
            get_json_string!(body, "head_repo"),
            get_json_string!(body, "head_branch"),
            get_json_string!(body, "commit_message"),
            get_json_string!(body, "username"),
            get_json_string!(body, "password"),
        );
    }

    fn handle_rewrite_history_request(body_string: &str) -> Result<String, String> {
        let body = try_or_string!(Json::from_str(body_string));

        return git::rewrite_history(
            get_json_string!(body, "repo"),
            get_json_string!(body, "branch"),
            get_json_string!(body, "baseline_repo"),
            get_json_string!(body, "baseline_branch"),
            get_json_string!(body, "commit_message"),
            get_json_string!(body, "username"),
            get_json_string!(body, "password")
        );
    }

    fn handle_oauth_authorize_request(&self, qs: Option<String>, mut res: Response) {
        let uuid = Uuid::new_v4().to_hyphenated_string();

        let redirect_uri = self.get_redirect_uri(&qs);

        if redirect_uri.is_none() {
            self.bad_request(res);
            return;
        }

        let mut uris = self.redirect_uris.lock().unwrap();
        uris.insert(uuid.clone(), redirect_uri.unwrap().to_owned());

        // TODO: Implement clone for oauth2::Config
        let mut oauth_config = oauth2::Config::new(
            &self.oauth_config.client_id,
            &self.oauth_config.client_secret,
            &self.oauth_config.auth_url.to_string(),
            &self.oauth_config.token_url.to_string(),
        );
        oauth_config.redirect_url = self.oauth_config.redirect_url.clone();
        oauth_config.scopes.extend(self.get_scopes(&qs));

        let _ = *res.status_mut() = StatusCode::Found;
        res.headers_mut().set(header::Location(oauth_config.authorize_url(uuid).to_string()));
    }

    fn handle_oauth_exchange_request(&self, qs: &str, res: Response) {
        let obj = match parse(qs) {
            Ok(q) => q,
            Err(err) => { self.error(res, &format!("Error parsing querystring: {}", err.message)); return },
        };
        let code = match obj.find("code") {
            Some(c) => match c.as_string() {
                Some(c) => c,
                None => { self.error(res, "code not a string"); return },
            },
            None => { self.error(res, "No code present"); return },
        };
        let state = match obj.find("state") {
            Some(c) => match c.as_string() {
                Some(c) => c,
                None => { self.error(res, "state not a string"); return },
            },
            None => { self.error(res, "No state present"); return },
        };
        let token = self.oauth_config.exchange(code.to_owned());
        match token {
            Ok(tok) => self.respond_with_token(res, state, &tok.access_token),
            Err(tok) => self.error(res, &format!("Error exchanging token: {}", tok)),
        };
    }

    fn respond_with_token(&self, mut res: Response, uuid: &str, token: &str) {
        let mut uris = self.redirect_uris.lock().unwrap();
        match uris.remove(uuid) {
            Some(uri) => {
                let _ = *res.status_mut() = StatusCode::Found;
                // TODO: Form URI better
                res.headers_mut().set(header::Location(format!("{}?token={}", uri, token)));
            },
            None => { self.bad_request(res); },
        };
    }

    fn send_json<T: Encodable>(&self, mut res: Response, obj: &T) {
        match json::encode(obj) {
            Ok(o) => { let _ = res.send(o.as_bytes()); },
            Err(_) => {
                let _ = *res.status_mut() = StatusCode::InternalServerError;
                let _ = res.send("{\"message\": \"error serializing response\"}".as_bytes());
            },
        };
    }

    fn get_redirect_uri(&self, qs: &Option<String>) -> Option<String> {
        let string = self.get_string_qs(qs, "redirect_uri");
        if string.is_none() {
            return string
        }

        let url = Url::parse(&string.clone().unwrap());
        match url {
            Ok(u) => {
                let domain = u.domain();
                if domain.is_none() {
                    return None;
                }
                if !self.is_whitelisted_domain(domain.unwrap()) {
                    return None;
                }
            },
            Err(_) => {return None; },
        };
        return string;
    }

    fn is_whitelisted_domain(&self, domain: &str) -> bool {
        return self.whitelisted_domains.contains(&domain.to_owned());
    }

    fn get_scopes(&self, qs: &Option<String>) -> Vec<String> {
        let val = self.get_string_qs(qs, "scopes")
            .clone()
            .unwrap_or("".to_owned());
        return val
            .split(",")
            .map(|x| x.to_string())
            .collect();
    }

    fn get_string_qs(&self, qs: &Option<String>, key: &str) -> Option<String> {
        let kv = match qs.clone() {
            Some(q) => match parse(&q) {
                Ok(kv) => kv,
                Err(_) => return None,
            },
            None => return None,
        };
        return kv.find(key)
            .and_then(|v| v.as_string())
            .and_then(|v| Some(v.to_owned().clone()));
    }

    fn error(&self, mut res: Response, description: &str) {
        let _ = *res.status_mut() = StatusCode::InternalServerError;
        self.send_json(res, &ErrorResponse { message: description });
    }

    fn bad_request(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::BadRequest;
    }

    fn not_allowed(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::MethodNotAllowed;
    }

    fn not_found(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::NotFound;
    }
}

#[derive(RustcEncodable)]
struct ShaResponse<'a> {
    sha: &'a str,
    merged: bool,
}

#[derive(RustcEncodable)]
struct ErrorResponse<'a> {
    message: &'a str,
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate llvm_sys;

use std::{env, process};

mod ast;
mod parser;
mod trans;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() != 1 {
        println!("Usage: rig INPUT");
        process::exit(1);
    }

    println!("\n---- PARSED ----\n");

    let module = parser::load_and_parse(&args[0]).unwrap().unwrap();
    println!("{:?}", module);

    println!("\n---- LLVM'D ----\n");

    let llvm_module = trans::translate_module(&module);
    llvm_module.dump();

    trans::write_object_file(&llvm_module, "out.o");
    println!("\nWrote output to out.o.");
}
<commit_msg>Use pretty debug printing.<commit_after>extern crate libc;
extern crate llvm_sys;

use std::{env, process};

mod ast;
mod parser;
mod trans;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() != 1 {
        println!("Usage: rig INPUT");
        process::exit(1);
    }

    println!("\n---- PARSED ----\n");

    let module = parser::load_and_parse(&args[0]).unwrap().unwrap();
    println!("{:#?}", module);

    println!("\n---- LLVM'D ----\n");

    let llvm_module = trans::translate_module(&module);
    llvm_module.dump();

    trans::write_object_file(&llvm_module, "out.o");
    println!("\nWrote output to out.o.");
}
<|endoftext|>