<commit_before>extern crate time;

fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();
    let (year, month, day, yoff) = current_day(time::now());
    let wd = (yoff + month * 3 + day) % dpw;
    let mut moff = yoff;

    for mm in 0..mpy {
        let spaces = (17 - moy[mm].len()) / 2;
        for _ in 0..spaces {
            print!(" ");
        }
        println!("{}", moy[mm]);

        for dd in 0..dpw {
            let mut abbrd = String::from(dow[dd]);
            abbrd.truncate(2);
            print!("{} ", abbrd);
        }

        let dd = if mm == month { day } else { 1000 };
        moff = print_days(moff, dpm, dpw, dd);
        if mm == 2 {
            println!("-- Peer Day --");
        }
        else if mm == 6 {
            println!("-- Torrent Feast --");
        }
        else if is_leap_year(year) && mm == 10 {
            println!("-- Immersion Feast --");
        }
    }

    let mut datestr = format!("{}, {:02} of {}", dow[wd], day, moy[month]);
    let mut daystr = format!("{:02}.{:02}", month + 1, day);
    if day == 34 {
        if month == 2 {
            datestr = format!("Peer Day");
            daystr = format!("P");
        }
        else if month == 6 {
            datestr = format!("Torrent Feast");
            daystr = format!("T");
        }
        else if month == 10 {
            datestr = format!("Immersion Feast");
            daystr = format!("T");
        }
    }
    let hol = format!("{}", what_holiday(month, day));
    println!("Today is {} {}{}.  [{}.{}]", datestr, year, hol, year, daystr);
}

fn print_days(moff: usize, dpm: usize, dpw: usize, highlight: usize) -> usize {
    if moff != 0 {
        println!("");
    }

    for _ in 0 .. moff {
        print!("   ");
    }

    for day in 1 .. dpm + 1 {
        if (day + moff) % dpw == 1 {
            println!("");
        }

        let ch = if day == highlight { ">" } else if day + 1 == highlight { "<" } else { " " };
        print!("{:2}{}", day, ch);
    }

    println!("");
    return if moff == 0 { 3 } else { 0 };
}

fn current_day(current_time: time::Tm) -> (usize, usize, usize, usize) {
    let epoch_days_offset = 1483889;
    let seconds_per_day = 86400;
    let now = current_time.to_timespec();
    let days_since_epoch = (epoch_days_offset + now.sec / seconds_per_day) as i32;
    let days_per_year = 365;
    let leap_year = 4;
    let not_leap_year = 33;
    let days_per_month = 33;

    let mut count = 1;
    let mut days_remaining = days_since_epoch;
    let mut year = 0;
    while days_remaining > days_per_year + 1 {
        days_remaining -= days_per_year;
        year += 1;
        if count > not_leap_year {
            count = 1;
        }
        if count % leap_year == 0 {
            days_remaining -= 1;
        }
        count += 1;
    }
    if days_remaining == days_per_year + 1 && count % leap_year == 0 {
        days_remaining = 0;
        year += 1;
    }

    let mut month = 0;
    while days_remaining > days_per_month {
        month += 1;
        days_remaining -= days_per_month;
        if month == 3 || month == 7 || month == 11 {
            days_remaining -= 1;
        }
    }

    let day = days_remaining;
    return (year, month as usize, day as usize + 1, (year % 2 * 3) as usize);
}

fn is_leap_year(year: usize) -> bool {
    let rem = year % 33;
    return rem != 0 && rem % 4 == 0;
}

fn what_holiday(month: usize, day: usize) -> String {
    let mut result = format!("");
    if month == 0 && day == 1 {
        result = format!(", New Year's Day");
    }
    else if month == 2 && day == 12 {
        result = format!(", Purification Day");
    }
    else if month == 5 && day == 4 {
        result = format!(", Harvest Gift");
    }
    else if month == 7 && day == 29 {
        result = format!(", Familytide");
    }
    else if month == 10 && day == 21 {
        result = format!(", Freedom Day");
    }
    else if month == 0 && day == 27 {
        result = format!(", International Workers' Day");
    }
    return result;
}

<commit_msg>Reformat epagomenal days<commit_after>extern crate time;

fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();
    let (year, month, day, yoff) = current_day(time::now());
    let wd = (yoff + month * 3 + day) % dpw;
    let mut moff = yoff;

    for mm in 0..mpy {
        let spaces = (17 - moy[mm].len()) / 2;
        for _ in 0..spaces {
            print!(" ");
        }
        println!("{}", moy[mm]);

        for dd in 0..dpw {
            let mut abbrd = String::from(dow[dd]);
            abbrd.truncate(2);
            print!("{} ", abbrd);
        }

        let dd = if mm == month { day } else { 1000 };
        moff = print_days(moff, dpm, dpw, dd);
        if mm == 2 {
            println!("--  Peer  Day  --");
        }
        else if mm == 6 {
            println!("--Torrent Feast--");
        }
        else if is_leap_year(year) && mm == 10 {
            println!("--Immersion Day--");
        }
    }

    let mut datestr = format!("{}, {:02} of {}", dow[wd], day, moy[month]);
    let mut daystr = format!("{:02}.{:02}", month + 1, day);
    if day == 34 {
        if month == 2 {
            datestr = format!("Peer Day");
            daystr = format!("P");
        }
        else if month == 6 {
            datestr = format!("Torrent Feast");
            daystr = format!("T");
        }
        else if month == 10 {
            datestr = format!("Immersion Feast");
            daystr = format!("T");
        }
    }
    let hol = format!("{}", what_holiday(month, day));
    println!("Today is {} {}{}.  [{}.{}]", datestr, year, hol, year, daystr);
}

fn print_days(moff: usize, dpm: usize, dpw: usize, highlight: usize) -> usize {
    if moff != 0 {
        println!("");
    }

    for _ in 0 .. moff {
        print!("   ");
    }

    for day in 1 .. dpm + 1 {
        if (day + moff) % dpw == 1 {
            println!("");
        }

        let ch = if day == highlight { ">" } else if day + 1 == highlight { "<" } else { " " };
        print!("{:2}{}", day, ch);
    }

    println!("");
    return if moff == 0 { 3 } else { 0 };
}

fn current_day(current_time: time::Tm) -> (usize, usize, usize, usize) {
    let epoch_days_offset = 1483889;
    let seconds_per_day = 86400;
    let now = current_time.to_timespec();
    let days_since_epoch = (epoch_days_offset + now.sec / seconds_per_day) as i32;
    let days_per_year = 365;
    let leap_year = 4;
    let not_leap_year = 33;
    let days_per_month = 33;

    let mut count = 1;
    let mut days_remaining = days_since_epoch;
    let mut year = 0;
    while days_remaining > days_per_year + 1 {
        days_remaining -= days_per_year;
        year += 1;
        if count > not_leap_year {
            count = 1;
        }
        if count % leap_year == 0 {
            days_remaining -= 1;
        }
        count += 1;
    }
    if days_remaining == days_per_year + 1 && count % leap_year == 0 {
        days_remaining = 0;
        year += 1;
    }

    let mut month = 0;
    while days_remaining > days_per_month {
        month += 1;
        days_remaining -= days_per_month;
        if month == 3 || month == 7 || month == 11 {
            days_remaining -= 1;
        }
    }

    let day = days_remaining;
    return (year, month as usize, day as usize + 1, (year % 2 * 3) as usize);
}

fn is_leap_year(year: usize) -> bool {
    let rem = year % 33;
    return rem != 0 && rem % 4 == 0;
}

fn what_holiday(month: usize, day: usize) -> String {
    let mut result = format!("");
    if month == 0 && day == 1 {
        result = format!(", New Year's Day");
    }
    else if month == 2 && day == 12 {
        result = format!(", Purification Day");
    }
    else if month == 5 && day == 4 {
        result = format!(", Harvest Gift");
    }
    else if month == 7 && day == 29 {
        result = format!(", Familytide");
    }
    else if month == 10 && day == 21 {
        result = format!(", Freedom Day");
    }
    else if month == 0 && day == 27 {
        result = format!(", International Workers' Day");
    }
    return result;
}

<|endoftext|>


<commit_before>#[cfg(test)]
#[macro_use]
mod test_util;

#[macro_use]
mod util;

mod codegen;
mod diagnostics;
mod index;
mod ir;

use crate::diagnostics::{DiagnosticsCtx, SourceFileMap, Span};
use crate::index::{Ident, Path};
use crate::ir::*;
use crate::util::DisplayName;
use clang::{
    self,
    source::{File, SourceRange},
    Accessibility, Clang, Entity, EntityKind, Parser, SourceError, TranslationUnit, Type, TypeKind,
};
use std::cell::RefCell;
use std::convert::TryInto;
use std::env;

