<commit_before>extern crate hkg;
extern crate rustbox;
extern crate rustc_serialize;

use std::default::Default;

use rustbox::{Color, RustBox};
use rustbox::Key;

use rustc_serialize::json;

use hkg::utility::cache;
use hkg::model::TopicItem;

fn main() {

    // GUI init
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let title = String::from("高登");
    let s = cache::readfile(String::from("topics.json"));
    let collection: Vec<TopicItem> = json::decode(&s).unwrap();

    let mut status = String::from(">");
    loop {

        let w = rustbox.width();
        let h = rustbox.height();

        print_header(&rustbox, w, &title);
        print_body(&rustbox, w, 2, h - 3, &collection);

        let status_width = if w > status.len() {
            w - status.len()
        } else {
            0
        };
        let status_spacing = (0..status_width).map(|_| " ").collect::<Vec<_>>().join("");

        rustbox.print(1,
                      h - 1,
                      rustbox::RB_BOLD,
                      Color::White,
                      Color::Black,
                      &format!("{status}{status_spacing}",
                               status = status,
                               status_spacing = status_spacing));

        rustbox.present();
        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => {
                        break;
                    }

                    Key::Up => {
                        if status.len() >= w {
                            status = String::from("U");
                        } else {
                            status = String::from(format!("{}{}", &status, &"U"));
                        }

                    }
                    Key::Down => {
                        if status.len() >= w {
                            status = String::from("D");
                        } else {
                            status = String::from(format!("{}{}", &status, &"D"));
                        }

                    }

                    _ => {}
                }
            }
            Err(e) => panic!("{}", e),
            _ => {}
        }
    }
}

fn print_header(rustbox: &rustbox::RustBox, width: usize, text: &str) {
    let padding = (width - text.len()) / 2;
    let header_bottom = (0..width).map(|_| "─").collect::<Vec<_>>().join("");

    clearline(&rustbox, width, 0, 0);
    rustbox.print(padding,
                  0,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  text);
    rustbox.print(0,
                  1,
                  rustbox::RB_BOLD,
                  Color::Yellow,
                  Color::Black,
                  &header_bottom);
}

fn print_body(rustbox: &rustbox::RustBox,
              width: usize,
              offset_y: usize,
              rows: usize,
              collection: &Vec<TopicItem>) {

    let right_offset = 3;
    let author_max_width = 12;
    let no_max_width = 5;
    let title_max_width = width - no_max_width - author_max_width - right_offset;

    for (i, item) in collection.iter().take(rows).enumerate() {

        let original_title = item.titles[0].text.clone();
        let title: String = substring(&original_title, title_max_width);
        let title_len = string_jks_len(&title);

        let title_spacin_minus = no_max_width + title_len + author_max_width + right_offset;
        let title_spacing_width = if width > title_spacin_minus {
            width - title_spacin_minus
        } else {
            0
        };
        let title_spacing = (0..title_spacing_width).map(|_| " ").collect::<Vec<_>>().join("");

        let author = item.author.name.clone();
        let author_spacing_width = author_max_width - string_jks_len(&author) + right_offset;
        let author_spacing = (0..author_spacing_width).map(|_| " ").collect::<Vec<_>>().join("");

        rustbox.print(0,
                      i + offset_y,
                      rustbox::RB_NORMAL,
                      Color::White,
                      Color::Black,
                      &format!("[{no:0>2}] {title}{title_spacing}| {author}{author_spacing}",
                               no = i + 1,
                               title = title,
                               title_spacing = title_spacing,
                               author = &author,
                               author_spacing = author_spacing
                           ));
    }

}

fn clearline(rustbox: &rustbox::RustBox, width: usize, x: usize, y: usize) {
    let s = (0..width).map(|_| "  ").collect::<Vec<_>>().join("");

    rustbox.print(x, y, rustbox::RB_NORMAL, Color::White, Color::Black, &s);
}

fn substring(s: &str, length: usize) -> String {
    let mut v = Vec::new();
    let mut c = 0;

    for x in s.chars() {
        if contains(x) {
            if c < length - 2 {
                v.push(x);
                c = c + 2;
            } else {
                break;
            }

        } else {
            if c < length - 1 {
                v.push(x);
                c = c + 1;
            } else {
                break;
            }
        }
    }

    let s: String = v.iter().cloned().collect();
    return s;
}

fn contains(c: char) -> bool {
    let cjks = vec![(0x4E00..0xA000),
                    (0x3400..0x4DC0),
                    (0x20000..0x2A6E0),
                    (0x2A700..0x2B740),
                    (0x2B740..0x2B820),
                    (0xF900..0xFB00),
                    (0x2F800..0x2FA20),
                    (0x9FA6..0x9FCC),

                    (0x3000..0x303F), // CJK Symbols and Punctuation
                    (0xff00..0xffef) /* Halfwidth and Fullwidth Forms */];

    for cjk in cjks {
        let h = c as u32;
        if cjk.start <= h && h < cjk.end {
            return true;
        }
    }
    return false;
}

fn string_jks_len(s: &str) -> usize {
    return s.chars()
            .map(|x| if contains(x) {
                2
            } else {
                1
            })
            .collect::<Vec<usize>>()
            .iter()
            .fold(0, |acc, &x| acc + x);
}

// fn debug_load_and_print_topics() {
//     let s = cache::readfile(String::from("topics.json"));
//     let collection: Vec<TopicItem> = json::decode(&s).unwrap();
//
//     println!("topics {:?}", collection.len());
//     debug_print_topics(collection);
// }
//
// fn debug_print_topics(collection: Vec<TopicItem>) {
//     for (i, item) in collection.iter().enumerate() {
//
//         println!("item[{}]= {title} {author_name} {last_replied_date} {last_replied_time} \
//                   {reply_count} {rating}",
//                  i,
//                  title = item.titles[0].text,
//                  author_name = item.author.name,
//                  last_replied_date = item.last_replied_date,
//                  last_replied_time = item.last_replied_time,
//                  reply_count = item.reply_count,
//                  rating = item.rating);
//     }
// }
<commit_msg>add a blinking rows cursor<commit_after>extern crate hkg;
extern crate rustbox;
extern crate rustc_serialize;

use std::default::Default;

use rustbox::{Color, RustBox};
use rustbox::Key;

use rustc_serialize::json;

use hkg::utility::cache;
use hkg::model::TopicItem;

fn main() {

    // GUI init
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let title = String::from("高登");
    let s = cache::readfile(String::from("topics.json"));
    let collection: Vec<TopicItem> = json::decode(&s).unwrap();

    let mut status = String::from(">");

    let mut selected_topic_index: usize = 0; // no selection by default

    loop {

        let w = rustbox.width();
        let h = rustbox.height();

        let body_height = if h >= 3 {
            h - 3
        } else {
            0
        };
        let body_width = if w >= 2 {
            w - 2
        } else {
            0
        };

        if selected_topic_index > body_height {
            selected_topic_index = body_height;
        }

        print_header(&rustbox, w, &title);
        print_body(&rustbox,
                   body_width,
                   2,
                   body_height,
                   &collection,
                   selected_topic_index);

        let status_width = if w > status.len() {
            w - status.len()
        } else {
            0
        };
        let status_spacing = (0..status_width).map(|_| " ").collect::<Vec<_>>().join("");

        rustbox.print(1,
                      h - 1,
                      rustbox::RB_BOLD,
                      Color::White,
                      Color::Black,
                      &format!("{status}{status_spacing}",
                               status = status,
                               status_spacing = status_spacing));

        rustbox.present();
        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => {
                        break;
                    }

                    Key::Up => {
                        if status.len() >= w {
                            status = String::from("U");
                        } else {
                            status = String::from(format!("{}{}", &status, &"U"));
                        }

                        if selected_topic_index > 1 {
                            selected_topic_index = selected_topic_index - 1;
                        }
                    }
                    Key::Down => {
                        if status.len() >= w {
                            status = String::from("D");
                        } else {
                            status = String::from(format!("{}{}", &status, &"D"));
                        }

                        if selected_topic_index < body_height {
                            selected_topic_index = selected_topic_index + 1;
                        }
                    }

                    _ => {}
                }
            }
            Err(e) => panic!("{}", e),
            _ => {}
        }
    }
}

fn print_header(rustbox: &rustbox::RustBox, width: usize, text: &str) {
    let padding = (width - text.len()) / 2;
    let header_bottom = (0..width).map(|_| "─").collect::<Vec<_>>().join("");

    clearline(&rustbox, width, 0, 0);
    rustbox.print(padding,
                  0,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  text);
    rustbox.print(0,
                  1,
                  rustbox::RB_BOLD,
                  Color::Yellow,
                  Color::Black,
                  &header_bottom);
}

fn print_body(rustbox: &rustbox::RustBox,
              width: usize,
              offset_y: usize,
              rows: usize,
              collection: &Vec<TopicItem>,
              selected_topic_index: usize) {

    let right_offset = 3;
    let author_max_width = 12;
    let no_max_width = 5;
    let title_max_width = width - no_max_width - author_max_width - right_offset;

    for (i, item) in collection.iter().take(rows).enumerate() {

        let original_title = item.titles[0].text.clone();
        let title: String = substring(&original_title, title_max_width);
        let title_len = string_jks_len(&title);

        let title_spacin_minus = no_max_width + title_len + author_max_width + right_offset;
        let title_spacing_width = if width > title_spacin_minus {
            width - title_spacin_minus
        } else {
            0
        };
        let title_spacing = (0..title_spacing_width).map(|_| " ").collect::<Vec<_>>().join("");

        let author = item.author.name.clone();
        let author_spacing_width = author_max_width - string_jks_len(&author) + right_offset;
        let author_spacing = (0..author_spacing_width).map(|_| " ").collect::<Vec<_>>().join("");

        if selected_topic_index == i + 1 {
            rustbox.print(0,
                          i + offset_y,
                          rustbox::RB_NORMAL,
                          Color::Black,
                          Color::Yellow,
                          &format!("[{no:0>2}] {title}{title_spacing}| {author}{author_spacing}",
                                   no = i + 1,
                                   title = title,
                                   title_spacing = title_spacing,
                                   author = &author,
                                   author_spacing = author_spacing));
        } else {
            rustbox.print(0,
                          i + offset_y,
                          rustbox::RB_NORMAL,
                          Color::White,
                          Color::Black,
                          &format!("[{no:0>2}] {title}{title_spacing}| {author}{author_spacing}",
                                   no = i + 1,
                                   title = title,
                                   title_spacing = title_spacing,
                                   author = &author,
                                   author_spacing = author_spacing));
        }

    }

}