fn main() -> Result<(), SourceError> {
    let sess = Session::new();
    let clang = Clang::new().unwrap();
    let index = clang::Index::new(&clang, false, true);
    let filename = env::args().nth(1).expect("Usage: cargo run <cc_file>");
    let tu = configure(index.parser(filename)).parse()?;
    LowerCtx::new(&sess, &tu).gen()?;
    Ok(())
}

pub(crate) fn configure(mut parser: Parser<'_>) -> Parser<'_> {
    parser.skip_function_bodies(true).arguments(&[
        "-std=c++17",
        "-isysroot",
        "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk",
    ]);
    parser
}

enum Export<'tu> {
    Decl(Entity<'tu>),
    Type(Type<'tu>),
    TemplateType(Entity<'tu>),
}

struct Session {
    // TODO: opts
    diags: DiagnosticsCtx<String>,
}

impl Session {
    fn new() -> Self {
        Session {
            diags: DiagnosticsCtx::new(),
        }
    }
}

impl<'tu> diagnostics::File<String> for File<'tu> {
    fn name(&self) -> String {
        self.get_path()
            .as_path()
            .to_str()
            .expect("path was not valid UTF-8")
            .into()
    }

    fn contents(&self) -> String {
        self.get_contents().unwrap()
    }
}

struct LowerCtx<'tu> {
    sess: &'tu Session,
    tu: &'tu TranslationUnit<'tu>,
    source_map: RefCell<SourceFileMap<File<'tu>, String>>,
    //visible: Vec<(Path, Entity<'tu>)>,
}

impl<'tu> LowerCtx<'tu> {
    fn new(sess: &'tu Session, tu: &'tu TranslationUnit<'tu>) -> Self {
        LowerCtx {
            sess,
            tu,
            source_map: RefCell::new(SourceFileMap::<File<'tu>, String>::new(&sess.diags)),
            //visible: vec![],
        }
    }

    fn gen(&mut self) -> Result<(), SourceError> {
        //let mut visitor = AstVisitor::new(&self);
        let mut exports = vec![];
        for ent in self.tu.get_entity().get_children() {
            if let EntityKind::Namespace = ent.get_kind() {
                if let Some("rust_export") = ent.get_name().as_deref() {
                    self.handle_rust_export(ent, &mut exports);
                }
            }
        }

        let mut mdl = ir::Module::new();
        for (name, export) in exports {
            match export {
                Export::Decl(decl_ref) => self.lower_decl(name, decl_ref, &mut mdl),
                Export::Type(ty) => {
                    println!("{} = {:?}", name, ty);
                    println!(
                        "  {:?}",
                        ty.get_elaborated_type()
                            .unwrap() // TODO hack
                            .get_template_argument_types()
                    );
                }
                Export::TemplateType(t) => {
                    println!("{} = {:?}", name, t);
                    for child in t.get_children() {
                        match child.get_kind() {
                            EntityKind::TemplateTypeParameter => {
                                println!("  type parameter {}", child.get_name().unwrap())
                            }
                            EntityKind::TypeAliasDecl => println!(
                                "  type alias => {:?} => {:?}",
                                child.get_typedef_underlying_type().unwrap(),
                                child
                                    .get_typedef_underlying_type()
                                    .unwrap()
                                    .get_declaration(),
                            ),
                            _ => println!("  unknown child {:?}", child),
                        }
                    }
                }
            }
        }

        mdl.check();
        codegen::perform_codegen(&mdl);

        Ok(())
    }

    fn handle_rust_export(&self, ns: Entity<'tu>, exports: &mut Vec<(Path, Export<'tu>)>) {
        for decl in ns.get_children() {
            println!("{:?}", decl);
            let name = Path::from(decl.get_name().unwrap());
            match decl.get_kind() {
                EntityKind::UsingDeclaration => {
                    exports.push((name, Export::Decl(decl.get_reference().unwrap())))
                }
                EntityKind::TypeAliasDecl => exports.push((
                    name,
                    Export::Type(decl.get_typedef_underlying_type().unwrap()),
                )),
                EntityKind::TypeAliasTemplateDecl => {
                    exports.push((name, Export::TemplateType(decl)))
                }
                _ => panic!(
                    "Only using declarations are permitted inside rust_export:\n{}",
                    decl.get_pretty_printer().print()
                ),
            }
        }
    }

    fn lower_decl(&self, name: Path, decl_ref: Entity<'tu>, mdl: &mut ir::Module) {
        let overloads = decl_ref.get_overloaded_declarations().unwrap();
        assert_eq!(overloads.len(), 1);
        let ent = overloads[0];

        println!("{} = {:?}", name, ent);
        for child in ent.get_children() {
            println!("  {}: {:?}", child.display_name(), child.get_kind());
        }

        match ent.get_kind() {
            EntityKind::StructDecl => self.lower_struct(name, ent, mdl),
            other => eprintln!("{}: Unsupported type {:?}", name, other),
        }
    }

    fn lower_struct(&self, name: Path, ent: Entity<'tu>, mdl: &mut ir::Module) {
        let ty = ent.get_type().unwrap();
        if !ty.is_pod() {
            self.sess
                .diags
                .error(
                    "unsupported type",
                    self.span(ent).label("only POD structs are supported"),
                )
                .emit();
            return;
        }

        let mut fields = vec![];
        let mut offsets = vec![];

        // TODO get rid of unwrap()
        for field in ty.get_fields().unwrap() {
            if let Some(acc) = field.get_accessibility() {
                if Accessibility::Public != acc {
                    continue;
                }
            }
            let field_name = field.get_name().unwrap();
            let field_ty = field.get_type().unwrap();
            fields.push(Field {
                name: Ident::from(field_name),
                ty: field_ty.lower(),
            });
            let offset = field.get_offset_of_field().unwrap().try_into().unwrap();
            offsets.push(offset);
        }
        let size = ty.get_alignof().unwrap().try_into().unwrap();
        let align = ty.get_alignof().unwrap().try_into().unwrap();

        let lowered = ir::Struct {
            name: name.clone(),
            fields,
            offsets,
            repr: ir::Repr::C,
            size: ir::Size::new(size).unwrap(),
            align: ir::Align::new(align).unwrap(),
        };
        mdl.structs.push(lowered);
    }

    fn span(&self, ent: Entity<'tu>) -> Span {
        self.maybe_span_from_range(ent.get_range())
            .expect("TODO dummy span")
    }

    fn maybe_span_from_range(&self, range: Option<SourceRange<'tu>>) -> Option<Span> {
        let range = match range {
            Some(range) => range,
            None => return None,
        };
        let (start, end) = (
            range.get_start().get_file_location(),
            range.get_end().get_file_location(),
        );
        let file = match (start.file, end.file) {
            (Some(f), Some(g)) if f == g => f,
            _ => return None,
        };
        let file_id = self.source_map.borrow_mut().lookup(&file);
        Some(Span::new(file_id, start.offset, end.offset))
    }
}

trait Lower {
    type Output;
    fn lower(&self) -> Self::Output;
}

impl<'tu> Lower for Type<'tu> {
    type Output = Ty;
    fn lower(&self) -> Ty {
        use TypeKind::*;
        match self.get_kind() {
            Int => Ty::Int,
            UInt => Ty::UInt,
            CharS => Ty::CharS,
            SChar => Ty::SChar,
            CharU => Ty::CharU,
            UChar => Ty::UChar,
            Float => Ty::Float,
            Double => Ty::Double,
            _ => panic!("unsupported type {:?}", self),
        }
    }
}
<commit_msg>Remove RefCell around source_map<commit_after>#[cfg(test)]
#[macro_use]
mod test_util;

#[macro_use]
mod util;

mod codegen;
mod diagnostics;
mod index;
mod ir;

use crate::diagnostics::{DiagnosticsCtx, SourceFileMap, Span};
use crate::index::{Ident, Path};
use crate::ir::*;
use crate::util::DisplayName;
use clang::{
    self,
    source::{File, SourceRange},
    Accessibility, Clang, Entity, EntityKind, Parser, SourceError, TranslationUnit, Type, TypeKind,
};
use std::convert::TryInto;
use std::env;

fn main() -> Result<(), SourceError> {
    let sess = Session::new();
    let clang = Clang::new().unwrap();
    let index = clang::Index::new(&clang, false, true);
    let filename = env::args().nth(1).expect("Usage: cargo run <cc_file>");
    let tu = configure(index.parser(filename)).parse()?;
    LowerCtx::new(&sess, &tu).gen()?;
    Ok(())
}

pub(crate) fn configure(mut parser: Parser<'_>) -> Parser<'_> {
    parser.skip_function_bodies(true).arguments(&[
        "-std=c++17",
        "-isysroot",
        "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk",
    ]);
    parser
}

enum Export<'tu> {
    Decl(Entity<'tu>),
    Type(Type<'tu>),
    TemplateType(Entity<'tu>),
}

struct Session {
    // TODO: opts
    diags: DiagnosticsCtx<String>,
}

impl Session {
    fn new() -> Self {
        Session {
            diags: DiagnosticsCtx::new(),
        }
    }
}

impl<'tu> diagnostics::File<String> for File<'tu> {
    fn name(&self) -> String {
        self.get_path()
            .as_path()
            .to_str()
            .expect("path was not valid UTF-8")
            .into()
    }

    fn contents(&self) -> String {
        self.get_contents().unwrap()
    }
}

struct LowerCtx<'tu> {
    sess: &'tu Session,
    tu: &'tu TranslationUnit<'tu>,
    source_map: SourceFileMap<File<'tu>, String>,
    //visible: Vec<(Path, Entity<'tu>)>,
}

impl<'tu> LowerCtx<'tu> {
    fn new(sess: &'tu Session, tu: &'tu TranslationUnit<'tu>) -> Self {
        LowerCtx {
            sess,
            tu,
            source_map: SourceFileMap::<File<'tu>, String>::new(&sess.diags),
            //visible: vec![],
        }
    }

    fn gen(&mut self) -> Result<(), SourceError> {
        //let mut visitor = AstVisitor::new(&self);
        let mut exports = vec![];
        for ent in self.tu.get_entity().get_children() {
            if let EntityKind::Namespace = ent.get_kind() {
                if let Some("rust_export") = ent.get_name().as_deref() {
                    self.handle_rust_export(ent, &mut exports);
                }
            }
        }

        let mut mdl = ir::Module::new();
        for (name, export) in exports {
            match export {
                Export::Decl(decl_ref) => self.lower_decl(name, decl_ref, &mut mdl),
                Export::Type(ty) => {
                    println!("{} = {:?}", name, ty);
                    println!(
                        "  {:?}",
                        ty.get_elaborated_type()
                            .unwrap() // TODO hack
                            .get_template_argument_types()
                    );
                }
                Export::TemplateType(t) => {
                    println!("{} = {:?}", name, t);
                    for child in t.get_children() {
                        match child.get_kind() {
                            EntityKind::TemplateTypeParameter => {
                                println!("  type parameter {}", child.get_name().unwrap())
                            }
                            EntityKind::TypeAliasDecl => println!(
                                "  type alias => {:?} => {:?}",
                                child.get_typedef_underlying_type().unwrap(),
                                child
                                    .get_typedef_underlying_type()
                                    .unwrap()
                                    .get_declaration(),
                            ),
                            _ => println!("  unknown child {:?}", child),
                        }
                    }
                }
            }
        }

        mdl.check();
        codegen::perform_codegen(&mdl);

        Ok(())
    }

    fn handle_rust_export(&mut self, ns: Entity<'tu>, exports: &mut Vec<(Path, Export<'tu>)>) {
        for decl in ns.get_children() {
            println!("{:?}", decl);
            let name = Path::from(decl.get_name().unwrap());
            match decl.get_kind() {
                EntityKind::UsingDeclaration => {
                    exports.push((name, Export::Decl(decl.get_reference().unwrap())))
                }
                EntityKind::TypeAliasDecl => exports.push((
                    name,
                    Export::Type(decl.get_typedef_underlying_type().unwrap()),
                )),
                EntityKind::TypeAliasTemplateDecl => {
                    exports.push((name, Export::TemplateType(decl)))
                }
                _ => panic!(
                    "Only using declarations are permitted inside rust_export:\n{}",
                    decl.get_pretty_printer().print()
                ),
            }
        }
    }

    fn lower_decl(&mut self, name: Path, decl_ref: Entity<'tu>, mdl: &mut ir::Module) {
        let overloads = decl_ref.get_overloaded_declarations().unwrap();
        assert_eq!(overloads.len(), 1);
        let ent = overloads[0];

        println!("{} = {:?}", name, ent);
        for child in ent.get_children() {
            println!("  {}: {:?}", child.display_name(), child.get_kind());
        }

        match ent.get_kind() {
            EntityKind::StructDecl => self.lower_struct(name, ent, mdl),
            other => eprintln!("{}: Unsupported type {:?}", name, other),
        }
    }

    fn lower_struct(&mut self, name: Path, ent: Entity<'tu>, mdl: &mut ir::Module) {
        let ty = ent.get_type().unwrap();
        if !ty.is_pod() {
            self.sess
                .diags
                .error(
                    "unsupported type",
                    self.span(ent).label("only POD structs are supported"),
                )
                .emit();
            return;
        }

        let mut fields = vec![];
        let mut offsets = vec![];

        // TODO get rid of unwrap()
        for field in ty.get_fields().unwrap() {
            if let Some(acc) = field.get_accessibility() {
                if Accessibility::Public != acc {
                    continue;
                }
            }
            let field_name = field.get_name().unwrap();
            let field_ty = field.get_type().unwrap();
            fields.push(Field {
                name: Ident::from(field_name),
                ty: field_ty.lower(),
            });
            let offset = field.get_offset_of_field().unwrap().try_into().unwrap();
            offsets.push(offset);
        }
        let size = ty.get_alignof().unwrap().try_into().unwrap();
        let align = ty.get_alignof().unwrap().try_into().unwrap();

        let lowered = ir::Struct {
            name: name.clone(),
            fields,
            offsets,
            repr: ir::Repr::C,
            size: ir::Size::new(size).unwrap(),
            align: ir::Align::new(align).unwrap(),
        };
        mdl.structs.push(lowered);
    }

    fn span(&mut self, ent: Entity<'tu>) -> Span {
        self.maybe_span_from_range(ent.get_range())
            .expect("TODO dummy span")
    }

    fn maybe_span_from_range(&mut self, range: Option<SourceRange<'tu>>) -> Option<Span> {
        let range = match range {
            Some(range) => range,
            None => return None,
        };
        let (start, end) = (
            range.get_start().get_file_location(),
            range.get_end().get_file_location(),
        );
        let file = match (start.file, end.file) {
            (Some(f), Some(g)) if f == g => f,
            _ => return None,
        };
        let file_id = self.source_map.lookup(&file);
        Some(Span::new(file_id, start.offset, end.offset))
    }
}

trait Lower {
    type Output;
    fn lower(&self) -> Self::Output;
}

impl<'tu> Lower for Type<'tu> {
    type Output = Ty;
    fn lower(&self) -> Ty {
        use TypeKind::*;
        match self.get_kind() {
            Int => Ty::Int,
            UInt => Ty::UInt,
            CharS => Ty::CharS,
            SChar => Ty::SChar,
            CharU => Ty::CharU,
            UChar => Ty::UChar,
            Float => Ty::Float,
            Double => Ty::Double,
            _ => panic!("unsupported type {:?}", self),
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate nickel;
#[macro_use] extern crate lazy_static;

extern crate rustc_serialize;
extern crate multipart;
extern crate nickel_jwt_session;
extern crate time;

use nickel::{Nickel,
            Request,
            Response,
            MiddlewareResult,
            HttpRouter,
            StaticFilesHandler,
            QueryString,
            MediaType,
            FormBody};
use nickel::status::StatusCode;
use nickel::extensions::Redirect;

use nickel_jwt_session::*;

use std::fs::{File,OpenOptions,remove_file};
use std::path::Path;
use std::io::Read;
use std::thread;
use std::collections::HashMap;

use multipart::server::{Multipart, SaveResult};

use rustc_serialize::json;

mod db;
mod sync;
mod commands;
mod utils;

use db::{Db,VoteImageError};
use utils::save_image;

use std::sync::Mutex;
use std::cell::RefCell;

lazy_static! {
    pub static ref DB : Mutex<Db> = Mutex::new(Db::new());

    /// Использование лежит в utils
    pub static ref OUTF : Mutex<RefCell<File>> = Mutex::new(RefCell::new(OpenOptions::new().append(true).create(true).open("OUTPUT").unwrap()));
}

macro_rules! routes(
    { $serv:ident, $($method:ident $($path:expr),+ => $fun:ident),+ } => {
        {
            $($(
                    $serv.$method($path, $fun);
               )+)+
        }
     };
);

fn index_n_search<'a, D>(_request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    response.render("src/templates/index.html", &[0]) // Вот тут и ниже так надо, чтобы не пересобирать программу при изменении HTML
}

fn show<'a, D>(request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let mut data = HashMap::new();
    let id = request.param("id").unwrap().parse::<i32>().unwrap();
    let cont = DB.lock().unwrap().get_image(id).unwrap();
    data.insert("image", cont);
    response.render("src/templates/show.html", &data)
}

fn upload_image<'mw>(req: &mut Request, mut res: Response<'mw>) -> MiddlewareResult<'mw> {
    if let Some(username) = req.authorized_user() {
        if let Ok(mut multipart) = Multipart::from_request(req) {
            match multipart.save_all() {
                SaveResult::Full(entries) | SaveResult::Partial(entries, _)  => {
                    if let Some(savedfile) = entries.files.get("image") {
                        if let Some(ref filename) = savedfile.filename {
                            if let Some(tags) = entries.fields.get("tags") {
                                let tags = tags.split_whitespace().map(String::from).collect::<Vec<_>>();
                                let mut body = Vec::new();
                                let _ = File::open(&savedfile.path).unwrap().read_to_end(&mut body);
                                let name = DB.lock().unwrap().add_with_tags_name(&tags, filename.split('.').collect::<Vec<_>>()[1], &username).unwrap();

                                save_image(Path::new("assets/images"), &name, &body);

                                res.redirect("/")

                            } else { res.send("No tags found") }
                        } else { res.send("Can't get filename") }
                    } else { res.send("Can't load file") }
                },

                SaveResult::Error(e) =>  res.send(format!("Server could not handle multipart POST! {:?}", e))
            }
        } else {
            res.set(nickel::status::StatusCode::BadRequest);
            res.send("Not a multipart request")
        }
    } else {
        res.error(StatusCode::Forbidden, "Not logged in")
    }
}

fn more<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let offset = request.query().get("offset").unwrap().parse::<usize>().unwrap();

    let images = match request.query().get("q") {
        Some(x) =>  DB.lock().unwrap().by_tags(25, offset, &x.to_lowercase().split_whitespace().map(String::from).collect::<Vec<_>>()).unwrap(),
        None    =>  DB.lock().unwrap().get_images(25, offset).unwrap()
    };

    response.set(MediaType::Json);
    response.send(json::encode(&images).unwrap())
}

fn adduser<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let body = try_with!(response, request.form_body());
    if let (Some(login), Some(pass), Some(confirm_pass)) = (body.get("login"), body.get("password"),body.get("confirm_password")) {
        if pass == confirm_pass {
            if let Ok(res) = DB.lock().unwrap().add_user(login,pass) {
                if res {
                    response.set_jwt_user(login);
                    response.redirect("/")
                } else {
                    response.send("User already exists")
                }
            } else {
                response.error(StatusCode::InternalServerError, "Internal server error")
            }
        } else {
            response.send("Password and confirmation are not equeal")
        }
    } else {
        response.send("No data")
    }
}

fn login<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let body = try_with!(response, request.form_body());
    if let (Some(login), Some(pass)) = (body.get("login"), body.get("password")) {
        match DB.lock().unwrap().check_user(login, pass) {
            Ok(x) => match x {
                Some(x) => if x {
                    response.set_jwt_user(login);
                    response.redirect("/")
                } else {
                    response.send("Incorrent password")
                },
                _   => response.send("No such user")
            },
            Err(e) =>  panic!(e)
        }
    } else {
        response.send("No login/pass")
    }
}

fn user_status<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {

    #[derive(RustcEncodable)]
    struct UserStatus {
        logined: bool,
        name: Option<String>
    }

    response.set(MediaType::Json);

    let (logined,name) = match request.authorized_user() {
        Some(user)  => (true, Some(user)),
        None        => (false, None)
    };

    response.send(json::encode(&UserStatus{
        logined: logined,
        name: name
    }).unwrap())
}

fn delete<'a, D>(request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let id = request.param("id").unwrap().parse::<i32>().unwrap();
    let name = DB.lock().unwrap().delete_image(id).unwrap();
    remove_file(format!("assets/images/{}", name)).unwrap();
    remove_file(format!("assets/images/preview/{}", name)).unwrap();
    response.redirect("/")
}

fn vote_image<'a, D>(request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let query = request.query() as *const nickel::Query;
    let (id,vote) = unsafe { // Бяка, конечно, но query() почему-то берёт mutable reference и не даёт использовать другим
        let q = &*query;
        (q.get("id"),q.get("vote"))
    };

    if let (Some(id), Some(vote)) = (id,vote) {
        if let Some(name) = request.authorized_user() {
            if let (Ok(vote),Ok(id)) = (vote.parse::<bool>(),id.parse::<i32>()) {
                match DB.lock().unwrap().vote_image(&name, id, vote).unwrap() {
                    Ok(newv)                        => response.send(newv.to_string()),
                    Err(VoteImageError::Already)    => response.send("Already voted that"),
                    Err(VoteImageError::NoImage)    => response.send("No such image")
                }
            } else {
                response.send("Invalid data")
            }
        } else {
            response.send("Not logged in")
        }
    } else {
        response.send("No data")
    }
}