fn clearline(rustbox: &rustbox::RustBox, width: usize, x: usize, y: usize) {
    let s = (0..width).map(|_| "  ").collect::<Vec<_>>().join("");

    rustbox.print(x, y, rustbox::RB_NORMAL, Color::White, Color::Black, &s);
}

fn substring(s: &str, length: usize) -> String {
    let mut v = Vec::new();
    let mut c = 0;

    for x in s.chars() {
        if contains(x) {
            if c < length - 2 {
                v.push(x);
                c = c + 2;
            } else {
                break;
            }

        } else {
            if c < length - 1 {
                v.push(x);
                c = c + 1;
            } else {
                break;
            }
        }
    }

    let s: String = v.iter().cloned().collect();
    return s;
}

fn contains(c: char) -> bool {
    let cjks = vec![(0x4E00..0xA000),
                    (0x3400..0x4DC0),
                    (0x20000..0x2A6E0),
                    (0x2A700..0x2B740),
                    (0x2B740..0x2B820),
                    (0xF900..0xFB00),
                    (0x2F800..0x2FA20),
                    (0x9FA6..0x9FCC),

                    (0x3000..0x303F), // CJK Symbols and Punctuation
                    (0xff00..0xffef) /* Halfwidth and Fullwidth Forms */];

    for cjk in cjks {
        let h = c as u32;
        if cjk.start <= h && h < cjk.end {
            return true;
        }
    }
    return false;
}

fn string_jks_len(s: &str) -> usize {
    return s.chars()
            .map(|x| if contains(x) {
                2
            } else {
                1
            })
            .collect::<Vec<usize>>()
            .iter()
            .fold(0, |acc, &x| acc + x);
}

// fn debug_load_and_print_topics() {
//     let s = cache::readfile(String::from("topics.json"));
//     let collection: Vec<TopicItem> = json::decode(&s).unwrap();
//
//     println!("topics {:?}", collection.len());
//     debug_print_topics(collection);
// }
//
// fn debug_print_topics(collection: Vec<TopicItem>) {
//     for (i, item) in collection.iter().enumerate() {
//
//         println!("item[{}]= {title} {author_name} {last_replied_date} {last_replied_time} \
//                   {reply_count} {rating}",
//                  i,
//                  title = item.titles[0].text,
//                  author_name = item.author.name,
//                  last_replied_date = item.last_replied_date,
//                  last_replied_time = item.last_replied_time,
//                  reply_count = item.reply_count,
//                  rating = item.rating);
//     }
// }
<|endoftext|>


<commit_before>use crossbeam_channel::bounded;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::ExitCode;
use std::thread;
use std::vec::Vec;

#[derive(Debug)]
struct TranscodeJob {
    src: PathBuf,
    dest: PathBuf,
}

fn main() -> Result<(), ExitCode> {
    let (srcdir, destdir) = parse_args()?;

    println!(
        "videos src: {} dest: {}",
        srcdir.display(),
        destdir.display()
    );

    if srcdir == destdir {
        println!("src and dest dirs must differ");
        return Err(ExitCode::FAILURE);
    }

    thread::scope(|s| {
        const NTHREADS: i32 = 6;

        let (job_sender, job_recv) = bounded::<TranscodeJob>(0);

        for i in 0..NTHREADS {
            let job_recv = job_recv.clone();
            s.spawn(move || transcoder(i, job_recv));
        }

        jobscheduler(srcdir.as_path(), destdir.as_path(), job_sender);
    });

    println!("done");

    Ok(())
}

fn jobscheduler(srcdir: &Path, destdir: &Path, job_sender: Sender<TranscodeJob>) {
    println!("starting job scheduler");

    let entries = fs::read_dir(srcdir).unwrap();
    let mut srcs = Vec::new();

    for entry in entries {
        match entry {
            Ok(entry) => {
                let srcdir = entry.path();
                if let Some(extension) = srcdir.extension() {
                    let extension = extension
                        .to_str()
                        .expect("file extensions should be valid utf-8");

                    match extension {
                        "avi" => {
                            srcs.push(srcdir);
                        }
                        _ => {
                            println!("ignoring file with extension: {}", extension)
                        }
                    }
                }
            }
            Err(err) => {
                print!("error reading dir {}: {}", srcdir.display(), err);
            }
        }
    }

    for src in srcs {
        // We already validated srcs, so they always have a filename
        let dest_file = src.with_extension("mkv");
        let mut dest = destdir.to_path_buf();
        dest.push(dest_file.file_name().unwrap());

        let job = TranscodeJob {
            src: src,
            dest: dest,
        };
        job_sender.send(job).expect("failed to send job to workers");
    }

    drop(job_sender);
}

fn transcoder(id: i32, job_recv: Receiver<TranscodeJob>) {
    println!("starting transcoder {}", id);

    while let Ok(job) = job_recv.recv() {
        println!(
            "transcoder {} transcoding from {} to {}",
            id,
            job.src.display(),
            job.dest.display()
        );

        let pipeline = format!(
            r#"
                gst-launch-1.0 filesrc location="{}" ! decodebin name=decoder ! \
                queue ! videoconvert ! videoscale ! x264enc ! queue ! \
                matroskamux name=muxer ! queue ! filesink location="{}" decoder. ! \
                queue ! audioconvert ! audioresample ! vorbisenc ! queue ! muxer.
            "#,
            job.src.display(),
            job.dest.display(),
        );

        println!("running: {}", pipeline);
        let mut cmd = Command::new("sh");
        cmd.arg("-c");
        cmd.arg(pipeline);

        match cmd.spawn() {
            Ok(mut child) => match child.wait() {
                Ok(res) => {
                    println!("transcoder result: {}", res);
                }
                Err(err) => {
                    println!("error running transcoder: {}", err);
                }
            },
            Err(err) => {
                println!("error starting transcoder: {}", err);
            }
        }
    }

    println!("finishing transcoder {}", id);
}

fn parse_args() -> Result<(PathBuf, PathBuf), ExitCode> {
    let args: Vec<String> = env::args().collect();

    return match args.len() {
        1 => {
            let src = env::current_dir().unwrap();
            let mut dest = src.clone();
            dest.push("transcoded");
            Ok((src, dest))
        }
        3 => Ok((PathBuf::from(&args[1]), PathBuf::from(&args[2]))),
        _ => {
            println!("usage using current dir: {}", args[0]);
            println!("usage explicit dirs: {} <src dir> <dest dir>", args[0]);
            Err(ExitCode::FAILURE)
        }
    };
}
<commit_msg>chore: add videorate<commit_after>use crossbeam_channel::bounded;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::ExitCode;
use std::thread;
use std::vec::Vec;

#[derive(Debug)]
struct TranscodeJob {
    src: PathBuf,
    dest: PathBuf,
}

fn main() -> Result<(), ExitCode> {
    let (srcdir, destdir) = parse_args()?;

    println!(
        "videos src: {} dest: {}",
        srcdir.display(),
        destdir.display()
    );

    if srcdir == destdir {
        println!("src and dest dirs must differ");
        return Err(ExitCode::FAILURE);
    }

    thread::scope(|s| {
        const NTHREADS: i32 = 6;

        let (job_sender, job_recv) = bounded::<TranscodeJob>(0);

        for i in 0..NTHREADS {
            let job_recv = job_recv.clone();
            s.spawn(move || transcoder(i, job_recv));
        }

        jobscheduler(srcdir.as_path(), destdir.as_path(), job_sender);
    });

    println!("done");

    Ok(())
}

fn jobscheduler(srcdir: &Path, destdir: &Path, job_sender: Sender<TranscodeJob>) {
    println!("starting job scheduler");

    let entries = fs::read_dir(srcdir).unwrap();
    let mut srcs = Vec::new();

    for entry in entries {
        match entry {
            Ok(entry) => {
                let srcdir = entry.path();
                if let Some(extension) = srcdir.extension() {
                    let extension = extension
                        .to_str()
                        .expect("file extensions should be valid utf-8");

                    match extension {
                        "avi" => {
                            srcs.push(srcdir);
                        }
                        _ => {
                            println!("ignoring file with extension: {}", extension)
                        }
                    }
                }
            }
            Err(err) => {
                print!("error reading dir {}: {}", srcdir.display(), err);
            }
        }
    }

    for src in srcs {
        // We already validated srcs, so they always have a filename
        let dest_file = src.with_extension("mkv");
        let mut dest = destdir.to_path_buf();
        dest.push(dest_file.file_name().unwrap());

        let job = TranscodeJob {
            src: src,
            dest: dest,
        };
        job_sender.send(job).expect("failed to send job to workers");
    }

    drop(job_sender);
}

fn transcoder(id: i32, job_recv: Receiver<TranscodeJob>) {
    println!("starting transcoder {}", id);

    while let Ok(job) = job_recv.recv() {
        println!(
            "transcoder {} transcoding from {} to {}",
            id,
            job.src.display(),
            job.dest.display()
        );

        let pipeline = format!(
            r#"
                gst-launch-1.0 filesrc location="{}" ! decodebin name=decoder ! \
                queue ! videoconvert ! videoscale ! videorate ! x264enc ! queue ! \
                matroskamux name=muxer ! queue ! filesink location="{}" decoder. ! \
                queue ! audioconvert ! audioresample ! vorbisenc ! queue ! muxer.
            "#,
            job.src.display(),
            job.dest.display(),
        );

        println!("running: {}", pipeline);
        let mut cmd = Command::new("sh");
        cmd.arg("-c");
        cmd.arg(pipeline);

        match cmd.spawn() {
            Ok(mut child) => match child.wait() {
                Ok(res) => {
                    println!("transcoder result: {}", res);
                }
                Err(err) => {
                    println!("error running transcoder: {}", err);
                }
            },
            Err(err) => {
                println!("error starting transcoder: {}", err);
            }
        }
    }

    println!("finishing transcoder {}", id);
}

fn parse_args() -> Result<(PathBuf, PathBuf), ExitCode> {
    let args: Vec<String> = env::args().collect();

    return match args.len() {
        1 => {
            let src = env::current_dir().unwrap();
            let mut dest = src.clone();
            dest.push("transcoded");
            Ok((src, dest))
        }
        3 => Ok((PathBuf::from(&args[1]), PathBuf::from(&args[2]))),
        _ => {
            println!("usage using current dir: {}", args[0]);
            println!("usage explicit dirs: {} <src dir> <dest dir>", args[0]);
            Err(ExitCode::FAILURE)
        }
    };
}
<|endoftext|>


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

// TODO: TESTS!
// TODO: Color shift
// TODO: Support capturing a virtual desktop, multiple monotors together
// TODO: Optional solid color or animation when there's no signal

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate captrs;
extern crate clock_ticks;
extern crate serde_json;
extern crate serial;

#[cfg(feature = "cpuprofiler")]
extern crate cpuprofiler;

use captrs::{CaptureError, Capturer};
use capture::ImageAnalyzer;
use color::{Color, HSVTransformer, Rgb8, RgbTransformer};
use config::parse_led_indices;
use serial::SerialPort;
use std::cmp::{max, Ordering};
use std::{thread, time};

mod capture;
mod color;
mod config;

type SmoothFn = fn(Rgb8, Rgb8, f32) -> Rgb8;

/// Returns smallest of `a` and `b` if there is one, else returns `expect`
fn partial_min<T: PartialOrd>(a: T, b: T, expect: T) -> T {
    match a.partial_cmp(&b) {
        Some(Ordering::Less) | Some(Ordering::Equal) => a,
        Some(Ordering::Greater) => b,
        None => expect,
    }
}

/// Returns greatest of `a` and `b` if there is one, else returns `expect`
fn partial_max<T: PartialOrd>(a: T, b: T, expect: T) -> T {
    match a.partial_cmp(&b) {
        Some(Ordering::Greater) | Some(Ordering::Equal) => a,
        Some(Ordering::Less) => b,
        None => expect,
    }
}

/// A special header is expected by the corresponding LED streaming code running on the Arduino.
/// This only needs to be initialized once since the number of LEDs remains constant.
fn new_pixel_buf_header(n_leds: u16) -> [u8; 6] {
    // In the two below, not sure why the -1 in `(n_leds - 1)` is needed,
    // but that's how LEDstream on the Arduino expects it
    let count_high = ((n_leds - 1) >> 8) as u8; // LED count high byte
    let count_low = ((n_leds - 1) & 0xff) as u8; // LED count low byte
    [
        'A' as u8,
        'd' as u8,
        'a' as u8,
        count_high,
        count_low,
        count_high ^ count_low ^ 0x55, /* Checksum */
    ]
}

/// A timer to track time passed between refreshes. Can for example be used to limit frame rate.
struct FrameTimer {
    before: f64,
    last_frame_dt: f64,
}

impl FrameTimer {
    fn new() -> FrameTimer {
        FrameTimer {
            before: clock_ticks::precise_time_s(),
            last_frame_dt: 0.0,
        }
    }

    /// For how long the previous frame lasted
    fn last_frame_dt(&self) -> f64 {
        self.last_frame_dt
    }

    /// Time passed since last tick
    fn dt_to_now(&mut self) -> f64 {
        let now = clock_ticks::precise_time_s();
        let dt = now - self.before;
        if dt >= 0.0 {
            dt
        } else {
            self.before = now;
            0.0
        }
    }

    /// An update/frame/refresh has occured; take the time.
    fn tick(&mut self) {
        let now = clock_ticks::precise_time_s();
        self.last_frame_dt = partial_max(now - self.before, 0.0, 0.0);
        self.before = now;
    }
}

/// Writes color data via serial to LEDstream compatible device
struct ColorWriter {
    port: String,
    baud_rate: serial::BaudRate,
    con: serial::SystemPort,
    header: [u8; 6],
}

impl ColorWriter {
    /// Configure serial writing given a serial port, baud rate, and header to write before
    /// each data write
    fn new(port: &str, baud_rate: u32, header: [u8; 6]) -> Self {
        let serial_baud_rate = serial::BaudRate::from_speed(baud_rate as usize);
        let serial_con = ColorWriter::connect(port, serial_baud_rate).unwrap();
        ColorWriter {
            port: port.to_string(),
            baud_rate: serial_baud_rate,
            con: serial_con,
            header: header,
        }
    }

    fn connect(
        port: &str,
        baud_rate: serial::BaudRate,
    ) -> Result<serial::SystemPort, serial::Error> {
        serial::open(port).and_then(|mut con| {
            con.reconfigure(&|settings| {
                settings.set_char_size(serial::CharSize::Bits8);
                settings.set_baud_rate(baud_rate)
            })
            .map(|()| con)
        })
    }

    /// Write a buffer of color data to the LEDstream device
    fn write_colors(&mut self, color_data: &[Rgb8]) {
        use std::io::Write;
        let color_bytes = color::rgbs_as_bytes(color_data);
        match self.con.write(&self.header) {
            Ok(hn) if hn == self.header.len() => match self.con.write(color_bytes) {
                Ok(bn) if bn == color_bytes.len() => (),
                Ok(bn) => println!(
                    "Failed to write all bytes of RGB data. Wrote {} of {}",
                    bn,
                    color_data.len()
                ),
                Err(e) => println!("Failed to write RGB data, {}", e),
            },
            Ok(_) => println!("Failed to write all bytes in header"),
            Err(e) => {
                println!("Failed to write header, {}", e);
                // Probably, the serial device was temporarily disconnected or rebooted.
                // Try to reconnect forever
                loop {
                    print!("Reopening serial port... ");
                    match ColorWriter::connect(&self.port, self.baud_rate) {
                        Ok(con) => {
                            self.con = con;
                            println!("success!");
                            break;
                        }
                        Err(e) => {
                            println!("{}", e);
                            thread::sleep(time::Duration::from_millis(1000));
                        }
                    }
                }
            }
        }
    }
}

/// Update the colors to output by analyzing the captured frame for each led
fn update_out_color_data(
    out_pixels: &mut [Rgb8],
    frame_analyzer: &ImageAnalyzer,
    leds: &[config::Region],
    leds_transformers: &[Vec<(Option<RgbTransformer>, Option<&HSVTransformer>)>],
    smooth: SmoothFn,
    smooth_factor: f32,
) {
    for (i, &led) in leds.iter().enumerate() {
        let avg_color = frame_analyzer.average_color(led);
        let to_pixel = leds_transformers[i]
            .iter()
            .map(|&(ref opt_rgb, ref opt_hsv)| (opt_rgb.as_ref(), opt_hsv.as_ref()))
            .fold(
                Color::RGB(avg_color),
                |acc_color, transformers| match transformers {
                    (Some(rgb_tr), Some(hsv_tr)) => Color::HSV(
                        hsv_tr.transform(rgb_tr.transform(acc_color.into_rgb()).to_hsv()),
                    ),
                    (Some(rgb_tr), _) => Color::RGB(rgb_tr.transform(acc_color.into_rgb())),
                    (_, Some(hsv_tr)) => Color::HSV(hsv_tr.transform(acc_color.into_hsv())),
                    _ => acc_color,
                },
            )
            .into_rgb();
        out_pixels[i] = smooth(out_pixels[i], to_pixel, smooth_factor);
    }
}

/// Specifies how to loop over the main body
#[cfg(not(feature = "cpuprofiler"))]
fn main_loop<F: FnMut() -> bool>(mut body: F) {
    loop {
        if !body() {
            break;
        }
    }
}

/// Loop over the main body a fixed number of times and track performance with a profiler
#[cfg(feature = "cpuprofiler")]
fn main_loop<F: FnMut() -> bool>(mut body: F) {
    cpuprofiler::PROFILER
        .lock()
        .unwrap()
        .start("./prof.profile")
        .unwrap();
    for _ in 0..1000 {
        if !body() {
            break;
        }
    }
    cpuprofiler::PROFILER.lock().unwrap().stop().unwrap();
}

fn main() {
    let config = config::parse_config();
    let leds: &[_] = &config.leds;
    let mut led_transformers_list: Vec<_> = vec![Vec::with_capacity(1); leds.len()];
    // Add color transforms from config to each led in matching vec
    for transform_conf in config.color.transform.iter() {
        let hsv_transformer = if !transform_conf.hsv.is_default() {
            Some(&transform_conf.hsv)
        } else {
            None
        };
        let rgb_transformer = if !(transform_conf.red.is_default()
            && transform_conf.green.is_default()
            && transform_conf.red.is_default())
        {
            Some(RgbTransformer {
                r: transform_conf.red.clone(),
                g: transform_conf.green.clone(),
                b: transform_conf.blue.clone(),
            })
        } else {
            None
        };
        for range in parse_led_indices(&transform_conf.leds, leds.len()).iter() {
            for transformers in led_transformers_list[range.clone()].iter_mut() {
                transformers.push((rgb_transformer.clone(), hsv_transformer));
            }
        }
    }
    // Header to write before led data
    let out_header = new_pixel_buf_header(leds.len() as u16);
    let mut color_writer = ColorWriter::new(&config.device.output, config.device.rate, out_header);
    // Skeleton for the output led pixel buffer to write to arduino
    let mut out_pixels = vec![Rgb8 { r: 0, g: 0, b: 0 }; leds.len()];
    let mut capturer = Some(Capturer::new(config.device.input as usize).unwrap());
    let capture_frame_interval = 1.0 / config.framegrabber.frequency_Hz;
    // Function to use when smoothing led colors
    let smooth = match config.color.smoothing.type_.as_ref() {
        "linear" => color::linear_smooth as SmoothFn,
        _ => color::no_smooth as SmoothFn,
    };
    // max w/ 1 to avoid future divide by zero
    let smooth_time_const = max(config.color.smoothing.time_ms, 1) as f64 / 1000.0;
    let led_refresh_interval = 1.0 / config.color.smoothing.update_frequency;
    println!(
        "Helion - An LED streamer\nNumber of LEDs: {}\nResize resolution: {} x {}\nCapture \
         rate: {} fps\nLED refresh rate: {} hz\nCapture output: {}\nSerial port: {}",
        leds.len(),
        config.framegrabber.width,
        config.framegrabber.height,
        config.framegrabber.frequency_Hz,
        1.0 / led_refresh_interval,
        config.device.input,
        config.device.output
    );
    let mut capture_timer = FrameTimer::new();
    let mut led_refresh_timer = FrameTimer::new();
    main_loop(|| {
        led_refresh_timer.tick();
        // Don't capture new frame if going faster than frame limit,
        // but still proceed to smooth leds
        if capture_timer.dt_to_now() > capture_frame_interval {
            // If something goes wrong, last frame is reused
            match capturer.as_mut().unwrap().capture_store_frame() {
                Ok(_) => (),
                Err(CaptureError::Timeout) => {
                    println!("timeout");
                    thread::sleep(time::Duration::from_millis(100));
                    return true;
                }
                Err(e) => {
                    println!("Capture error: {:?}", e);
                    drop(capturer.take());
                    thread::sleep(time::Duration::from_millis(2_000));
                    loop {
                        match Capturer::new(config.device.input as usize) {
                            Ok(c) => {
                                capturer = Some(c);
                                break;
                            }
                            Err(e1) => {
                                println!("Error creating capturer: {:?}", e1);
                                thread::sleep(time::Duration::from_millis(1_000));
                            }
                        }
                    }
                }
            }
            capture_timer.tick();
        }
        if let Some(frame) = capturer.as_ref().unwrap().get_stored_frame() {
            let (w, h) = capturer.as_ref().unwrap().geometry();
            let frame_analyzer = ImageAnalyzer::new(
                frame,
                w as usize,
                h as usize,
                config.framegrabber.width,
                config.framegrabber.height,
            );
            let smooth_factor = (led_refresh_timer.last_frame_dt() / smooth_time_const) as f32;
            update_out_color_data(
                &mut out_pixels,
                &frame_analyzer,
                leds,
                &led_transformers_list,
                smooth,
                smooth_factor,
            );
            color_writer.write_colors(&out_pixels)
        }
        let time_left = led_refresh_interval - led_refresh_timer.dt_to_now();
        if time_left > 0.0 {
            let ms = if time_left > 0.0 {
                time_left * 1_000.0
            } else {
                0.0
            };
            thread::sleep(time::Duration::from_millis(ms as u64));
        }
        true
    })
}
<commit_msg>On error, sleep for 3s instead of 1s<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Johan Johansson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// TODO: TESTS!
// TODO: Color shift
// TODO: Support capturing a virtual desktop, multiple monotors together
// TODO: Optional solid color or animation when there's no signal

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate captrs;
extern crate clock_ticks;
extern crate serde_json;
extern crate serial;