fn main() {
    let mut server = Nickel::new();

    server.utilize(StaticFilesHandler::new("assets"));
    server.utilize(SessionMiddleware::new(&time::now().to_timespec().sec.to_string()));

    routes!{server,
        get "/","/search" => index_n_search,
        get "/show/:id" => show,
        get "/more" => more,
        get "/user_status" => user_status,
        get "/delete/:id" => delete,
        get "/vote_image" => vote_image,

        post "/upload_image" => upload_image,
        post "/login" => login,
        post "/adduser" => adduser
    };

    thread::spawn(commands::main);

    let _server = server.listen("127.0.0.1:3000");
}
<commit_msg>Какая-никакая проверка на то, есть ли логин/пароль<commit_after>#[macro_use] extern crate nickel;
#[macro_use] extern crate lazy_static;

extern crate rustc_serialize;
extern crate multipart;
extern crate nickel_jwt_session;
extern crate time;

use nickel::{Nickel,
            Request,
            Response,
            MiddlewareResult,
            HttpRouter,
            StaticFilesHandler,
            QueryString,
            MediaType,
            FormBody};
use nickel::status::StatusCode;
use nickel::extensions::Redirect;

use nickel_jwt_session::*;

use std::fs::{File,OpenOptions,remove_file};
use std::path::Path;
use std::io::Read;
use std::thread;
use std::collections::HashMap;

use multipart::server::{Multipart, SaveResult};

use rustc_serialize::json;

mod db;
mod sync;
mod commands;
mod utils;

use db::{Db,VoteImageError};
use utils::save_image;

use std::sync::Mutex;
use std::cell::RefCell;

lazy_static! {
    pub static ref DB : Mutex<Db> = Mutex::new(Db::new());

    /// Использование лежит в utils
    pub static ref OUTF : Mutex<RefCell<File>> = Mutex::new(RefCell::new(OpenOptions::new().append(true).create(true).open("OUTPUT").unwrap()));
}

macro_rules! routes(
    { $serv:ident, $($method:ident $($path:expr),+ => $fun:ident),+ } => {
        {
            $($(
                    $serv.$method($path, $fun);
               )+)+
        }
     };
);

fn index_n_search<'a, D>(_request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    response.render("src/templates/index.html", &[0]) // Вот тут и ниже так надо, чтобы не пересобирать программу при изменении HTML
}

fn show<'a, D>(request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let mut data = HashMap::new();
    let id = request.param("id").unwrap().parse::<i32>().unwrap();
    let cont = DB.lock().unwrap().get_image(id).unwrap();
    data.insert("image", cont);
    response.render("src/templates/show.html", &data)
}

fn upload_image<'mw>(req: &mut Request, mut res: Response<'mw>) -> MiddlewareResult<'mw> {
    if let Some(username) = req.authorized_user() {
        if let Ok(mut multipart) = Multipart::from_request(req) {
            match multipart.save_all() {
                SaveResult::Full(entries) | SaveResult::Partial(entries, _)  => {
                    if let Some(savedfile) = entries.files.get("image") {
                        if let Some(ref filename) = savedfile.filename {
                            if let Some(tags) = entries.fields.get("tags") {
                                let tags = tags.split_whitespace().map(String::from).collect::<Vec<_>>();
                                let mut body = Vec::new();
                                let _ = File::open(&savedfile.path).unwrap().read_to_end(&mut body);
                                let name = DB.lock().unwrap().add_with_tags_name(&tags, filename.split('.').collect::<Vec<_>>()[1], &username).unwrap();

                                save_image(Path::new("assets/images"), &name, &body);

                                res.redirect("/")

                            } else { res.send("No tags found") }
                        } else { res.send("Can't get filename") }
                    } else { res.send("Can't load file") }
                },

                SaveResult::Error(e) =>  res.send(format!("Server could not handle multipart POST! {:?}", e))
            }
        } else {
            res.set(nickel::status::StatusCode::BadRequest);
            res.send("Not a multipart request")
        }
    } else {
        res.error(StatusCode::Forbidden, "Not logged in")
    }
}