#[cfg(feature = "cpuprofiler")]
extern crate cpuprofiler;

use captrs::{CaptureError, Capturer};
use capture::ImageAnalyzer;
use color::{Color, HSVTransformer, Rgb8, RgbTransformer};
use config::parse_led_indices;
use serial::SerialPort;
use std::cmp::{max, Ordering};
use std::{thread, time};

mod capture;
mod color;
mod config;

type SmoothFn = fn(Rgb8, Rgb8, f32) -> Rgb8;

/// Returns smallest of `a` and `b` if there is one, else returns `expect`
fn partial_min<T: PartialOrd>(a: T, b: T, expect: T) -> T {
    match a.partial_cmp(&b) {
        Some(Ordering::Less) | Some(Ordering::Equal) => a,
        Some(Ordering::Greater) => b,
        None => expect,
    }
}

/// Returns greatest of `a` and `b` if there is one, else returns `expect`
fn partial_max<T: PartialOrd>(a: T, b: T, expect: T) -> T {
    match a.partial_cmp(&b) {
        Some(Ordering::Greater) | Some(Ordering::Equal) => a,
        Some(Ordering::Less) => b,
        None => expect,
    }
}

/// A special header is expected by the corresponding LED streaming code running on the Arduino.
/// This only needs to be initialized once since the number of LEDs remains constant.
fn new_pixel_buf_header(n_leds: u16) -> [u8; 6] {
    // In the two below, not sure why the -1 in `(n_leds - 1)` is needed,
    // but that's how LEDstream on the Arduino expects it
    let count_high = ((n_leds - 1) >> 8) as u8; // LED count high byte
    let count_low = ((n_leds - 1) & 0xff) as u8; // LED count low byte
    [
        'A' as u8,
        'd' as u8,
        'a' as u8,
        count_high,
        count_low,
        count_high ^ count_low ^ 0x55, /* Checksum */
    ]
}

/// A timer to track time passed between refreshes. Can for example be used to limit frame rate.
struct FrameTimer {
    before: f64,
    last_frame_dt: f64,
}

impl FrameTimer {
    fn new() -> FrameTimer {
        FrameTimer {
            before: clock_ticks::precise_time_s(),
            last_frame_dt: 0.0,
        }
    }

    /// For how long the previous frame lasted
    fn last_frame_dt(&self) -> f64 {
        self.last_frame_dt
    }

    /// Time passed since last tick
    fn dt_to_now(&mut self) -> f64 {
        let now = clock_ticks::precise_time_s();
        let dt = now - self.before;
        if dt >= 0.0 {
            dt
        } else {
            self.before = now;
            0.0
        }
    }

    /// An update/frame/refresh has occured; take the time.
    fn tick(&mut self) {
        let now = clock_ticks::precise_time_s();
        self.last_frame_dt = partial_max(now - self.before, 0.0, 0.0);
        self.before = now;
    }
}

/// Writes color data via serial to LEDstream compatible device
struct ColorWriter {
    port: String,
    baud_rate: serial::BaudRate,
    con: serial::SystemPort,
    header: [u8; 6],
}

impl ColorWriter {
    /// Configure serial writing given a serial port, baud rate, and header to write before
    /// each data write
    fn new(port: &str, baud_rate: u32, header: [u8; 6]) -> Self {
        let serial_baud_rate = serial::BaudRate::from_speed(baud_rate as usize);
        let serial_con = ColorWriter::connect(port, serial_baud_rate).unwrap();
        ColorWriter {
            port: port.to_string(),
            baud_rate: serial_baud_rate,
            con: serial_con,
            header: header,
        }
    }

    fn connect(
        port: &str,
        baud_rate: serial::BaudRate,
    ) -> Result<serial::SystemPort, serial::Error> {
        serial::open(port).and_then(|mut con| {
            con.reconfigure(&|settings| {
                settings.set_char_size(serial::CharSize::Bits8);
                settings.set_baud_rate(baud_rate)
            })
            .map(|()| con)
        })
    }

    /// Write a buffer of color data to the LEDstream device
    fn write_colors(&mut self, color_data: &[Rgb8]) {
        use std::io::Write;
        let color_bytes = color::rgbs_as_bytes(color_data);
        match self.con.write(&self.header) {
            Ok(hn) if hn == self.header.len() => match self.con.write(color_bytes) {
                Ok(bn) if bn == color_bytes.len() => (),
                Ok(bn) => println!(
                    "Failed to write all bytes of RGB data. Wrote {} of {}",
                    bn,
                    color_data.len()
                ),
                Err(e) => println!("Failed to write RGB data, {}", e),
            },
            Ok(_) => println!("Failed to write all bytes in header"),
            Err(e) => {
                println!("Failed to write header, {}", e);
                // Probably, the serial device was temporarily disconnected or rebooted.
                // Try to reconnect forever
                loop {
                    print!("Reopening serial port... ");
                    match ColorWriter::connect(&self.port, self.baud_rate) {
                        Ok(con) => {
                            self.con = con;
                            println!("success!");
                            break;
                        }
                        Err(e) => {
                            println!("{}", e);
                            thread::sleep(time::Duration::from_millis(3000));
                        }
                    }
                }
            }
        }
    }
}

/// Update the colors to output by analyzing the captured frame for each led
fn update_out_color_data(
    out_pixels: &mut [Rgb8],
    frame_analyzer: &ImageAnalyzer,
    leds: &[config::Region],
    leds_transformers: &[Vec<(Option<RgbTransformer>, Option<&HSVTransformer>)>],
    smooth: SmoothFn,
    smooth_factor: f32,
) {
    for (i, &led) in leds.iter().enumerate() {
        let avg_color = frame_analyzer.average_color(led);
        let to_pixel = leds_transformers[i]
            .iter()
            .map(|&(ref opt_rgb, ref opt_hsv)| (opt_rgb.as_ref(), opt_hsv.as_ref()))
            .fold(
                Color::RGB(avg_color),
                |acc_color, transformers| match transformers {
                    (Some(rgb_tr), Some(hsv_tr)) => Color::HSV(
                        hsv_tr.transform(rgb_tr.transform(acc_color.into_rgb()).to_hsv()),
                    ),
                    (Some(rgb_tr), _) => Color::RGB(rgb_tr.transform(acc_color.into_rgb())),
                    (_, Some(hsv_tr)) => Color::HSV(hsv_tr.transform(acc_color.into_hsv())),
                    _ => acc_color,
                },
            )
            .into_rgb();
        out_pixels[i] = smooth(out_pixels[i], to_pixel, smooth_factor);
    }
}

/// Specifies how to loop over the main body
#[cfg(not(feature = "cpuprofiler"))]
fn main_loop<F: FnMut() -> bool>(mut body: F) {
    loop {
        if !body() {
            break;
        }
    }
}

/// Loop over the main body a fixed number of times and track performance with a profiler
#[cfg(feature = "cpuprofiler")]
fn main_loop<F: FnMut() -> bool>(mut body: F) {
    cpuprofiler::PROFILER
        .lock()
        .unwrap()
        .start("./prof.profile")
        .unwrap();
    for _ in 0..1000 {
        if !body() {
            break;
        }
    }
    cpuprofiler::PROFILER.lock().unwrap().stop().unwrap();
}

fn main() {
    let config = config::parse_config();
    let leds: &[_] = &config.leds;
    let mut led_transformers_list: Vec<_> = vec![Vec::with_capacity(1); leds.len()];
    // Add color transforms from config to each led in matching vec
    for transform_conf in config.color.transform.iter() {
        let hsv_transformer = if !transform_conf.hsv.is_default() {
            Some(&transform_conf.hsv)
        } else {
            None
        };
        let rgb_transformer = if !(transform_conf.red.is_default()
            && transform_conf.green.is_default()
            && transform_conf.red.is_default())
        {
            Some(RgbTransformer {
                r: transform_conf.red.clone(),
                g: transform_conf.green.clone(),
                b: transform_conf.blue.clone(),
            })
        } else {
            None
        };
        for range in parse_led_indices(&transform_conf.leds, leds.len()).iter() {
            for transformers in led_transformers_list[range.clone()].iter_mut() {
                transformers.push((rgb_transformer.clone(), hsv_transformer));
            }
        }
    }
    // Header to write before led data
    let out_header = new_pixel_buf_header(leds.len() as u16);
    let mut color_writer = ColorWriter::new(&config.device.output, config.device.rate, out_header);
    // Skeleton for the output led pixel buffer to write to arduino
    let mut out_pixels = vec![Rgb8 { r: 0, g: 0, b: 0 }; leds.len()];
    let mut capturer = Some(Capturer::new(config.device.input as usize).unwrap());
    let capture_frame_interval = 1.0 / config.framegrabber.frequency_Hz;
    // Function to use when smoothing led colors
    let smooth = match config.color.smoothing.type_.as_ref() {
        "linear" => color::linear_smooth as SmoothFn,
        _ => color::no_smooth as SmoothFn,
    };
    // max w/ 1 to avoid future divide by zero
    let smooth_time_const = max(config.color.smoothing.time_ms, 1) as f64 / 1000.0;
    let led_refresh_interval = 1.0 / config.color.smoothing.update_frequency;
    println!(
        "Helion - An LED streamer\nNumber of LEDs: {}\nResize resolution: {} x {}\nCapture \
         rate: {} fps\nLED refresh rate: {} hz\nCapture output: {}\nSerial port: {}",
        leds.len(),
        config.framegrabber.width,
        config.framegrabber.height,
        config.framegrabber.frequency_Hz,
        1.0 / led_refresh_interval,
        config.device.input,
        config.device.output
    );
    let mut capture_timer = FrameTimer::new();
    let mut led_refresh_timer = FrameTimer::new();
    main_loop(|| {
        led_refresh_timer.tick();
        // Don't capture new frame if going faster than frame limit,
        // but still proceed to smooth leds
        if capture_timer.dt_to_now() > capture_frame_interval {
            // If something goes wrong, last frame is reused
            match capturer.as_mut().unwrap().capture_store_frame() {
                Ok(_) => (),
                Err(CaptureError::Timeout) => {
                    println!("timeout");
                    thread::sleep(time::Duration::from_millis(100));
                    return true;
                }
                Err(e) => {
                    println!("Capture error: {:?}", e);
                    drop(capturer.take());
                    thread::sleep(time::Duration::from_millis(2_000));
                    loop {
                        match Capturer::new(config.device.input as usize) {
                            Ok(c) => {
                                capturer = Some(c);
                                break;
                            }
                            Err(e1) => {
                                println!("Error creating capturer: {:?}", e1);
                                thread::sleep(time::Duration::from_millis(1_000));
                            }
                        }
                    }
                }
            }
            capture_timer.tick();
        }
        if let Some(frame) = capturer.as_ref().unwrap().get_stored_frame() {
            let (w, h) = capturer.as_ref().unwrap().geometry();
            let frame_analyzer = ImageAnalyzer::new(
                frame,
                w as usize,
                h as usize,
                config.framegrabber.width,
                config.framegrabber.height,
            );
            let smooth_factor = (led_refresh_timer.last_frame_dt() / smooth_time_const) as f32;
            update_out_color_data(
                &mut out_pixels,
                &frame_analyzer,
                leds,
                &led_transformers_list,
                smooth,
                smooth_factor,
            );
            color_writer.write_colors(&out_pixels)
        }
        let time_left = led_refresh_interval - led_refresh_timer.dt_to_now();
        if time_left > 0.0 {
            let ms = if time_left > 0.0 {
                time_left * 1_000.0
            } else {
                0.0
            };
            thread::sleep(time::Duration::from_millis(ms as u64));
        }
        true
    })
}
<|endoftext|>


<commit_before>// Copyright © 2014, Peter Atashian

#![feature(slicing_syntax, tuple_indexing)]

extern crate image;
extern crate lodepng;
extern crate serialize;

use serialize::json::{decode};
use std::cell::Cell;
use std::io::{File};
use std::iter::AdditiveIterator;
use std::num::Zero;

#[deriving(Show)]
struct Coverage(char, f32);
impl PartialEq for Coverage {
    fn eq(&self, o: &Coverage) -> bool {
        self.1 == o.1
    }
}

#[deriving(Show, PartialEq)]
struct Pixel(f32, f32, f32);
impl Pixel {
    fn gray(&self) -> f32 {
        (self.0 + self.1 + self.2) / 3.
    }
}
impl Add<Pixel, Pixel> for Pixel {
    fn add(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 + o.0, self.1 + o.1, self.2 + o.2)
    }
}
impl Zero for Pixel {
    fn zero() -> Pixel {
        Pixel(0., 0., 0.)
    }
    fn is_zero(&self) -> bool {
        *self == Zero::zero()
    }
}

#[deriving(Show)]
struct Image {
    pixels: Vec<Cell<Pixel>>,
    width: u32,
    height: u32,
}
impl Image {
    fn get(&self, x: u32, y: u32) -> Pixel {
        self.pixels[(y * self.width + x) as uint].get()
    }
}

#[deriving(Decodable, Show)]
struct Config {
    winwidth: u32,
    winheight: u32,
    charwidth: u32,
    charheight: u32,
    offsetx: u32,
    offsety: u32,
    chars: String
}
impl Config {
    fn load() -> Config {
        let mut file = File::open(&Path::new("config.json")).unwrap();
        let data = file.read_to_string().unwrap();
        decode(data.as_slice()).unwrap()
    }
}

#[deriving(Show)]
struct Ascii {
    config: Config,
    table: Vec<f32>,
    colors: Vec<Pixel>,
    chars: Vec<Coverage>,
}
impl Ascii {
    fn new() -> Ascii {
        let config = Config::load();
        Ascii {
            config: config,
            table: Vec::new(),
            colors: Vec::new(),
            chars: Vec::new(),
        }
    }
    #[allow(dead_code)]
    fn print_chars(&self) {
        for x in range(0u16, 256) {
            bg(x as u8);
            print!(" ");
        }
        println!("");
        fg(15);
        bg(0);
        println!("{}", self.config.chars);
        let lines = 256 / self.config.winwidth;
        let lines = lines + self.config.chars[].chars().count() as u32 / self.config.winwidth;
        for _ in range(lines + 5, self.config.winheight) {
            println!("");
        }
    }
    fn build_table(&mut self) {
        fn decode(x: f32) -> f32 {
            if x <= 0.04045 {
                x / 12.92
            } else {
                ((x + 0.055) / (1. + 0.055)).powf(2.4)
            }
        }
        self.table = range(0u32, 256).map(|x| decode(x as f32 * (1. / 255.))).collect();
    }
    fn load(&self, p: &Path) -> Image {
        use image::GenericImage;
        assert!(self.table.len() == 256);
        let img = match lodepng::load(p) {
            Ok(x) => x,
            Err(_) => image::open(p).unwrap().to_rgba(),
        };
        let (w, h) = img.dimensions();
        let pixels = img.into_vec();
        let pixels = pixels.iter().map(|p| {
            let (&r, &g, &b, &a) = unsafe {(
                self.table[].unsafe_get(p.0 as uint),
                self.table[].unsafe_get(p.1 as uint),
                self.table[].unsafe_get(p.2 as uint),
                self.table[].unsafe_get(p.3 as uint),
            )};
            Cell::new(Pixel(r * a, g * a, b * a))
        }).collect();
        Image {
            pixels: pixels,
            width: w,
            height: h,
        }
    }
    fn calc_colors(&mut self) {
        let img = self.load(&Path::new("colors.png"));
        self.colors = range(0u32, 256).map(|i| {
            let x = i % self.config.winwidth * self.config.charwidth + self.config.offsetx;
            let y = i / self.config.winwidth * self.config.charheight + self.config.offsety;
            img.get(x, y)
        }).collect();
        let offset = (256 / self.config.winwidth + 1) * self.config.charheight
            + self.config.offsety;
        let (lo, hi) = (self.colors[0].gray(), self.colors[15].gray());
        self.chars = self.config.chars[].chars().enumerate().map(|(i, c)| {
            let x = i as u32 % self.config.winwidth * self.config.charwidth + self.config.offsetx;
            let y = i as u32 / self.config.winwidth * self.config.charheight + offset;
            let p = range(x, x + self.config.charwidth).map(|x| {
                range(y, y + self.config.charheight).map(|y| {
                    img.get(x, y).gray()
                }).sum()
            }).sum() / (self.config.charwidth * self.config.charheight) as f32;
            Coverage(c, (p - lo) / (hi - lo))
        }).collect();
        self.chars.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
        self.chars.dedup();
    }
    fn prepare(&mut self) {
        self.build_table();
        self.calc_colors();
    }
    fn convert(&self, p: &Path) {
        let img = self.load(p);
    }
}

fn fg(x: u8) {
    print!("\x1b[38;5;{}m", x);
}
fn bg(x: u8) {
    print!("\x1b[48;5;{}m", x);
}

fn main() {
    let mut ascii = Ascii::new();
    // ascii.print_chars();
    ascii.prepare();
    ascii.convert(&Path::new("RainbowDashFluff.png"));
}
<commit_msg>Seems to look good.<commit_after>// Copyright © 2014, Peter Atashian

#![feature(slicing_syntax, tuple_indexing)]

extern crate core;
extern crate image;
extern crate lodepng;
extern crate serialize;

use core::slice::{Found, NotFound};
use serialize::json::{decode};
use std::cell::{Cell};
use std::cmp::{max};
use std::collections::{PriorityQueue};
use std::io::{File};
use std::iter::{AdditiveIterator};
use std::num::{Zero};

#[deriving(Show)]
struct TermColor(Pixel, u8);
impl PartialEq for TermColor {
    fn eq(&self, o: &TermColor) -> bool {
        self.0 == o.0
    }
}

#[deriving(Show)]
struct Coverage(char, f32);
impl PartialEq for Coverage {
    fn eq(&self, o: &Coverage) -> bool {
        self.1 == o.1
    }
}

#[deriving(Show, PartialEq, PartialOrd)]
struct Pixel(f32, f32, f32);
impl Pixel {
    fn gray(&self) -> f32 {
        (self.0 + self.1 + self.2) / 3.
    }
    fn abs(&self) -> Pixel {
        Pixel(self.0.abs(), self.1.abs(), self.2.abs())
    }
    fn sqsum(&self) -> f32 {
        self.0 * self.0 + self.1 * self.1 + self.2 * self.2
    }
    fn dot(&self, o: &Pixel) -> f32 {
        self.0 * o.0 + self.1 * o.1 + self.2 * o.2
    }
}
impl Add<Pixel, Pixel> for Pixel {
    fn add(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 + o.0, self.1 + o.1, self.2 + o.2)
    }
}
impl Sub<Pixel, Pixel> for Pixel {
    fn sub(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 - o.0, self.1 - o.1, self.2 - o.2)
    }
}
impl Zero for Pixel {
    fn zero() -> Pixel {
        Pixel(0., 0., 0.)
    }
    fn is_zero(&self) -> bool {
        *self == Zero::zero()
    }
}
impl Mul<Pixel, Pixel> for Pixel {
    fn mul(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 * o.0, self.1 * o.1, self.2 * o.2)
    }
}
impl Div<Pixel, Pixel> for Pixel {
    fn div(&self, o: &Pixel) -> Pixel {
        Pixel(self.0 / o.0, self.1 / o.1, self.2 / o.2)
    }
}