fn more<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let offset = request.query().get("offset").unwrap().parse::<usize>().unwrap();

    let images = match request.query().get("q") {
        Some(x) =>  DB.lock().unwrap().by_tags(25, offset, &x.to_lowercase().split_whitespace().map(String::from).collect::<Vec<_>>()).unwrap(),
        None    =>  DB.lock().unwrap().get_images(25, offset).unwrap()
    };

    response.set(MediaType::Json);
    response.send(json::encode(&images).unwrap())
}

fn adduser<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let body = try_with!(response, request.form_body());
    if let (Some(login), Some(pass), Some(confirm_pass)) = (body.get("login"), body.get("password"),body.get("confirm_password")) {
        if pass == confirm_pass {
            if !pass.trim().is_empty() && !login.trim().is_empty() {
                if let Ok(res) = DB.lock().unwrap().add_user(login,pass) {
                    if res {
                        response.set_jwt_user(login);
                        response.redirect("/")
                    } else {
                        response.send("User already exists")
                    }
                } else {
                    response.error(StatusCode::InternalServerError, "Internal server error")
                }
            } else {
                response.send("Empty login/pass")
            }
        } else {
            response.send("Password and confirmation are not equeal")
        }
    } else {
        response.send("No data")
    }
}

fn login<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let body = try_with!(response, request.form_body());
    if let (Some(login), Some(pass)) = (body.get("login"), body.get("password")) {
        match DB.lock().unwrap().check_user(login, pass) {
            Ok(x) => match x {
                Some(x) => if x {
                    response.set_jwt_user(login);
                    response.redirect("/")
                } else {
                    response.send("Incorrent password")
                },
                _   => response.send("No such user")
            },
            Err(e) =>  panic!(e)
        }
    } else {
        response.send("No login/pass")
    }
}

fn user_status<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {

    #[derive(RustcEncodable)]
    struct UserStatus {
        logined: bool,
        name: Option<String>
    }

    response.set(MediaType::Json);

    let (logined,name) = match request.authorized_user() {
        Some(user)  => (true, Some(user)),
        None        => (false, None)
    };

    response.send(json::encode(&UserStatus{
        logined: logined,
        name: name
    }).unwrap())
}

fn delete<'a, D>(request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let id = request.param("id").unwrap().parse::<i32>().unwrap();
    let name = DB.lock().unwrap().delete_image(id).unwrap();
    remove_file(format!("assets/images/{}", name)).unwrap();
    remove_file(format!("assets/images/preview/{}", name)).unwrap();
    response.redirect("/")
}

fn vote_image<'a, D>(request: &mut Request<D>, response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let query = request.query() as *const nickel::Query;
    let (id,vote) = unsafe { // Бяка, конечно, но query() почему-то берёт mutable reference и не даёт использовать другим
        let q = &*query;
        (q.get("id"),q.get("vote"))
    };

    if let (Some(id), Some(vote)) = (id,vote) {
        if let Some(name) = request.authorized_user() {
            if let (Ok(vote),Ok(id)) = (vote.parse::<bool>(),id.parse::<i32>()) {
                match DB.lock().unwrap().vote_image(&name, id, vote).unwrap() {
                    Ok(newv)                        => response.send(newv.to_string()),
                    Err(VoteImageError::Already)    => response.send("Already voted that"),
                    Err(VoteImageError::NoImage)    => response.send("No such image")
                }
            } else {
                response.send("Invalid data")
            }
        } else {
            response.send("Not logged in")
        }
    } else {
        response.send("No data")
    }
}

fn main() {
    let mut server = Nickel::new();

    server.utilize(StaticFilesHandler::new("assets"));
    server.utilize(SessionMiddleware::new(&time::now().to_timespec().sec.to_string()));

    routes!{server,
        get "/","/search" => index_n_search,
        get "/show/:id" => show,
        get "/more" => more,
        get "/user_status" => user_status,
        get "/delete/:id" => delete,
        get "/vote_image" => vote_image,

        post "/upload_image" => upload_image,
        post "/login" => login,
        post "/adduser" => adduser
    };

    thread::spawn(commands::main);

    let _server = server.listen("127.0.0.1:3000");
}
<|endoftext|>


<commit_before>mod vm;
mod command;
mod parse;
mod program;

use vm::VM;
use program::Program;
use std::io::Write;
use std::env;

fn main() {
    for argument in env::args() {
        println!("{}", argument);
    }

    let vm = VM::new();
    let program = Program::new();
    repl(vm, program);
}

fn repl (mut vm: VM, mut program: Program) -> () {
    loop {
        print!("{}", if program.is_seeking {"... "} else {"bf> "});
        std::io::stdout().flush().unwrap();

        let mut line = String::new();
        let commands = match std::io::stdin().read_line(&mut line) {
            Ok(0) => break,
            Ok(_) => parse::parse(&line),
            Err(error) => panic!(error),
        };
        program.append(&commands);
        program.execute(&mut vm);
    }
}
<commit_msg>Only trigger repl if no arguments are supplied<commit_after>mod vm;
mod command;
mod parse;
mod program;

use vm::VM;
use program::Program;
use std::io::Write;
use std::env;

fn main() {
    for argument in env::args() {
        println!("{}", argument);
    }

    let vm = VM::new();
    let program = Program::new();
    match env::args().nth(1) {
        None => repl(vm, program),
        Some(_arg) => {}
    }
}

fn repl (mut vm: VM, mut program: Program) -> () {
    loop {
        print!("{}", if program.is_seeking {"... "} else {"bf> "});
        std::io::stdout().flush().unwrap();

        let mut line = String::new();
        let commands = match std::io::stdin().read_line(&mut line) {
            Ok(0) => break,
            Ok(_) => parse::parse(&line),
            Err(error) => panic!(error),
        };
        program.append(&commands);
        program.execute(&mut vm);
    }
}
<|endoftext|>


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

/* TODO:
 *  * redirect root url
 *  * add watermark? url to repo?
 *  * specify root node
 *  * specify node labels
 *  * update git repo?
 *  * serverside caching? redis?
 */

use std::collections::{HashMap, HashSet};
use std::env;
use std::io::{Write, Read};
use std::process::{Command, Stdio};

use tiny_http::{Header, Response};

extern crate crates_index;
extern crate tiny_http;

static INDEX_LOCAL_PATH: &'static str = "crates.io-index";

fn build_dot(crate_name: &str, dep_map: &HashMap<String, Vec<String>>) -> Vec<u8> {
    let mut crate_names = vec![crate_name];

    let mut dot = String::new();
    dot.push_str("digraph graphname {");
    dot.push_str("ratio=0.75;");

    // Which dependencies we've already seen
    let mut seen_set = HashSet::new();

    while let Some(crate_name) = crate_names.pop() {
        if seen_set.contains(crate_name as &str) {
            continue;
        }
        seen_set.insert(crate_name);
        for crate_dep in dep_map.get(crate_name).unwrap() {
            dot.push_str(&format!("\"{}\" -> \"{}\";", crate_name, crate_dep));
            if !seen_set.contains(crate_dep as &str) {
                crate_names.push(crate_dep);
            }
        }
    }
    dot.push_str("}");

    let child = Command::new("dot").arg("-Tpng").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn().unwrap();
    child.stdin.unwrap().write_all(dot.as_bytes()).unwrap();

    let mut ret = vec![];
    child.stdout.unwrap().read_to_end(&mut ret).unwrap();
    ret
}

fn main() {
    let index = crates_index::CratesIndex::new(INDEX_LOCAL_PATH.into());
    if !index.exists() {
        println!("Cloning crates.io-index");
        index.clone_index();
    }

    let dep_map = index.dependency_map();

    let port = match env::var("PORT") {
        Ok(p) => p.parse::<u16>().unwrap(),
        Err(..) => 8000,
    };

    let server = tiny_http::ServerBuilder::new().with_port(port).build().unwrap();

    println!("Server listening on port {}", port);
    for req in server.incoming_requests() {
        let response = {
            let crate_name = req.url().trim_left_matches("/");
            if dep_map.get(crate_name).is_some() {
                let data = build_dot(crate_name, &dep_map);
                let content_type_header = "Content-Type: image/png".parse::<Header>().unwrap();
                let cache_control_header = "cache-control: no-cache".parse::<Header>().unwrap();
                Response::from_data(data).with_header(content_type_header)
                                         .with_header(cache_control_header)
            } else {
                Response::from_string("could not find crate").with_status_code(400)
            }
        };
        req.respond(response);
    }
}
<commit_msg>Remove completed TODO<commit_after>// Copyright 2015 Corey Farwell
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/* TODO:
 *  * redirect root url
 *  * add watermark? url to repo?
 *  * specify root node
 *  * update git repo?
 *  * serverside caching? redis?
 */

use std::collections::{HashMap, HashSet};
use std::env;
use std::io::{Write, Read};
use std::process::{Command, Stdio};

use tiny_http::{Header, Response};

extern crate crates_index;
extern crate tiny_http;

static INDEX_LOCAL_PATH: &'static str = "crates.io-index";

fn build_dot(crate_name: &str, dep_map: &HashMap<String, Vec<String>>) -> Vec<u8> {
    let mut crate_names = vec![crate_name];

    let mut dot = String::new();
    dot.push_str("digraph graphname {");
    dot.push_str("ratio=0.75;");

    // Which dependencies we've already seen
    let mut seen_set = HashSet::new();

    while let Some(crate_name) = crate_names.pop() {
        if seen_set.contains(crate_name as &str) {
            continue;
        }
        seen_set.insert(crate_name);
        for crate_dep in dep_map.get(crate_name).unwrap() {
            dot.push_str(&format!("\"{}\" -> \"{}\";", crate_name, crate_dep));
            if !seen_set.contains(crate_dep as &str) {
                crate_names.push(crate_dep);
            }
        }
    }
    dot.push_str("}");

    let child = Command::new("dot").arg("-Tpng").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn().unwrap();
    child.stdin.unwrap().write_all(dot.as_bytes()).unwrap();

    let mut ret = vec![];
    child.stdout.unwrap().read_to_end(&mut ret).unwrap();
    ret
}

fn main() {
    let index = crates_index::CratesIndex::new(INDEX_LOCAL_PATH.into());
    if !index.exists() {
        println!("Cloning crates.io-index");
        index.clone_index();
    }

    let dep_map = index.dependency_map();

    let port = match env::var("PORT") {
        Ok(p) => p.parse::<u16>().unwrap(),
        Err(..) => 8000,
    };

    let server = tiny_http::ServerBuilder::new().with_port(port).build().unwrap();

    println!("Server listening on port {}", port);
    for req in server.incoming_requests() {
        let response = {
            let crate_name = req.url().trim_left_matches("/");
            if dep_map.get(crate_name).is_some() {
                let data = build_dot(crate_name, &dep_map);
                let content_type_header = "Content-Type: image/png".parse::<Header>().unwrap();
                let cache_control_header = "cache-control: no-cache".parse::<Header>().unwrap();
                Response::from_data(data).with_header(content_type_header)
                                         .with_header(cache_control_header)
            } else {
                Response::from_string("could not find crate").with_status_code(400)
            }
        };
        req.respond(response);
    }
}
<|endoftext|>


<commit_before>extern crate ansi_term;
extern crate getopts;
extern crate isatty;
extern crate regex;
extern crate walkdir;

use std::env;
use std::error::Error;
use std::ffi::OsStr;
use std::io::Write;
use std::path::{Path, Component};
use std::process;

use ansi_term::Colour;
use getopts::Options;
use isatty::stdout_isatty;
use regex::{Regex, RegexBuilder};
use walkdir::{WalkDir, DirEntry, WalkDirIterator};

/// Configuration options for *fd*.
struct FdOptions {
    case_sensitive: bool,
    search_full_path: bool,
    search_hidden: bool,
    follow_links: bool,
    colored: bool,
    max_depth: usize
}

/// The default maximum recursion depth.
const MAX_DEPTH_DEFAULT : usize = 25;

/// Print a search result to the console.
fn print_entry(path_root: &Path, path_entry: &Path, config: &FdOptions) {
    let path_full = path_root.join(path_entry);

    let path_str = match path_entry.to_str() {
        Some(p) => p,
        None    => return
    };

    if !config.colored {
        println!("{}", path_str);
    } else {
        let mut component_path = path_root.to_path_buf();

        // Traverse the path and colorize each component
        for component in path_entry.components() {
            let comp_str = match component {
                Component::Normal(p) => p,
                _                    => error("Unexpected path component")
            };

            component_path.push(Path::new(comp_str));

            let style =
                if component_path.symlink_metadata()
                                 .map(|md| md.file_type().is_symlink())
                                 .unwrap_or(false) {
                    Colour::Purple
                } else if component_path.is_dir() {
                    Colour::Cyan
                } else {
                    Colour::White
                };

            print!("{}", style.paint(comp_str.to_str().unwrap()));

            if component_path.is_dir() && component_path != path_full {
                let sep = std::path::MAIN_SEPARATOR.to_string();
                print!("{}", style.paint(sep));
            }
        }
        print!("\n");
    }
}

/// Check if filename of entry starts with a dot.
fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("."))
         .unwrap_or(false)
}

/// Recursively scan the given root path and search for files / pathnames
/// matching the pattern.
fn scan(root: &Path, pattern: &Regex, config: &FdOptions) {
    let walker = WalkDir::new(root)
                     .follow_links(config.follow_links)
                     .max_depth(config.max_depth)
                     .into_iter()
                     .filter_entry(|e| config.search_hidden || !is_hidden(e))
                     .filter_map(|e| e.ok())
                     .filter(|e| e.path() != root);

    for entry in walker {
        let path_rel = match entry.path().strip_prefix(root) {
            Ok(p) => p,
            Err(_) => continue
        };

        let search_str =
            if config.search_full_path {
                path_rel.to_str()
            } else {
                if !path_rel.is_file() { continue }

                path_rel.file_name()
                        .and_then(OsStr::to_str)
            };

        search_str.and_then(|s| pattern.find(s))
                  .map(|_| print_entry(&root, path_rel, &config));
    }
}

/// Print error message to stderr and exit with status `1`.
fn error(message: &str) -> ! {
    writeln!(&mut std::io::stderr(), "{}", message)
        .expect("Failed writing to stderr");
    process::exit(1);
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help message");
    opts.optflag("s", "sensitive",
                      "case-sensitive search (default: smart case)");
    opts.optflag("f", "filename",
                      "search filenames only (default: full path)");
    opts.optflag("H", "hidden",
                      "search hidden files/directories (default: off)");
    opts.optflag("F", "follow", "follow symlinks (default: off)");
    opts.optflag("n", "no-color", "do not colorize output (default: on)");
    opts.optopt("d", "max-depth",
                     format!("maximum search depth (default: {})",
                             MAX_DEPTH_DEFAULT).as_str(),
                     "D");

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(e) => error(e.description())
    };

    if matches.opt_present("h") {
        let brief = "Usage: fd [options] [PATTERN]";
        print!("{}", opts.usage(&brief));
        process::exit(1);
    }

    let empty = String::new();
    let pattern = matches.free.get(0).unwrap_or(&empty);

    let current_dir_buf = match env::current_dir() {
        Ok(cd) => cd,
        Err(_) => error("Could not get current directory!")
    };
    let current_dir = current_dir_buf.as_path();

    let config = FdOptions {
        // The search will be case-sensitive if the command line flag is set or
        // if the pattern has an uppercase character (smart case).
        case_sensitive:    matches.opt_present("sensitive") ||
                           pattern.chars().any(char::is_uppercase),
        search_full_path: !matches.opt_present("filename"),
        search_hidden:     matches.opt_present("hidden"),
        colored:          !matches.opt_present("no-color") &&
                           stdout_isatty(),
        follow_links:      matches.opt_present("follow"),
        max_depth:
            matches.opt_str("max-depth")
                   .and_then(|ds| usize::from_str_radix(&ds, 10).ok())
                   .unwrap_or(MAX_DEPTH_DEFAULT)
    };

    match RegexBuilder::new(pattern)
              .case_insensitive(!config.case_sensitive)
              .build() {
        Ok(re)   => scan(&current_dir, &re, &config),
        Err(err) => error(err.description())
    }
}
<commit_msg>Parse dircolors files, closes #20<commit_after>extern crate ansi_term;
extern crate getopts;
extern crate isatty;
extern crate regex;
extern crate walkdir;

use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::ffi::OsStr;
use std::fs::File;
use std::io::{Write, BufReader,BufRead};
use std::path::{Path, Component};
use std::process;

use ansi_term::Colour;
use getopts::Options;
use isatty::stdout_isatty;
use regex::{Regex, RegexBuilder};
use walkdir::{WalkDir, DirEntry, WalkDirIterator};

/// Maps file extensions to ANSI colors / styles.
type ExtensionStyles = HashMap<String, ansi_term::Style>;

/// Configuration options for *fd*.
struct FdOptions {
    case_sensitive: bool,
    search_full_path: bool,
    search_hidden: bool,
    follow_links: bool,
    colored: bool,
    max_depth: usize,
    extension_styles: Option<ExtensionStyles>
}