#[deriving(Show)]
struct Image {
    pixels: Vec<Cell<Pixel>>,
    width: u32,
    height: u32,
}
impl Image {
    fn get(&self, x: u32, y: u32) -> Pixel {
        self.pixels[(y * self.width + x) as uint].get()
    }
    fn resize(&self, width: u32, height: u32) -> Image {
        let (rw, rh) = (self.width as f32 / (width as f32), self.height as f32 / (height as f32));
        let mut pixels = Vec::with_capacity((width * height) as uint);
        for y in range(0, height) {
            for x in range(0, width) {
                let (x1, x2) = ((x as f32 * rw) as u32, ((x + 1) as f32 * rw) as u32);
                let (y1, y2) = ((y as f32 * rh) as u32, ((y + 1) as f32 * rh) as u32);
                let (x2, y2) = (max(x2, x1 + 1), max(y2, y1 + 1));
                let c = range(x1, x2).map(|x| {
                    range(y1, y2).map(|y| {
                        self.get(x, y)
                    }).sum()
                }).sum();
                let m = 1. / (((x2 - x1) * (y2 - y1)) as f32);
                pixels.push(Cell::new(c * Pixel(m, m, m)));
            }
        }
        assert!(pixels.len() as u32 == width * height);
        Image {
            pixels: pixels,
            width: width,
            height: height,
        }
    }
}

#[deriving(Decodable, Show)]
struct Config {
    winwidth: u32,
    winheight: u32,
    charwidth: u32,
    charheight: u32,
    offsetx: u32,
    offsety: u32,
    chars: String
}
impl Config {
    fn load() -> Config {
        let mut file = File::open(&Path::new("config.json")).unwrap();
        let data = file.read_to_string().unwrap();
        decode(data.as_slice()).unwrap()
    }
}

#[deriving(Show)]
struct Ascii {
    config: Config,
    table: Vec<f32>,
    colors: Vec<TermColor>,
    chars: Vec<Coverage>,
}
impl Ascii {
    fn new() -> Ascii {
        let config = Config::load();
        Ascii {
            config: config,
            table: Vec::new(),
            colors: Vec::new(),
            chars: Vec::new(),
        }
    }
    #[allow(dead_code)]
    fn print_chars(&self) {
        for x in range(0u16, 256) {
            bg(x as u8);
            print!(" ");
        }
        println!("");
        fg(15);
        bg(0);
        println!("{}", self.config.chars);
        let lines = 256 / self.config.winwidth;
        let lines = lines + self.config.chars[].chars().count() as u32 / self.config.winwidth;
        for _ in range(lines + 5, self.config.winheight) {
            println!("");
        }
    }
    fn build_table(&mut self) {
        fn decode(x: f32) -> f32 {
            if x <= 0.04045 {
                x / 12.92
            } else {
                ((x + 0.055) / (1. + 0.055)).powf(2.4)
            }
        }
        self.table = range(0u32, 256).map(|x| decode(x as f32 * (1. / 255.))).collect();
    }
    fn load(&self, p: &Path) -> Image {
        use image::GenericImage;
        assert!(self.table.len() == 256);
        let img = match lodepng::load(p) {
            Ok(x) => x,
            Err(_) => image::open(p).unwrap().to_rgba(),
        };
        let (w, h) = img.dimensions();
        let pixels = img.into_vec();
        let pixels = pixels.iter().map(|p| {
            let (&r, &g, &b, &a) = unsafe {(
                self.table[].unsafe_get(p.0 as uint),
                self.table[].unsafe_get(p.1 as uint),
                self.table[].unsafe_get(p.2 as uint),
                self.table[].unsafe_get(p.3 as uint),
            )};
            Cell::new(Pixel(r * a, g * a, b * a))
        }).collect();
        Image {
            pixels: pixels,
            width: w,
            height: h,
        }
    }
    fn calc_colors(&mut self) {
        let img = self.load(&Path::new("colors.png"));
        self.colors = range(0u32, 256).map(|i| {
            let x = i % self.config.winwidth * self.config.charwidth + self.config.offsetx;
            let y = i / self.config.winwidth * self.config.charheight + self.config.offsety;
            TermColor(img.get(x, y), i as u8)
        }).collect();
        let offset = (256 / self.config.winwidth + 1) * self.config.charheight
            + self.config.offsety;
        let (lo, hi) = (self.colors[0].0.gray(), self.colors[15].0.gray());
        self.chars = self.config.chars[].chars().enumerate().map(|(i, c)| {
            let x = i as u32 % self.config.winwidth * self.config.charwidth + self.config.offsetx;
            let y = i as u32 / self.config.winwidth * self.config.charheight + offset;
            let p = range(x, x + self.config.charwidth).map(|x| {
                range(y, y + self.config.charheight).map(|y| {
                    img.get(x, y).gray()
                }).sum()
            }).sum() / (self.config.charwidth * self.config.charheight) as f32;
            Coverage(c, (p - lo) / (hi - lo))
        }).collect();
        self.colors.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
        self.colors.dedup();
        self.chars.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
        self.chars.dedup();
        self.chars.retain(|x| x.1 <= 0.5);
    }
    fn prepare(&mut self) {
        self.build_table();
        self.calc_colors();
    }
    fn fit_window(&self, img: &Image) -> Image {
        let (ww, wh) = (self.config.winwidth - 1, self.config.winheight - 3);
        let (cw, ch) = (self.config.charwidth, self.config.charheight);
        let fw = (cw * ww) as f32;
        let fh = (ch * wh) as f32;
        let ratio = fh / fw;
        let imgratio = img.height as f32 / img.width as f32;
        if ratio > imgratio {
            let h = (fw * imgratio / ch as f32) as u32;
            img.resize(ww, h)
        } else {
            let w = (fh / imgratio / cw as f32) as u32;
            img.resize(w, wh)
        }
    }
    fn closest(&self, p: &Pixel) -> (u8, u8, char, Pixel) {
        struct Entry(Pixel, u8, f32);
        impl Ord for Entry {
            fn cmp(&self, o: &Entry) -> Ordering {
                self.partial_cmp(o).unwrap()
            }
        }
        impl PartialOrd for Entry {
            fn partial_cmp(&self, o: &Entry) -> Option<Ordering> {
                self.2.partial_cmp(&o.2).map(|x| x.reverse())
            }
        }
        impl Eq for Entry {}
        impl PartialEq for Entry {
            fn eq(&self, o: &Entry) -> bool {
                self.2 == o.2
            }
        }
        let mut colors: PriorityQueue<_> = self.colors.iter().map(|c| {
            let d = (c.0 - *p).abs().gray();
            Entry(c.0, c.1, d)
        }).collect();
        let colors: Vec<_> = range(0, 10u).map(|_| colors.pop().unwrap()).collect();
        let mut bestdiff = 100f32;
        let mut best = (0, colors[0].1, ' ', p - colors[0].0);
        for c1 in colors.iter() {
            for c2 in colors.iter() {
                let (v1, v2) = (p - c1.0, c2.0 - c1.0);
                let cover = if v2.abs().gray() < 0.0001 {
                    0.
                } else {
                    v1.dot(&v2) / v2.sqsum()
                };
                if cover < 0. || cover > 0.5 { continue }
                let Coverage(ch, n) = match self.chars[].binary_search(|x| {
                    x.1.partial_cmp(&cover).unwrap()
                }) {
                    Found(x) => self.chars[x],
                    NotFound(x) => self.chars[x - 1],
                };
                let color = c1.0 * Pixel(1. - n, 1. - n, 1. - n) + c2.0 * Pixel(n, n, n);
                let diff = color - *p;
                let sdiff = c2.0 - c1.0;
                let d = diff.abs().sqsum();
                if d < bestdiff {
                    bestdiff = d;
                    best = (c2.1, c1.1, ch, diff);
                }
            }
        }
        best
    }
    fn convert(&self, p: &Path) {
        let img = self.load(p);
        let mut img = self.fit_window(&img);
        let width = img.width;
        img.pixels.reserve_additional(width as uint * 2);
        let pixels = img.pixels[];
        for (y, chunk) in pixels.chunks(img.width as uint).enumerate() {
            for (x, p) in chunk.iter().enumerate() {
                let (f, b, c, diff) = self.closest(&p.get());
                let adjust = |ox, oy, mult| {
                    let cell = unsafe { pixels.unsafe_get((y + oy) * (width as uint) + x + ox) };
                    cell.set(cell.get() + diff * Pixel(mult, mult, mult));
                };
                adjust( 1, 0, 5. / 32.);
                adjust( 2, 0, 3. / 32.);
                adjust( 2, 1, 2. / 32.);
                adjust(-1, 1, 4. / 32.);
                adjust(-0, 1, 5. / 32.);
                adjust( 1, 1, 4. / 32.);
                adjust( 2, 1, 2. / 32.);
                adjust(-1, 2, 2. / 32.);
                adjust( 0, 2, 3. / 32.);
                adjust( 1, 2, 2. / 32.);
                fg(f);
                bg(b);
                print!("{}", c);
            }
            bg(0);
            println!("");
        }
    }
}

fn fg(x: u8) {
    print!("\x1b[38;5;{}m", x);
}
fn bg(x: u8) {
    print!("\x1b[48;5;{}m", x);
}

fn main() {
    let mut ascii = Ascii::new();
    // ascii.print_chars();
    ascii.prepare();
    ascii.convert(&Path::new("winter_evening_by_gign_3208-d6vvbvo.png"));
    // ascii.convert(&Path::new("RainbowDashFluff.png"));
}
<|endoftext|>


<commit_before>use std::cell::Cell;

struct Coordinates {
    x: Cell<i32>,
    y: Cell<i32>,
}

fn main() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    move_up(&origin.y); // borrow the value
    move_up(&origin.y); // so that it can change
    move_down(&origin.y);
    move_right(&origin.x);
    move_right(&origin.x);
    move_left(&origin.x);

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);
}

fn move_up(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get(); // get the value and store it
    y_cord.set(new_val + 1); // modify the stored value and set the incoming param
    y_cord // no semicolon here because it is the return value
}

fn move_down(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get(); // get the value and store it
    y_cord.set(new_val - 1); // modify the stored value and set the incoming param
    y_cord // no semicolon here because it is the return value
}

fn move_right(x_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = x_cord.get(); // get the value and store it
    x_cord.set(new_val + 1); // modify the stored value and set the incoming param
    x_cord // no semicolon here because it is the return value
}

fn move_left(x_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = x_cord.get(); // get the value and store it
    x_cord.set(new_val - 1); // modify the stored value and set the incoming param
    x_cord // no semicolon here because it is the return value
}

#[test]
fn it_can_move() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    assert_eq!(0, origin.y.get());

    move_up(&origin.y);

    assert_eq!(1, origin.y.get());

    move_up(&origin.y);
    move_up(&origin.y);
    move_up(&origin.y);

    assert_eq!(4, origin.y.get());

    for _ in 0..19 { // _ instead of x so that rust does not complain about x not being used
        move_up(&origin.y); // moves up 19 times
    }

    assert_eq!(23, origin.y.get());

    // test it can move down

    for _ in 0..19 {
        move_down(&origin.y);
    }

    assert_eq!(4, origin.y.get());

    // test it can move right

    for _ in 0..19 {
        move_right(&origin.x);
    }

    assert_eq!(19, origin.x.get());

    // test it can move left

    for _ in 0..10 {
        move_left(&origin.x);
    }

    assert_eq!(9, origin.x.get());
}
<commit_msg>one line comment code for borrow explanation<commit_after>use std::cell::Cell;

struct Coordinates {
    x: Cell<i32>,
    y: Cell<i32>,
}

fn main() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    move_up(&origin.y); // borrow the value so that it can change (using the '&' symbol borrows the var)
    move_up(&origin.y);
    move_down(&origin.y);
    move_right(&origin.x);
    move_right(&origin.x);
    move_left(&origin.x);

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);
}

fn move_up(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get(); // get the value and store it
    y_cord.set(new_val + 1); // modify the stored value and set the incoming param
    y_cord // no semicolon here because it is the return value
}

fn move_down(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get(); // get the value and store it
    y_cord.set(new_val - 1); // modify the stored value and set the incoming param
    y_cord // no semicolon here because it is the return value
}

fn move_right(x_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = x_cord.get(); // get the value and store it
    x_cord.set(new_val + 1); // modify the stored value and set the incoming param
    x_cord // no semicolon here because it is the return value
}

fn move_left(x_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = x_cord.get(); // get the value and store it
    x_cord.set(new_val - 1); // modify the stored value and set the incoming param
    x_cord // no semicolon here because it is the return value
}

#[test]
fn it_can_move() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    assert_eq!(0, origin.y.get());

    move_up(&origin.y);

    assert_eq!(1, origin.y.get());

    move_up(&origin.y);
    move_up(&origin.y);
    move_up(&origin.y);

    assert_eq!(4, origin.y.get());

    for _ in 0..19 { // _ instead of x so that rust does not complain about x not being used
        move_up(&origin.y); // moves up 19 times
    }

    assert_eq!(23, origin.y.get());

    // test it can move down

    for _ in 0..19 {
        move_down(&origin.y);
    }

    assert_eq!(4, origin.y.get());

    // test it can move right

    for _ in 0..19 {
        move_right(&origin.x);
    }

    assert_eq!(19, origin.x.get());

    // test it can move left

    for _ in 0..10 {
        move_left(&origin.x);
    }

    assert_eq!(9, origin.x.get());
}
<|endoftext|>


<commit_before>// Worklog is for recording your hours.
//

extern crate chrono;
extern crate csv;
extern crate getopts;
extern crate rustc_serialize;

mod error;
mod timeclock;
mod util;

use chrono::*;
use error::WorklogError;
use getopts::Options;
use std::env;
use std::error::Error;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::path::PathBuf;
use timeclock::direction::Direction;

#[cfg(target_family = "unix")]
static CSV_FILE_NAME: &'static str = ".worklog.csv";
#[cfg(target_family = "windows")]
static CSV_FILE_NAME: &'static str = "worklog.csv";

// TODO This belongs in a config file somewhere
const WEEKSTART: i64 = Weekday::Sat as i64;

fn print_csv_entries<R: Read>(file: R) -> Result<(), WorklogError> {
    let csv_entries = try!(timeclock::read_timesheet(file));
    for rec in csv_entries {
        println!("{}", rec);
    }
    Ok(())
}

fn print_full_summary(file: &File) -> Result<u8, WorklogError> {
    let csv_entries = try!(timeclock::read_timesheet(file));
    let entries = timeclock::pair_time_entries(csv_entries);
    let records = timeclock::collect_date_records(entries);

    let mut total_hours: f64 = 0.0;
    for rec in records {
        total_hours += rec.hours();
        println!("{}", rec);
    }

    println!("Total Hours: {:.2}", total_hours);
    Ok(0)
}

fn print_short_summary(file: &File,
                       since: Date<FixedOffset>)
                       -> Result<(), WorklogError> {
    let csv_entries = try!(timeclock::read_timesheet(file));
    let entries = timeclock::pair_time_entries(csv_entries);
    let records = timeclock::collect_date_records(entries);

    let mut total_hours: f64 = 0.0;
    for rec in records {
        if rec.date >= since {
            total_hours += rec.hours();
            println!("{}", rec);
        }
    }

    println!("Total Hours: {:.2}", total_hours);
    Ok(())
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

/// Get the path for the csv data file
fn get_csv_path() -> Result<PathBuf, WorklogError> {
    let mut data_path = match env::home_dir() {
        Some(path) => path,
        None => {
            return Err(env::VarError::NotPresent).map_err(WorklogError::Env)
        }
    };

    data_path.push(CSV_FILE_NAME);
    return Ok(data_path);
}

fn main0() -> Result<i8, WorklogError> {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflagopt("i", "in", "in time", "TIME");
    opts.optflagopt("o", "out", "out time", "TIME");
    opts.optopt("m", "memo", "the memo", "MEMO");
    opts.optflag("s", "summary", "show the full summary");
    opts.optflag("e", "entries", "show all entries");
    opts.optflag("h", "help", "print this help menu");

    let matches = try!(opts.parse(&args[1..]));

    let csv_path = try!(get_csv_path());

    let csv_file = try!(OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(csv_path));

    if matches.opt_present("h") {
        print_usage(&program, opts);

    } else if matches.opts_present(&["i".to_owned(), "o".to_owned()]) {
        // The ridiculous array above isn't my fault.

        // Reject --in and --out if present at the same time
        if matches.opt_present("i") && matches.opt_present("o") {
            println!("--in or --out, not both");
            return Ok(1);
        }

        let dir = match matches.opt_present("i") {
            true => Direction::In,
            false => Direction::Out,
        };

        let time = matches.opt_str("i")
            .or(matches.opt_str("o"))
            .or(Some("now".to_owned()))
            .unwrap();
        let time = try!(util::parse_multi_time_fmt(&time));

        let memo = matches.opt_str("m").or(Some("".to_owned())).unwrap();
        timeclock::mark_time(dir, time, memo, csv_file);

        let dir_str = format!("{}", dir).to_lowercase();
        println!("Clocked {} at {}", dir_str, time.format("%F %I:%M %P"));

    } else if matches.opt_present("s") {
        try!(print_full_summary(&csv_file));

    } else if matches.opt_present("e") {
        try!(print_csv_entries(&csv_file));

    } else {
        let days_back = (-WEEKSTART + 7) % 7;
        let start_date = util::now().date() - Duration::days(days_back);
        try!(print_short_summary(&csv_file, start_date));
    }

    Ok(0)
}

fn main() {
    match main0() {
        Ok(_) => {}
        Err(err) => println!("Whoops! {}", err.description()),
    };
}
<commit_msg>clean up print_* functions<commit_after>// Worklog is for recording your hours.
//

extern crate chrono;
extern crate csv;
extern crate getopts;
extern crate rustc_serialize;

mod error;
mod timeclock;
mod util;

use chrono::*;
use error::WorklogError;
use getopts::Options;
use std::env;
use std::error::Error;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::path::PathBuf;
use timeclock::direction::Direction;

#[cfg(target_family = "unix")]
static CSV_FILE_NAME: &'static str = ".worklog.csv";
#[cfg(target_family = "windows")]
static CSV_FILE_NAME: &'static str = "worklog.csv";

// TODO This belongs in a config file somewhere
const WEEKSTART: i64 = Weekday::Sat as i64;

fn print_csv_entries<R: Read>(file: R) -> Result<(), WorklogError> {
    let csv_entries = try!(timeclock::read_timesheet(file));
    for rec in csv_entries {
        println!("{}", rec);
    }
    Ok(())
}

fn print_full_summary<R: Read>(file: R) -> Result<(), WorklogError> {
    let csv_entries = try!(timeclock::read_timesheet(file));
    let entries = timeclock::pair_time_entries(csv_entries);
    let records = timeclock::collect_date_records(entries);

    let mut total_hours: f64 = 0.0;
    for rec in records {
        total_hours += rec.hours();
        println!("{}", rec);
    }

    println!("Total Hours: {:.2}", total_hours);
    Ok(())
}

fn print_short_summary<R: Read>(file: R,
                                since: Date<FixedOffset>)
                                -> Result<(), WorklogError> {
    let csv_entries = try!(timeclock::read_timesheet(file));
    let entries = timeclock::pair_time_entries(csv_entries);
    let records = timeclock::collect_date_records(entries);

    let mut total_hours: f64 = 0.0;
    for rec in records {
        if rec.date >= since {
            total_hours += rec.hours();
            println!("{}", rec);
        }
    }

    println!("Total Hours: {:.2}", total_hours);
    Ok(())
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

/// Get the path for the csv data file
fn get_csv_path() -> Result<PathBuf, WorklogError> {
    let mut data_path = match env::home_dir() {
        Some(path) => path,
        None => {
            return Err(env::VarError::NotPresent).map_err(WorklogError::Env)
        }
    };

    data_path.push(CSV_FILE_NAME);
    return Ok(data_path);
}

fn main0() -> Result<i8, WorklogError> {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflagopt("i", "in", "in time", "TIME");
    opts.optflagopt("o", "out", "out time", "TIME");
    opts.optopt("m", "memo", "the memo", "MEMO");
    opts.optflag("s", "summary", "show the full summary");
    opts.optflag("e", "entries", "show all entries");
    opts.optflag("h", "help", "print this help menu");

    let matches = try!(opts.parse(&args[1..]));

    let csv_path = try!(get_csv_path());

    let csv_file = try!(OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(csv_path));

    if matches.opt_present("h") {
        print_usage(&program, opts);

    } else if matches.opts_present(&["i".to_owned(), "o".to_owned()]) {
        // The ridiculous array above isn't my fault.

        // Reject --in and --out if present at the same time
        if matches.opt_present("i") && matches.opt_present("o") {
            println!("--in or --out, not both");
            return Ok(1);
        }

        let dir = match matches.opt_present("i") {
            true => Direction::In,
            false => Direction::Out,
        };

        let time = matches.opt_str("i")
            .or(matches.opt_str("o"))
            .or(Some("now".to_owned()))
            .unwrap();
        let time = try!(util::parse_multi_time_fmt(&time));

        let memo = matches.opt_str("m").or(Some("".to_owned())).unwrap();
        timeclock::mark_time(dir, time, memo, csv_file);

        let dir_str = format!("{}", dir).to_lowercase();
        println!("Clocked {} at {}", dir_str, time.format("%F %I:%M %P"));

    } else if matches.opt_present("s") {
        try!(print_full_summary(&csv_file));

    } else if matches.opt_present("e") {
        try!(print_csv_entries(&csv_file));

    } else {
        let days_back = (-WEEKSTART + 7) % 7;
        let start_date = util::now().date() - Duration::days(days_back);
        try!(print_short_summary(&csv_file, start_date));
    }

    Ok(0)
}

fn main() {
    match main0() {
        Ok(_) => {}
        Err(err) => println!("Whoops! {}", err.description()),
    };
}
<|endoftext|>


<commit_before>extern crate gtk;
extern crate gtk_sys;
extern crate gdk;
extern crate glib;
extern crate cairo;

use gtk::prelude::*;
use gtk::{Label, Window, WindowType};

use glib::translate::*;

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let window = Window::new(WindowType::Toplevel);
    window.set_title("Dropzone");
    window.set_default_size(350, 70);
    window.set_keep_above(true);
    window.set_skip_taskbar_hint(true);
    window.set_skip_pager_hint(true);
    window.set_deletable(false);
    window.set_decorated(false);

    let zone = Label::new(Some("zone"));
    unsafe {
        // FIXME: use wrapped API
        gtk_sys::gtk_drag_dest_set(zone.to_glib_none().0,
                                   gtk_sys::GtkDestDefaults::all(),
                                   std::ptr::null_mut(),
                                   0,
                                   gdk::DragAction::all());
    }
    zone.drag_dest_add_text_targets();
    zone.connect_drag_data_received(|_self, _drag_context, _x, _y, data, _info, _time| {
        if let Some(text) = data.get_text() {
            println!("{}", text);
        }
    });

    window.add(&zone);
    window.show_all();

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    gtk::main();
}
<commit_msg>transparent window<commit_after>extern crate gtk;
extern crate gtk_sys;
extern crate gdk;
extern crate glib;
extern crate cairo;

use gtk::prelude::*;
use gdk::prelude::*;
use glib::translate::*;

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    window.set_title("Dropzone");
    window.set_default_size(350, 70);
    window.set_keep_above(true);
    window.set_skip_taskbar_hint(true);
    window.set_skip_pager_hint(true);
    window.set_deletable(false);
    window.set_decorated(false);
    window.set_app_paintable(true);

    window.connect_draw(|window, _context| {
        let cr = cairo::Context::create_from_window(&window.get_window().unwrap());
        cr.set_source_rgba(0.0, 0.0, 0.0, 0.5);
        cr.set_operator(cairo::Operator::Source);
        cr.paint();
        Inhibit(false)
    });
    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });
    let update_visual = |window: &gtk::Window, _old_screen: &Option<gdk::Screen>| {
        let screen = gtk::WindowExt::get_screen(window).unwrap();
        let visual = screen.get_rgba_visual();

        window.set_visual(visual.as_ref());
    };
    update_visual(&window, &None);
    window.connect_screen_changed(update_visual);

    let zone = gtk::Label::new(Some("zone"));
    unsafe {
        // FIXME: use wrapped API
        gtk_sys::gtk_drag_dest_set(zone.to_glib_none().0,
                                   gtk_sys::GtkDestDefaults::all(),
                                   std::ptr::null_mut(),
                                   0,
                                   gdk::DragAction::all());
    }
    zone.drag_dest_add_text_targets();
    zone.connect_drag_data_received(|_self, _drag_context, _x, _y, data, _info, _time| {
        if let Some(text) = data.get_text() {
            println!("{}", text);
        }
    });
    window.add(&zone);

    window.show_all();

    gtk::main();
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
use rustc_serialize::json;