/// The default maximum recursion depth.
const MAX_DEPTH_DEFAULT : usize = 25;

/// Print a search result to the console.
fn print_entry(path_root: &Path, path_entry: &Path, config: &FdOptions) {
    let path_full = path_root.join(path_entry);

    let path_str = match path_entry.to_str() {
        Some(p) => p,
        None    => return
    };

    if !config.colored {
        println!("{}", path_str);
    } else {
        let mut component_path = path_root.to_path_buf();

        // Traverse the path and colorize each component
        for component in path_entry.components() {
            let comp_str = match component {
                Component::Normal(p) => p,
                _                    => error("Unexpected path component")
            };

            component_path.push(Path::new(comp_str));

            let style =
                if component_path.symlink_metadata()
                                 .map(|md| md.file_type().is_symlink())
                                 .unwrap_or(false) {
                    Colour::Cyan.normal()
                } else if component_path.is_dir() {
                    Colour::Blue.bold()
                } else {
                    // Loop up file extension
                    if let Some(ref ext_styles) = config.extension_styles {
                        component_path.extension()
                                      .and_then(|e| e.to_str())
                                      .and_then(|e| ext_styles.get(e))
                                      .map(|r| r.clone())
                                      .unwrap_or(Colour::White.normal())
                    }
                    else {
                        Colour::White.normal()
                    }
                };

            print!("{}", style.paint(comp_str.to_str().unwrap()));

            if component_path.is_dir() && component_path != path_full {
                let sep = std::path::MAIN_SEPARATOR.to_string();
                print!("{}", style.paint(sep));
            }
        }
        println!();
    }
}

/// Check if filename of entry starts with a dot.
fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("."))
         .unwrap_or(false)
}

/// Recursively scan the given root path and search for files / pathnames
/// matching the pattern.
fn scan(root: &Path, pattern: &Regex, config: &FdOptions) {
    let walker = WalkDir::new(root)
                     .follow_links(config.follow_links)
                     .max_depth(config.max_depth)
                     .into_iter()
                     .filter_entry(|e| config.search_hidden || !is_hidden(e))
                     .filter_map(|e| e.ok())
                     .filter(|e| e.path() != root);

    for entry in walker {
        let path_rel = match entry.path().strip_prefix(root) {
            Ok(p) => p,
            Err(_) => continue
        };

        let search_str =
            if config.search_full_path {
                path_rel.to_str()
            } else {
                if !path_rel.is_file() { continue }

                path_rel.file_name()
                        .and_then(OsStr::to_str)
            };

        search_str.and_then(|s| pattern.find(s))
                  .map(|_| print_entry(&root, path_rel, &config));
    }
}

/// Print error message to stderr and exit with status `1`.
fn error(message: &str) -> ! {
    writeln!(&mut std::io::stderr(), "{}", message)
        .expect("Failed writing to stderr");
    process::exit(1);
}

/// Parse `dircolors` file.
fn parse_dircolors(path: &Path) -> std::io::Result<ExtensionStyles> {
    let file = File::open(path)?;
    let mut ext_styles = HashMap::new();

    let pattern =
        Regex::new(r"^\.([A-Za-z0-9]+)\s*38;5;([0-9]+)\b").unwrap();

    for line in BufReader::new(file).lines() {
        if let Some(caps) = pattern.captures(line.unwrap().as_str()) {
            if let Some(ext) = caps.get(1).map(|m| m.as_str()) {
                let fg = caps.get(2)
                             .map(|m| m.as_str())
                             .and_then(|n| u8::from_str_radix(n, 10).ok())
                             .unwrap_or(7); // white
                ext_styles.insert(String::from(ext),
                                  Colour::Fixed(fg).normal());
            }
        }
    }
    Ok(ext_styles)
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help message");
    opts.optflag("s", "sensitive",
                      "case-sensitive search (default: smart case)");
    opts.optflag("f", "filename",
                      "search filenames only (default: full path)");
    opts.optflag("H", "hidden",
                      "search hidden files/directories (default: off)");
    opts.optflag("F", "follow", "follow symlinks (default: off)");
    opts.optflag("n", "no-color", "do not colorize output (default: on)");
    opts.optopt("d", "max-depth",
                     format!("maximum search depth (default: {})",
                             MAX_DEPTH_DEFAULT).as_str(),
                     "D");

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(e) => error(e.description())
    };

    if matches.opt_present("h") {
        let brief = "Usage: fd [options] [PATTERN]";
        print!("{}", opts.usage(&brief));
        process::exit(1);
    }

    let empty = String::new();
    let pattern = matches.free.get(0).unwrap_or(&empty);

    let current_dir_buf = match env::current_dir() {
        Ok(cd) => cd,
        Err(_) => error("Could not get current directory!")
    };
    let current_dir = current_dir_buf.as_path();

    let ext_styles = env::home_dir()
                         .map(|h| h.join(".dir_colors"))
                         .and_then(|path| parse_dircolors(&path).ok());

    let config = FdOptions {
        // The search will be case-sensitive if the command line flag is set or
        // if the pattern has an uppercase character (smart case).
        case_sensitive:    matches.opt_present("sensitive") ||
                           pattern.chars().any(char::is_uppercase),
        search_full_path: !matches.opt_present("filename"),
        search_hidden:     matches.opt_present("hidden"),
        colored:          !matches.opt_present("no-color") &&
                           stdout_isatty(),
        follow_links:      matches.opt_present("follow"),
        max_depth:
            matches.opt_str("max-depth")
                   .and_then(|ds| usize::from_str_radix(&ds, 10).ok())
                   .unwrap_or(MAX_DEPTH_DEFAULT),
        extension_styles:  ext_styles
    };

    match RegexBuilder::new(pattern)
              .case_insensitive(!config.case_sensitive)
              .build() {
        Ok(re)   => scan(&current_dir, &re, &config),
        Err(err) => error(err.description())
    }
}
<|endoftext|>


<commit_before>extern crate hyper;

mod get;
mod post;

use hyper::{Get, Post};
use hyper::header;
use hyper::server::{Handler, Server, Request, Response};
use hyper::status::StatusCode;

struct HttpTin {
    server: String,
}

impl HttpTin {
    pub fn new() -> Self {
        let server = format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
        HttpTin { server: server }
    }

    fn prepare_response(&self, response: &mut Response) {
        let server = header::Server(self.server.clone());
        response.headers_mut().set(server);
    }
}

impl Handler for HttpTin {
    fn handle(&self, request: Request, mut response: Response) {
        println!("{} {} {}", request.remote_addr, request.method, request.uri);
        self.prepare_response(&mut response);
        match request.method {
            Get => get::get(request, response),
            Post => post::post(request, response),
             _ => *response.status_mut() = StatusCode::MethodNotAllowed,
        }
    }
}

fn main() {
    let server = Server::http("::1:8000").unwrap();
    // println!("Server {:?}", server);
    let active = server.handle(HttpTin::new()).unwrap();
    println!("Active {:?}", active.socket);
}
<commit_msg>rustfmt<commit_after>extern crate hyper;

mod get;
mod post;

use hyper::{Get, Post};
use hyper::header;
use hyper::server::{Handler, Server, Request, Response};
use hyper::status::StatusCode;

struct HttpTin {
    server: String,
}

impl HttpTin {
    pub fn new() -> Self {
        let server = format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
        HttpTin { server: server }
    }

    fn prepare_response(&self, response: &mut Response) {
        let server = header::Server(self.server.clone());
        response.headers_mut().set(server);
    }
}

impl Handler for HttpTin {
    fn handle(&self, request: Request, mut response: Response) {
        println!("{} {} {}", request.remote_addr, request.method, request.uri);
        self.prepare_response(&mut response);
        match request.method {
            Get => get::get(request, response),
            Post => post::post(request, response),
            _ => *response.status_mut() = StatusCode::MethodNotAllowed,
        }
    }
}

fn main() {
    let server = Server::http("::1:8000").unwrap();
    // println!("Server {:?}", server);
    let active = server.handle(HttpTin::new()).unwrap();
    println!("Active {:?}", active.socket);
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::mpsc;

fn main() {

    let (sender, receiver) = mpsc::channel();

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

        sender.send(1).unwrap();
    });

    thread_1.join();

    println!("main thread receive {:?}", receiver.recv().unwrap());
}<commit_msg>test: 提交修改<commit_after>fn main() {

    let mut a = Box::new("abc");

    println!("main before abc run a is: {}", a);

    let b = abc(&mut a);

    println!("main after abc run a is {}", a);

    println!("main after abc run b is {}", b);
}

fn abc(a: &mut Box<&str>) -> &'static str {

    let temp: &str = get();

    *a = Box::new("abc changed");

    print_temp(temp);

    temp
}