extern crate rs_es;
use rs_es::Client;
use rs_es::operations::search::{Sort, SortField, Order};

extern crate postgres;
use postgres::{Connection, SslMode};

extern crate iron;
use iron::prelude::*;
use iron::status;
use iron::mime::Mime;

extern crate router;
use router::Router;

extern crate params;
use params::*;

extern crate honeysearch;
use honeysearch::resources::user::User;

use std::env;

#[derive(Debug, RustcDecodable)]
struct TalentsSearchResult {
  id: i32
}

const PG_URL:     &'static str            = "postgres://lando@localhost/lando_development";
const ES_INDEXES: &'static [&'static str] = &["honeypot_dev_talents"];
const VERSION:    &'static str            = env!("CARGO_PKG_VERSION");

fn main() {
  let port = env::args().nth(1).unwrap_or(String::from("3000"));
  let host = format!("127.0.0.1:{}", port);

  println!("Honeysearch v{}", VERSION);
  println!("Listening on http://{}...", host);

  let mut router = Router::new();
  router.get("/talents", talents);

  Iron::new(router).http(&*host).unwrap();
}

fn talents(req: &mut Request) -> IronResult<Response> {
  let mut es = Client::new("localhost", 9200);
  let     pg = Connection::connect(PG_URL, SslMode::None).unwrap();

  let params = req.get_ref::<Params>().ok().unwrap();

  let result = es.search_query()
                 .with_indexes(ES_INDEXES)
                 .with_query(&User::search_filters(&pg, params))
                 .with_sort(&Sort::new(
                   vec![
                     SortField::new("updated_at", Some(Order::Desc)).build()
                   ]))
                 .send()
                 .ok()
                 .unwrap();

  let user_ids = result.hits.hits.into_iter()
                                 .filter_map(|hit| {
                                   let talent: TalentsSearchResult = hit.source().unwrap();
                                   User::find(&pg, &talent.id)
                                 })
                                 .collect::<Vec<User>>();

  let content_type = "application/json".parse::<Mime>().unwrap();
  Ok(Response::with((content_type, status::Ok, json::encode(&user_ids).unwrap())))
}
<commit_msg>main: implement Config<commit_after>#![allow(non_upper_case_globals)]
#[macro_use]
extern crate lazy_static;

extern crate rustc_serialize;
use rustc_serialize::json;

extern crate rs_es;
use rs_es::Client;
use rs_es::operations::search::{Sort, SortField, Order};

extern crate postgres;
use postgres::{Connection, SslMode};

extern crate iron;
use iron::prelude::*;
use iron::status;
use iron::mime::Mime;

extern crate router;
use router::Router;

extern crate params;
use params::*;

extern crate honeysearch;
use honeysearch::resources::user::User;
use honeysearch::config::*;

use std::env;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

lazy_static! {
  static ref config: Config = Config::load_config(env::args()
                                                      .nth(1)
                                                      .unwrap_or("honeysearch.toml".to_owned()));
}

fn main() {
  let host = format!("{}:{}", config.http.host, config.http.port);

  println!("Honeysearch v{}", VERSION);
  println!("Listening on http://{}...", host);

  let mut router = Router::new();
  router.get("/talents", talents);

  Iron::new(router).http(&*host).unwrap();
}

#[derive(Debug, RustcDecodable)]
struct TalentsSearchResult {
  id: i32
}

fn talents(req: &mut Request) -> IronResult<Response> {
  let mut es = Client::new(&*config.es.host, config.es.port);
  let     pg = Connection::connect(&*config.db.uri, SslMode::None).unwrap();

  let params = req.get_ref::<Params>().ok().unwrap();
  let result = es.search_query()
                 .with_indexes(&config.es.indexes.clone()
                                                 .iter()
                                                 .map(|e| &**e)
                                                 .collect::<Vec<&str>>())
                 .with_query(&User::search_filters(&pg, params))
                 .with_sort(&Sort::new(
                   vec![
                     SortField::new("updated_at", Some(Order::Desc)).build()
                   ]))
                 .send()
                 .ok()
                 .unwrap();

  let user_ids = result.hits.hits.into_iter()
                                 .filter_map(|hit| {
                                   let talent: TalentsSearchResult = hit.source().unwrap();
                                   User::find(&pg, &talent.id)
                                 })
                                 .collect::<Vec<User>>();

  let content_type = "application/json".parse::<Mime>().unwrap();
  Ok(Response::with((content_type, status::Ok, json::encode(&user_ids).unwrap())))
}
<|endoftext|>


<commit_before>/*
  main.rs
  Platypie's core executable
  currently only requires Rust's standard API
*/

fn main() {
  let message = [
    "Thanks for installing Platypie!",
    "We're trying to make the world a better place.",
    "We're very happy you've decided to get involved.",
    "Platypie is a work in progress.",
    "If you want to get involved, join us on IRC!",
    "irc.freenode.net/#platypie"
  ];

  for sptr in message.iter() {
    println!("{}",*sptr);
  }
}

#[test]
fn main_test() {
  assert!(true); // the simplest test possible, a tautology
}
<commit_msg>style compliant<commit_after>/*
    main.rs
    Platypie's core executable
    currently only requires Rust's standard API
*/

fn main() {
    let message = [
        "Thanks for installing Platypie!",
        "We're trying to make the world a better place.",
        "We're very happy you've decided to get involved.",
        "Platypie is a work in progress.",
        "If you want to get involved, join us on IRC!",
        "irc.freenode.net/#platypie"
  ];

    for sptr in message.iter() {
        println!("{}",*sptr);
  }
}

#[test]
fn main_test() {
    assert!(true); // the simplest test possible, a tautology
}
<|endoftext|>


<commit_before>/// Represent the state of the game as a graph.  One node for each cell

fn main() {
}
<commit_msg>Start with macroquad example program<commit_after>/// Represent the state of the game as a graph.  One node for each cell

use macroquad::prelude::*;

#[macroquad::main("BasicShapes")]
async fn main() {
    loop {
        clear_background(RED);

        draw_line(40.0, 40.0, 100.0, 200.0, 15.0, BLUE);
        draw_rectangle(screen_width() / 2.0 - 60.0, 100.0, 120.0, 60.0, GREEN);
        draw_circle(screen_width() - 30.0, screen_height() - 30.0, 15.0, YELLOW);
        draw_text("HELLO", 20.0, 20.0, 20.0, DARKGRAY);

        next_frame().await
    }
}
<|endoftext|>