fn get() -> &'static str {

    "xyz"
}

fn print_temp(a: &str) {

    println!("temp is: {}", a);
}<|endoftext|>


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

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex;
extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;
use regex::Regex;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            let output = find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
            make_output(output);
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {let (old, new) = (&pair[1], &pair[0]);
			let output = find_moves(&repo, old.clone(), new.clone()).unwrap();
			// mangle the vectors here to make pretty output
			make_output(output);
		}
	}
}

fn make_output(output: Vec<Output>) {
	println!("make_output:");
    println!("\toutput.len()={}", output.len());
    for i in range(0, output.len()) {
            println!("\told_commit={}",         output[i].old_commit);
            println!("\tnew_commit={}",         output[i].new_commit);
            println!("\torigin_line={}",        output[i].origin_line);
            println!("\tdestintation_line={}",  output[i].destination_line);
            println!("\tnum_lines={}",          output[i].num_lines);
    }
}

#[derive(Debug)]
enum State {
    Other, Addition, Deletion
}

#[derive(Debug)]
enum FoundState {
    Added, Deleted
}

fn dump_diffdelta(delta: &DiffDelta) {
    println!("delta: nfiles={} status={:?} old_file=(id={} path_bytes={:?} path={:?} tsize={}) new_file=(id={} path_bytes={:?} path={:?} tsize={})",
            delta.nfiles(), delta.status(),
            delta.old_file().id(), delta.old_file().path_bytes(), delta.old_file().path(), delta.old_file().size(),
            delta.new_file().id(), delta.new_file().path_bytes(), delta.new_file().path(), delta.new_file().size());
}

fn format_key(key: String) -> String {
    let removeWhiteSpace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    removeWhiteSpace.replace_all(key.as_slice(), "")
}

#[derive(Debug)]
struct Found {
    filename: Path,
    key: String,
    state: FoundState,
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {

    println!("\nFIND MOVES---------------------");

	let old_tree = try!(old.tree());
	let new_tree = try!(new.tree());
	// Build up a diff of the two trees.
	let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();

    let mut keys: Vec<String> = Vec::new();  // TODO Will become hashmap.

    let mut founds: Vec<Found> = Vec::new();

	// Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
	// It's a bit weird, but I think it will provide the necessary information.
	diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        // Have not defined behaviour for number of files != 2.
        assert!(delta.nfiles() == 2);

		// Thinking:
		//  * If is not a hunk, keep going.
		//  * If it's a hunk, do regex magic.
		//  * Stick regex output into a hashmap as a hash.
		//  * Later, we will iterate through and output pased on the values.
		// Filter out all the boring context lines.
		// If we're not interested in this line just return since it will iterate to the next.
		if maybe_hunk.is_none() { return true }; // Return early.

        //println!("top of loop: keys={:?}", keys);
        //println!("top of loop: added={:?} deleted={:?}", added, deleted);

        // 'origin' is wrapped in pipes to ease displaying space characters.
        print!("line: old={:?} new={:?} offset={} |origin|=|{}|\n      content={}",
                 line.old_lineno(), line.new_lineno(), line.content_offset(),
                 line.origin(), str::from_utf8(line.content()).unwrap());

        //dump_diffdelta(&delta);

		match line.origin() {
			// Additions
			'+' | '>' => {
                println!("In additions. state={:?}", state);

                added.push_str(str::from_utf8(line.content()).unwrap());

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: delta.old_file().path().unwrap().clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Addition;
				true
			},
			// Deletions
			'-' | '<' => {
                println!("In deletion. state={:?}", state);

                deleted.push_str(str::from_utf8(line.content()).unwrap());

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: delta.new_file().path().unwrap().clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                        });
                        added = String::new();
                    },
                    _ => (),
                }

                state = State::Deletion;
				true
			},
			// Other
			_         => {
                println!("in _. state={:?}", state);

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: delta.new_file().path().unwrap().clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: delta.old_file().path().unwrap().clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
		}
	});

    println!("KEYS={:?}", keys);
    println!("FOUNDS={:?}", founds);

    Ok(vec![Output {
	    old_commit: old.id(),
	    new_commit: new.id(),
        old_filename: String::from_str("oldfilename"),
        new_filename: String::from_str("newfilename"),
	    origin_line: 0,
	    destination_line: 0,
	    num_lines: 0
    }])
}

#[derive(Debug)]
struct Output {
    old_commit: Oid,
    new_commit: Oid,
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<commit_msg>Add message to assert!().<commit_after>#![feature(core)]
#![feature(path)]

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex;
extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;
use regex::Regex;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            let output = find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
            make_output(output);
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {let (old, new) = (&pair[1], &pair[0]);
			let output = find_moves(&repo, old.clone(), new.clone()).unwrap();
			// mangle the vectors here to make pretty output
			make_output(output);
		}
	}
}

fn make_output(output: Vec<Output>) {
	println!("make_output:");
    println!("\toutput.len()={}", output.len());
    for i in range(0, output.len()) {
            println!("\told_commit={}",         output[i].old_commit);
            println!("\tnew_commit={}",         output[i].new_commit);
            println!("\torigin_line={}",        output[i].origin_line);
            println!("\tdestintation_line={}",  output[i].destination_line);
            println!("\tnum_lines={}",          output[i].num_lines);
    }
}

#[derive(Debug)]
enum State {
    Other, Addition, Deletion
}

#[derive(Debug)]
enum FoundState {
    Added, Deleted
}

fn dump_diffdelta(delta: &DiffDelta) {
    println!("delta: nfiles={} status={:?} old_file=(id={} path_bytes={:?} path={:?} tsize={}) new_file=(id={} path_bytes={:?} path={:?} tsize={})",
            delta.nfiles(), delta.status(),
            delta.old_file().id(), delta.old_file().path_bytes(), delta.old_file().path(), delta.old_file().size(),
            delta.new_file().id(), delta.new_file().path_bytes(), delta.new_file().path(), delta.new_file().size());
}

fn format_key(key: String) -> String {
    let removeWhiteSpace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    removeWhiteSpace.replace_all(key.as_slice(), "")
}

#[derive(Debug)]
struct Found {
    filename: Path,
    key: String,
    state: FoundState,
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {

    println!("\nFIND MOVES---------------------");

	let old_tree = try!(old.tree());
	let new_tree = try!(new.tree());
	// Build up a diff of the two trees.
	let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();

    let mut keys: Vec<String> = Vec::new();  // TODO Will become hashmap.

    let mut founds: Vec<Found> = Vec::new();

	// Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
	// It's a bit weird, but I think it will provide the necessary information.
	diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files.");

		// Thinking:
		//  * If is not a hunk, keep going.
		//  * If it's a hunk, do regex magic.
		//  * Stick regex output into a hashmap as a hash.
		//  * Later, we will iterate through and output pased on the values.
		// Filter out all the boring context lines.
		// If we're not interested in this line just return since it will iterate to the next.
		if maybe_hunk.is_none() { return true }; // Return early.

        //println!("top of loop: keys={:?}", keys);
        //println!("top of loop: added={:?} deleted={:?}", added, deleted);

        // 'origin' is wrapped in pipes to ease displaying space characters.
        print!("line: old={:?} new={:?} offset={} |origin|=|{}|\n      content={}",
                 line.old_lineno(), line.new_lineno(), line.content_offset(),
                 line.origin(), str::from_utf8(line.content()).unwrap());

        //dump_diffdelta(&delta);

		match line.origin() {
			// Additions
			'+' | '>' => {
                println!("In additions. state={:?}", state);

                added.push_str(str::from_utf8(line.content()).unwrap());

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: delta.old_file().path().unwrap().clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Addition;
				true
			},
			// Deletions
			'-' | '<' => {
                println!("In deletion. state={:?}", state);

                deleted.push_str(str::from_utf8(line.content()).unwrap());

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: delta.new_file().path().unwrap().clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                        });
                        added = String::new();
                    },
                    _ => (),
                }

                state = State::Deletion;
				true
			},
			// Other
			_         => {
                println!("in _. state={:?}", state);

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: delta.new_file().path().unwrap().clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: delta.old_file().path().unwrap().clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
		}
	});

    println!("KEYS={:?}", keys);
    println!("FOUNDS={:?}", founds);

    Ok(vec![Output {
	    old_commit: old.id(),
	    new_commit: new.id(),
        old_filename: String::from_str("oldfilename"),
        new_filename: String::from_str("newfilename"),
	    origin_line: 0,
	    destination_line: 0,
	    num_lines: 0
    }])
}

#[derive(Debug)]
struct Output {
    old_commit: Oid,
    new_commit: Oid,
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<|endoftext|>