<commit_before>#![feature(phase)]
extern crate cargo;
extern crate docopt;
#[phase(plugin)] extern crate docopt_macros;
extern crate graphviz;
extern crate serialize;

use cargo::core::resolver::Resolve;
use cargo::core::source::SourceId;
use docopt::{Error, FlagParser};
use graphviz as dot;
use std::io::File;
use std::str;

docopt!(Flags, "
Generate a graph of package dependencies in graphviz format

Usage: cargo dot [options]
       cargo dot --help

Options:
    -h, --help         Show this message
    -V, --version      Print version info and exit
    --lock-file=FILE   Specify location of input file, default \"Cargo.lock\"
    --dot-file=FILE    Output to file, default prints to stdout
")

fn main() {
    let config = docopt::Config { version: Some("0.1.0".to_string()) , ..docopt::DEFAULT_CONFIG };
    let flags: Flags = FlagParser::parse_conf(config).unwrap_or_else(|e| e.exit());
    let lock_file  = unless_empty(flags.flag_lock_file, "Cargo.lock");
    let dot_f_flag = if flags.flag_dot_file.is_empty() { None } else { Some(flags.flag_dot_file) };

    // TODO: figure out how to get rid of this.
    let dummy_src_id = SourceId::from_url("git+https://github.com/doopdoop/dodoododo#b3a9dee814af4846267383c800999a42b295e0d2".to_string());
    let resolved = cargo::ops::load_lockfile(&Path::new(lock_file), &dummy_src_id)
                     .unwrap_or_else(|e| exit_with(e.description().as_slice()))
                     .unwrap_or_else(||  exit_with("Lock file not found."));

    let graph = Graph::with_root(resolved.root().get_name());
    graph.add_dependencies(&resolved);

    match dot_f_flag {
        None           => graph.render_to(&mut std::io::stdio::stdout()),
        Some(dot_file) => graph.render_to(&mut File::create(&Path::new(dot_file)))
    };

}

fn exit_with(s: &str) -> ! {
    fail!("Error parsing lockfile: {}", s)
}

fn unless_empty(s: String, default: &str) -> String {
    if s.is_empty() {
        default.to_string()
    } else {
        s
    }
}

pub type Nd = uint;
pub type Ed = (uint, uint);
pub struct Graph<'a> {
    nodes: Vec<&'a str>,
    edges: Vec<Ed>,
}

impl<'a> Graph<'a> {
    pub fn with_root(root: &str) -> Graph {
        Graph { nodes: vec![root], edges: vec![] }
    }

    pub fn add_dependencies(&mut self, resolved: &'a Resolve) {
        for crat in resolved.iter() {
            match resolved.deps(crat) {
                Some(mut crate_deps) => {
                    let name = crat.get_name();
                    let idl = self.find_or_add(name);
                    for dep in crate_deps {
                        let dep_name = dep.get_name();
                        let idr = self.find_or_add(dep_name);
                        self.edges.push((idl, idr));
                    };
                },
                None => { }
            }
        }
    }

    fn find_or_add(&mut self, new: &'a str) -> uint {
        for (i, s) in self.nodes.iter().enumerate() {
            if s.as_slice() == new {
                return i
            }
        }
        self.nodes.push(new);
        self.nodes.len() - 1
    }

    pub fn render_to<W:Writer>(&'a self, output: &mut W) {
        dot::render(self, output).unwrap()
    }
}

impl<'a> dot::Labeller<'a, Nd, Ed> for Graph<'a> {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("example3")
    }
    fn node_id(&'a self, n: &Nd) -> dot::Id {
        dot::Id::new(format!("N{:u}", *n))
    }
    fn node_label<'a>(&'a self, i: &Nd) -> dot::LabelText<'a> {
        dot::LabelStr(str::Slice(self.nodes[*i].as_slice()))
    }
}

impl<'a> dot::GraphWalk<'a, Nd, Ed> for Graph<'a> {
    fn nodes(&'a self) -> dot::Nodes<'a,Nd> {
        range(0, self.nodes.len()).collect()
    }
    fn edges(&'a self) -> dot::Edges<'a,Ed> {
        dot::maybe_owned_vec::Borrowed(self.edges.as_slice())
    }
    fn source(&self, &(s, _): &Ed) -> Nd { s }
    fn target(&self, &(_, t): &Ed) -> Nd { t }
}
<commit_msg>Remove unnecessary lifetime specifiers<commit_after>#![feature(phase)]
extern crate cargo;
extern crate docopt;
#[phase(plugin)] extern crate docopt_macros;
extern crate graphviz;
extern crate serialize;

use cargo::core::resolver::Resolve;
use cargo::core::source::SourceId;
use docopt::{Error, FlagParser};
use graphviz as dot;
use std::io::File;
use std::str;

docopt!(Flags, "
Generate a graph of package dependencies in graphviz format

Usage: cargo dot [options]
       cargo dot --help

Options:
    -h, --help         Show this message
    -V, --version      Print version info and exit
    --lock-file=FILE   Specify location of input file, default \"Cargo.lock\"
    --dot-file=FILE    Output to file, default prints to stdout
")

fn main() {
    let config = docopt::Config { version: Some("0.1.0".to_string()) , ..docopt::DEFAULT_CONFIG };
    let flags: Flags = FlagParser::parse_conf(config).unwrap_or_else(|e| e.exit());
    let lock_file  = unless_empty(flags.flag_lock_file, "Cargo.lock");
    let dot_f_flag = if flags.flag_dot_file.is_empty() { None } else { Some(flags.flag_dot_file) };

    // TODO: figure out how to get rid of this.
    let dummy_src_id = SourceId::from_url("git+https://github.com/doopdoop/dodoododo#b3a9dee814af4846267383c800999a42b295e0d2".to_string());
    let resolved = cargo::ops::load_lockfile(&Path::new(lock_file), &dummy_src_id)
                     .unwrap_or_else(|e| exit_with(e.description().as_slice()))
                     .unwrap_or_else(||  exit_with("Lock file not found."));

    let mut graph = Graph::with_root(resolved.root().get_name());
    graph.add_dependencies(&resolved);

    match dot_f_flag {
        None           => graph.render_to(&mut std::io::stdio::stdout()),
        Some(dot_file) => graph.render_to(&mut File::create(&Path::new(dot_file)))
    };

}

fn exit_with(s: &str) -> ! {
    fail!("Error parsing lockfile: {}", s)
}

fn unless_empty(s: String, default: &str) -> String {
    if s.is_empty() {
        default.to_string()
    } else {
        s
    }
}

pub type Nd = uint;
pub type Ed = (uint, uint);
pub struct Graph<'a> {
    nodes: Vec<&'a str>,
    edges: Vec<Ed>,
}

impl<'a> Graph<'a> {
    pub fn with_root(root: &str) -> Graph {
        Graph { nodes: vec![root], edges: vec![] }
    }

    pub fn add_dependencies(&mut self, resolved: &'a Resolve) {
        for crat in resolved.iter() {
            match resolved.deps(crat) {
                Some(mut crate_deps) => {
                    let name = crat.get_name();
                    let idl = self.find_or_add(name);
                    for dep in crate_deps {
                        let dep_name = dep.get_name();
                        let idr = self.find_or_add(dep_name);
                        self.edges.push((idl, idr));
                    };
                },
                None => { }
            }
        }
    }

    fn find_or_add(&mut self, new: &'a str) -> uint {
        for (i, s) in self.nodes.iter().enumerate() {
            if s.as_slice() == new {
                return i
            }
        }
        self.nodes.push(new);
        self.nodes.len() - 1
    }

    pub fn render_to<W:Writer>(&'a self, output: &mut W) {
        dot::render(self, output).unwrap()
    }
}

impl<'a> dot::Labeller<'a, Nd, Ed> for Graph<'a> {
    fn graph_id(&self) -> dot::Id<'a> {
        dot::Id::new("example3")
    }
    fn node_id(&self, n: &Nd) -> dot::Id {
        dot::Id::new(format!("N{:u}", *n))
    }
    fn node_label<'a>(&'a self, i: &Nd) -> dot::LabelText<'a> {
        dot::LabelStr(str::Slice(self.nodes[*i].as_slice()))
    }
}

impl<'a> dot::GraphWalk<'a, Nd, Ed> for Graph<'a> {
    fn nodes(&self) -> dot::Nodes<'a,Nd> {
        range(0, self.nodes.len()).collect()
    }
    fn edges(&'a self) -> dot::Edges<'a,Ed> {
        dot::maybe_owned_vec::Borrowed(self.edges.as_slice())
    }
    fn source(&self, &(s, _): &Ed) -> Nd { s }
    fn target(&self, &(_, t): &Ed) -> Nd { t }
}
<|endoftext|>


<commit_before>#[ macro_use ] extern crate diesel_codegen;
pub mod schema;
pub mod models;
#[ macro_use ] extern crate diesel;
#[ macro_use ] extern crate clap;
extern crate dotenv;
extern crate chrono;

use chrono::prelude::*;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use dotenv::dotenv;
use std::env;
use std::process;

use clap::App;

use models::*;
use schema::timers;

fn establish_connection() -> SqliteConnection {
    dotenv().ok();

    let database_url = env::var( "DATABASE_URL" )
        .expect( "DATABASE_URL expected to be set in the environment" );
    SqliteConnection::establish( &database_url )
        .expect( &format!( "Error connecting to {}", database_url ) )
}

fn create_timer<'a>( conn: &SqliteConnection, name: &'a str, start_entry: &'a str ) -> usize {

    let new_timer = NewTimer {
        name: name,
        start_time: Local::now().timestamp() as i32,
        start_entry: start_entry,
        running: 1,
    };

    diesel::insert( &new_timer )
        .into( timers::table )
        .execute( conn )
        .expect( "Error saving new timer" )
}

fn parse_date<'a>( ts: i32 ) -> String {
    let timestring = format!( "{:?}", ts );
    let dt: DateTime<Local> = Local.datetime_from_str( &timestring, "%s" ).unwrap();
    dt.format( "%Y-%m-%d" ).to_string()
}

fn parse_time<'a>( ts: i32 ) -> String {
    let timestring = format!( "{:?}", ts );
    let dt: DateTime<Local> = Local.datetime_from_str( &timestring, "%s" ).unwrap();
    if ts == 0 {
        format!( "NOW" )
    } else {
        dt.format( "%H:%M:%S" ).to_string()
    }
}

fn get_duration<'a>( s: i32, e: i32 ) -> String {
    let mut now: i32 = Local::now().timestamp() as i32;
    if e > s {
        now = e;
    }
    let delta = now - s;
    format!(
        "{hours:02}:{minutes:02}:{seconds:02}",
        hours=delta / 60 / 60,
        minutes=delta / 60 % 60,
        seconds=delta % 60
    )
}

/*
 * The main function which sets up the CLI and calls the match handlers
 */
fn main () {
    let config = load_yaml!( "config.yml" );
    let matches = App::from_yaml( config )
        .about( env!( "CARGO_PKG_DESCRIPTION" ) )
        .version( crate_version!() )
        .get_matches();
    let verbosity = matches.is_present( "verbose" );
    let connection = establish_connection();

    match matches.subcommand() {
        ( "start", Some( o ) ) => {
            let n = o.value_of( "name" ).unwrap_or( "" );
            let e = o.value_of( "entry" ).unwrap_or( "" );

            if n == "" {
                println!( "Cannot start a timer without a name: {}", &n );
                process::exit(99);
            }

            if verbosity {
                println!( "Starting a timer for `{}` with message \"{}\".", &n, &e );
            }
            create_timer( &connection, &n, &e );
            if verbosity {
                println!( "Started timer for {}", n );
            }
        },
        ( "stop", Some( o ) ) => {
            use schema::timers::dsl::*;
            let n = o.value_of( "name" ).unwrap_or( "" );
            let e = o.value_of( "entry" ).unwrap_or( "" );
            if verbosity {
                if n == "" {
                    println!( "Ending latest running timer with message \"{}\"", &e );
                } else {
                    println!( "Ending a timer for `{}` with message \"{}\".", &n, &e );
                }
            }

            let timer: std::result::Result<models::Timer, diesel::result::Error> = timers.filter( name.like( &n ) )
                .filter( running.eq( 1 ) )
                .first( &connection );

            match timer {
                Ok( t ) => {
                    let _ = diesel::update( timers.find( &t.id ) )
                        .set( ( running.eq( 0 ), end_time.eq( Local::now().timestamp() as i32 ), end_entry.eq( &e ) ) )
                        .execute( &connection )
                        .expect( &format!( "Unable to stop timer {}", &t.id ) );
                    if verbosity {
                        println!( "Stopped timer for {}", &t.name );
                    }
                },
                Err( err ) => {
                    if verbosity {
                        println!( "{} running timers matching {}, so attempting to stop lastest running timer.", &err, &n );
                    }
                    let latest_timer: std::result::Result<models::Timer, diesel::result::Error> = timers.filter( running.eq( 1 ) ).first( &connection );
                    match latest_timer {
                        Ok( lt ) => {
                            let _ = diesel::update( timers.find( &lt.id ) )
                                .set( ( running.eq( 0 ), end_time.eq( Local::now().timestamp() as i32 ), end_entry.eq( &e ) ) )
                                .execute( &connection )
                                .expect( "Unable to stop latest running timer." );
                            if verbosity {
                                println!( "Stopped latest running timer for {}", &lt.name );
                            }
                        },
                        _ => (),
                    }
                }
            }
        },
        ( "list", Some( o ) ) => {
            use schema::timers::dsl::*;
            let results = timers.order( id.asc() )
                .load::<Timer>( &connection )
                .expect( "Error loading timers table" );
            if verbosity {
                println!( "Displaying {} timers", results.len() );
            }
            for timer in results {
                println!(
                    "{timer_id} {start_date} [ {start_time} - {end_time} ] ( {duration} ) [ {timer_name} ]",
                    timer_id=timer.id,
                    start_date=parse_date( timer.start_time ),
                    start_time=parse_time( timer.start_time ),
                    end_time=parse_time( timer.end_time ),
                    duration=get_duration( timer.start_time, timer.end_time ),
                    timer_name=timer.name
                );
                if verbosity {
                    println!( "message(s):\n{}\n{}", timer.start_entry, timer.end_entry );
                }
            }
        },
        ( "status", Some( o ) ) => {
            use schema::timers::dsl::*;
            let results = timers.filter( running.eq( 1 ) )
                .order( id.desc() )
                .load::<Timer>( &connection )
                .expect( "Error loading timers" );

            if results.len() > 0 {
                let timer = results.first().unwrap();
                if verbosity {
                    println!(
                        "{timer_id} {start_date} [ {start_time} - {end_time} ] ( {duration} ) [ {timer_name} ]",
                        timer_id=timer.id,
                        start_date=parse_date( timer.start_time ),
                        start_time=parse_time( timer.start_time ),
                        end_time=parse_time( timer.end_time ),
                        duration=get_duration( timer.start_time, timer.end_time ),
                        timer_name=timer.name
                    );
                    println!( "message(s):\n{} {}", timer.start_entry, timer.end_entry );
                } else {
                    println!( "elapsed time: {}", get_duration( timer.start_time, timer.end_time ) );
                }
            } else {
                println!( "No timers currently running" );
                process::exit(99);
            }
        },
        ( "remove", Some( o ) ) => {
            use schema::timers::dsl::*;
            let i: i32 = value_t!( o, "id", i32 ).unwrap_or( 0 );

            if i < 1 {
                println!( "Cannot remove timers without a proper id." );
                process::exit(99);
            }

            if verbosity {
                println!( "Removing timer with matching id {}", &i );
            }

            let _ = diesel::delete( timers.find( &i ) )
                .execute( &connection )
                .expect( &format!( "Unable to remove timer matching id {}", &i ) );
        },
        _ => (),
    };
}
<commit_msg>Remove warnings & use _ rather than unused options<commit_after>#[ macro_use ] extern crate diesel_codegen;
pub mod schema;
pub mod models;
#[ macro_use ] extern crate diesel;
#[ macro_use ] extern crate clap;
extern crate dotenv;
extern crate chrono;

use chrono::prelude::*;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use dotenv::dotenv;
use std::env;
use std::process;

use clap::App;

use models::*;
use schema::timers;

fn establish_connection() -> SqliteConnection {
    dotenv().ok();

    let database_url = env::var( "DATABASE_URL" )
        .expect( "DATABASE_URL expected to be set in the environment" );
    SqliteConnection::establish( &database_url )
        .expect( &format!( "Error connecting to {}", database_url ) )
}

fn create_timer<'a>( conn: &SqliteConnection, name: &'a str, start_entry: &'a str ) -> usize {

    let new_timer = NewTimer {
        name: name,
        start_time: Local::now().timestamp() as i32,
        start_entry: start_entry,
        running: 1,
    };

    diesel::insert( &new_timer )
        .into( timers::table )
        .execute( conn )
        .expect( "Error saving new timer" )
}

fn parse_date<'a>( ts: i32 ) -> String {
    let timestring = format!( "{:?}", ts );
    let dt: DateTime<Local> = Local.datetime_from_str( &timestring, "%s" ).unwrap();
    dt.format( "%Y-%m-%d" ).to_string()
}

fn parse_time<'a>( ts: i32 ) -> String {
    let timestring = format!( "{:?}", ts );
    let dt: DateTime<Local> = Local.datetime_from_str( &timestring, "%s" ).unwrap();
    if ts == 0 {
        format!( "NOW" )
    } else {
        dt.format( "%H:%M:%S" ).to_string()
    }
}

fn get_duration<'a>( s: i32, e: i32 ) -> String {
    let mut now: i32 = Local::now().timestamp() as i32;
    if e > s {
        now = e;
    }
    let delta = now - s;
    format!(
        "{hours:02}:{minutes:02}:{seconds:02}",
        hours=delta / 60 / 60,
        minutes=delta / 60 % 60,
        seconds=delta % 60
    )
}

/*
 * The main function which sets up the CLI and calls the match handlers
 */
fn main () {
    let config = load_yaml!( "config.yml" );
    let matches = App::from_yaml( config )
        .about( env!( "CARGO_PKG_DESCRIPTION" ) )
        .version( crate_version!() )
        .get_matches();
    let verbosity = matches.is_present( "verbose" );
    let connection = establish_connection();

    match matches.subcommand() {
        ( "start", Some( o ) ) => {
            let n = o.value_of( "name" ).unwrap_or( "" );
            let e = o.value_of( "entry" ).unwrap_or( "" );

            if n == "" {
                println!( "Cannot start a timer without a name: {}", &n );
                process::exit(99);
            }

            if verbosity {
                println!( "Starting a timer for `{}` with message \"{}\".", &n, &e );
            }
            create_timer( &connection, &n, &e );
            if verbosity {
                println!( "Started timer for {}", n );
            }
        },
        ( "stop", Some( o ) ) => {
            use schema::timers::dsl::*;
            let n = o.value_of( "name" ).unwrap_or( "" );
            let e = o.value_of( "entry" ).unwrap_or( "" );
            if verbosity {
                if n == "" {
                    println!( "Ending latest running timer with message \"{}\"", &e );
                } else {
                    println!( "Ending a timer for `{}` with message \"{}\".", &n, &e );
                }
            }

            let timer: std::result::Result<models::Timer, diesel::result::Error> = timers.filter( name.like( &n ) )
                .filter( running.eq( 1 ) )
                .first( &connection );

            match timer {
                Ok( t ) => {
                    let _ = diesel::update( timers.find( &t.id ) )
                        .set( ( running.eq( 0 ), end_time.eq( Local::now().timestamp() as i32 ), end_entry.eq( &e ) ) )
                        .execute( &connection )
                        .expect( &format!( "Unable to stop timer {}", &t.id ) );
                    if verbosity {
                        println!( "Stopped timer for {}", &t.name );
                    }
                },
                Err( err ) => {
                    if verbosity {
                        println!( "{} running timers matching {}, so attempting to stop lastest running timer.", &err, &n );
                    }
                    let latest_timer: std::result::Result<models::Timer, diesel::result::Error> = timers.filter( running.eq( 1 ) ).first( &connection );
                    match latest_timer {
                        Ok( lt ) => {
                            let _ = diesel::update( timers.find( &lt.id ) )
                                .set( ( running.eq( 0 ), end_time.eq( Local::now().timestamp() as i32 ), end_entry.eq( &e ) ) )
                                .execute( &connection )
                                .expect( "Unable to stop latest running timer." );
                            if verbosity {
                                println!( "Stopped latest running timer for {}", &lt.name );
                            }
                        },
                        _ => (),
                    }
                }
            }
        },
        ( "list", _ ) => {
            use schema::timers::dsl::*;
            let results = timers.order( id.asc() )
                .load::<Timer>( &connection )
                .expect( "Error loading timers table" );
            if verbosity {
                println!( "Displaying {} timers", results.len() );
            }
            for timer in results {
                println!(
                    "{timer_id} {start_date} [ {start_time} - {end_time} ] ( {duration} ) [ {timer_name} ]",
                    timer_id=timer.id,
                    start_date=parse_date( timer.start_time ),
                    start_time=parse_time( timer.start_time ),
                    end_time=parse_time( timer.end_time ),
                    duration=get_duration( timer.start_time, timer.end_time ),
                    timer_name=timer.name
                );
                if verbosity {
                    println!( "message(s):\n{}\n{}", timer.start_entry, timer.end_entry );
                }
            }
        },
        ( "status", _ ) => {
            use schema::timers::dsl::*;
            let results = timers.filter( running.eq( 1 ) )
                .order( id.desc() )
                .load::<Timer>( &connection )
                .expect( "Error loading timers" );

            if results.len() > 0 {
                let timer = results.first().unwrap();
                if verbosity {
                    println!(
                        "{timer_id} {start_date} [ {start_time} - {end_time} ] ( {duration} ) [ {timer_name} ]",
                        timer_id=timer.id,
                        start_date=parse_date( timer.start_time ),
                        start_time=parse_time( timer.start_time ),
                        end_time=parse_time( timer.end_time ),
                        duration=get_duration( timer.start_time, timer.end_time ),
                        timer_name=timer.name
                    );
                    println!( "message(s):\n{} {}", timer.start_entry, timer.end_entry );
                } else {
                    println!( "elapsed time: {}", get_duration( timer.start_time, timer.end_time ) );
                }
            } else {
                println!( "No timers currently running" );
                process::exit(99);
            }
        },
        ( "remove", Some( o ) ) => {
            use schema::timers::dsl::*;
            let i: i32 = value_t!( o, "id", i32 ).unwrap_or( 0 );

            if i < 1 {
                println!( "Cannot remove timers without a proper id." );
                process::exit(99);
            }

            if verbosity {
                println!( "Removing timer with matching id {}", &i );
            }

            let _ = diesel::delete( timers.find( &i ) )
                .execute( &connection )
                .expect( &format!( "Unable to remove timer matching id {}", &i ) );
        },
        _ => (),
    };
}
<|endoftext|>


<commit_before>use csv;
use indicatif;
use lazy_static;
use prettytable;
use regex;
use rusqlite;
use rustyline;
use std::env;
use std::error::Error;
use std::fs::File;

fn _normalize_col(col: &str) -> String {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"\(.*?\)").unwrap();
    }
    RE.replace_all(col, "")
        .to_lowercase()
        .trim()
        .replace(" ", "_")
        .replace(".", "_")
        .replace("-", "_")
        .replace("/", "_")
        .replace("?", "")
}

fn _create_table(db: &mut rusqlite::Connection, table_name: &str, cols: &[String]) {
    let create_columns = cols
        .iter()
        .map(|c| format!("{} varchar", c))
        .collect::<Vec<String>>()
        .join(", ");
    db.execute(
        &format!("CREATE TABLE {} ({})", table_name, create_columns),
        &[] as &[&dyn rusqlite::types::ToSql],
    )
    .unwrap();
}

fn _load_table_from_path(
    db: &mut rusqlite::Connection,
    table_name: &str,
    path: String,
) -> Vec<String> {
    let mut num_rows = 0;
    let f = File::open(path).unwrap();
    let file_size = f.metadata().unwrap().len();
    let mut reader = csv::Reader::from_reader(f);

    let normalized_cols =
        reader
            .headers()
            .unwrap()
            .iter()
            .map(_normalize_col)
            .fold(vec![], |mut v, orig_col| {
                let mut col = orig_col.clone();
                let mut i = 1;
                while v.contains(&col) {
                    col = format!("{}_{}", orig_col, i);
                    i += 1
                }
                v.push(col);
                v
            });
    _create_table(db, table_name, &normalized_cols);

    let insert_query = format!(
        "INSERT INTO {} VALUES ({})",
        table_name,
        normalized_cols
            .iter()
            .map(|_| "?")
            .collect::<Vec<_>>()
            .join(", ")
    );
    let pb = indicatif::ProgressBar::new(file_size);
    pb.set_style(
        indicatif::ProgressStyle::default_bar()
            .template("[{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
            .progress_chars("#>-"),
    );
    let mut records = reader.records();
    let tx = db.transaction().unwrap();
    {
        let mut stmt = tx.prepare(&insert_query).unwrap();
        while let Some(row) = records.next() {
            stmt.execute(&row.unwrap()).unwrap();

            num_rows += 1;
            if num_rows % 10000 == 0 {
                pb.set_position(records.reader().position().byte())
            }
        }
    }
    tx.commit().unwrap();
    pb.finish();

    println!(
        "Loaded {} rows into {}({})",
        num_rows,
        table_name,
        normalized_cols.join(", "),
    );
    normalized_cols
}

struct FromAnySqlType {
    value: String,
}

impl rusqlite::types::FromSql for FromAnySqlType {
    fn column_result(
        value: rusqlite::types::ValueRef<'_>,
    ) -> Result<FromAnySqlType, rusqlite::types::FromSqlError> {
        let result = match value {
            rusqlite::types::ValueRef::Null => "null".to_string(),
            rusqlite::types::ValueRef::Integer(v) => v.to_string(),
            rusqlite::types::ValueRef::Real(v) => v.to_string(),
            rusqlite::types::ValueRef::Blob(v) | rusqlite::types::ValueRef::Text(v) => {
                String::from_utf8(v.to_vec()).unwrap()
            }
        };
        Ok(FromAnySqlType { value: result })
    }
}

fn _prepare_query<'a>(
    conn: &'a mut rusqlite::Connection,
    query: &str,
) -> Result<rusqlite::Statement<'a>, String> {
    return conn.prepare(&query).map_err(|e| e.description().to_owned());
}

fn _handle_query(conn: &mut rusqlite::Connection, line: &str) -> Result<(), String> {
    let mut stmt = _prepare_query(conn, line)?;

    let mut table = prettytable::Table::new();
    let mut title_row = prettytable::Row::new(vec![]);
    for col in stmt.column_names() {
        title_row.add_cell(prettytable::Cell::new(col));
    }
    table.set_titles(title_row);
    table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);

    let mut results = stmt.query(&[] as &[&dyn rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        let mut row = prettytable::Row::new(vec![]);
        for i in 0..r.column_count() {
            let cell: FromAnySqlType = r.get(i).unwrap();
            row.add_cell(prettytable::Cell::new(&cell.value));
        }
        table.add_row(row);
    }
    table.printstd();
    Ok(())
}

fn _handle_export(conn: &mut rusqlite::Connection, line: &str) -> Result<(), String> {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"^\.export\(([\w_\-\./]+)\) (.*)").unwrap();
    }
    let caps = RE
        .captures(line)
        .ok_or_else(|| "Must match `.export(file-name) SQL`".to_owned())?;
    let destination_path = &caps[1];
    let query = &caps[2];

    let mut stmt = _prepare_query(conn, query)?;

    let mut writer = csv::Writer::from_path(destination_path).unwrap();
    writer.write_record(stmt.column_names()).unwrap();

    let mut results = stmt.query(&[] as &[&dyn rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        writer
            .write_record((0..r.column_count()).map(|i| {
                let cell: FromAnySqlType = r.get(i).unwrap();
                cell.value
            }))
            .unwrap();
    }

    Ok(())
}

fn _process_query(conn: &mut rusqlite::Connection, line: &str) {
    let result = if line.starts_with(".export") {
        _handle_export(conn, line)
    } else {
        _handle_query(conn, line)
    };
    if let Err(e) = result {
        println!("{}", e);
    }
}

struct SimpleWordCompleter {
    words: Vec<String>,
}

static BREAK_CHARS: [u8; 4] = [b' ', b'(', b')', b','];
impl SimpleWordCompleter {
    fn new(words: Vec<String>) -> SimpleWordCompleter {
        SimpleWordCompleter { words }
    }
}

impl rustyline::Helper for SimpleWordCompleter {}

impl rustyline::hint::Hinter for SimpleWordCompleter {
    fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context<'_>) -> Option<String> {
        None
    }
}

impl rustyline::highlight::Highlighter for SimpleWordCompleter {}

impl rustyline::completion::Completer for SimpleWordCompleter {
    type Candidate = String;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        _ctx: &rustyline::Context<'_>,
    ) -> rustyline::Result<(usize, Vec<String>)> {
        let (start, word) = rustyline::completion::extract_word(line, pos, None, &BREAK_CHARS);

        let matches = self
            .words
            .iter()
            .filter(|w| w.starts_with(word))
            .cloned()
            .collect();
        Ok((start, matches))
    }
}

fn main() {
    let mut paths = env::args().skip(1);

    let mut conn = rusqlite::Connection::open_in_memory().unwrap();

    let mut base_words = [
        "distinct", "select", "from", "group", "by", "order", "where", "count", "limit", "offset",
        ".export",
    ]
    .iter()
    .map(|s| s.to_string())
    .collect::<Vec<String>>();

    if paths.len() == 1 {
        let mut col_names = _load_table_from_path(&mut conn, "t", paths.next().unwrap());
        base_words.append(&mut col_names);
    } else {
        for (idx, path) in paths.enumerate() {
            let mut col_names = _load_table_from_path(&mut conn, &format!("t{}", idx + 1), path);
            base_words.append(&mut col_names);
        }
    }

    let completer = SimpleWordCompleter::new(base_words);
    let mut rl = rustyline::Editor::new();
    rl.set_helper(Some(completer));
    loop {
        match rl.readline("> ") {
            Ok(line) => {
                if line.trim().is_empty() {
                    continue;
                }
                _process_query(&mut conn, &line);
                rl.add_history_entry(line);
            }
            Err(rustyline::error::ReadlineError::Interrupted) => {
                println!("Interrupted");
                continue;
            }
            Err(rustyline::error::ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
}
<commit_msg>clippy<commit_after>use csv;
use indicatif;
use lazy_static;
use prettytable;
use regex;
use rusqlite;
use rustyline;
use std::env;
use std::error::Error;
use std::fs::File;

fn _normalize_col(col: &str) -> String {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"\(.*?\)").unwrap();
    }
    RE.replace_all(col, "")
        .to_lowercase()
        .trim()
        .replace(" ", "_")
        .replace(".", "_")
        .replace("-", "_")
        .replace("/", "_")
        .replace("?", "")
}

fn _create_table(db: &mut rusqlite::Connection, table_name: &str, cols: &[String]) {
    let create_columns = cols
        .iter()
        .map(|c| format!("{} varchar", c))
        .collect::<Vec<String>>()
        .join(", ");
    db.execute(
        &format!("CREATE TABLE {} ({})", table_name, create_columns),
        &[] as &[&dyn rusqlite::types::ToSql],
    )
    .unwrap();
}

fn _load_table_from_path(
    db: &mut rusqlite::Connection,
    table_name: &str,
    path: String,
) -> Vec<String> {
    let mut num_rows = 0;
    let f = File::open(path).unwrap();
    let file_size = f.metadata().unwrap().len();
    let mut reader = csv::Reader::from_reader(f);

    let normalized_cols =
        reader
            .headers()
            .unwrap()
            .iter()
            .map(_normalize_col)
            .fold(vec![], |mut v, orig_col| {
                let mut col = orig_col.clone();
                let mut i = 1;
                while v.contains(&col) {
                    col = format!("{}_{}", orig_col, i);
                    i += 1
                }
                v.push(col);
                v
            });
    _create_table(db, table_name, &normalized_cols);

    let insert_query = format!(
        "INSERT INTO {} VALUES ({})",
        table_name,
        normalized_cols
            .iter()
            .map(|_| "?")
            .collect::<Vec<_>>()
            .join(", ")
    );
    let pb = indicatif::ProgressBar::new(file_size);
    pb.set_style(
        indicatif::ProgressStyle::default_bar()
            .template("[{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
            .progress_chars("#>-"),
    );
    let mut records = reader.records();
    let tx = db.transaction().unwrap();
    {
        let mut stmt = tx.prepare(&insert_query).unwrap();
        while let Some(row) = records.next() {
            stmt.execute(&row.unwrap()).unwrap();

            num_rows += 1;
            if num_rows % 10000 == 0 {
                pb.set_position(records.reader().position().byte())
            }
        }
    }
    tx.commit().unwrap();
    pb.finish();

    println!(
        "Loaded {} rows into {}({})",
        num_rows,
        table_name,
        normalized_cols.join(", "),
    );
    normalized_cols
}

struct FromAnySqlType {
    value: String,
}

impl rusqlite::types::FromSql for FromAnySqlType {
    fn column_result(
        value: rusqlite::types::ValueRef<'_>,
    ) -> Result<FromAnySqlType, rusqlite::types::FromSqlError> {
        let result = match value {
            rusqlite::types::ValueRef::Null => "null".to_string(),
            rusqlite::types::ValueRef::Integer(v) => v.to_string(),
            rusqlite::types::ValueRef::Real(v) => v.to_string(),
            rusqlite::types::ValueRef::Blob(v) | rusqlite::types::ValueRef::Text(v) => {
                String::from_utf8(v.to_vec()).unwrap()
            }
        };
        Ok(FromAnySqlType { value: result })
    }
}

fn _prepare_query<'a>(
    conn: &'a mut rusqlite::Connection,
    query: &str,
) -> Result<rusqlite::Statement<'a>, String> {
    conn.prepare(&query).map_err(|e| e.description().to_owned())
}

fn _handle_query(conn: &mut rusqlite::Connection, line: &str) -> Result<(), String> {
    let mut stmt = _prepare_query(conn, line)?;

    let mut table = prettytable::Table::new();
    let mut title_row = prettytable::Row::new(vec![]);
    for col in stmt.column_names() {
        title_row.add_cell(prettytable::Cell::new(col));
    }
    table.set_titles(title_row);
    table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);

    let mut results = stmt.query(&[] as &[&dyn rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        let mut row = prettytable::Row::new(vec![]);
        for i in 0..r.column_count() {
            let cell: FromAnySqlType = r.get(i).unwrap();
            row.add_cell(prettytable::Cell::new(&cell.value));
        }
        table.add_row(row);
    }
    table.printstd();
    Ok(())
}

fn _handle_export(conn: &mut rusqlite::Connection, line: &str) -> Result<(), String> {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"^\.export\(([\w_\-\./]+)\) (.*)").unwrap();
    }
    let caps = RE
        .captures(line)
        .ok_or_else(|| "Must match `.export(file-name) SQL`".to_owned())?;
    let destination_path = &caps[1];
    let query = &caps[2];

    let mut stmt = _prepare_query(conn, query)?;

    let mut writer = csv::Writer::from_path(destination_path).unwrap();
    writer.write_record(stmt.column_names()).unwrap();

    let mut results = stmt.query(&[] as &[&dyn rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        writer
            .write_record((0..r.column_count()).map(|i| {
                let cell: FromAnySqlType = r.get(i).unwrap();
                cell.value
            }))
            .unwrap();
    }

    Ok(())
}

fn _process_query(conn: &mut rusqlite::Connection, line: &str) {
    let result = if line.starts_with(".export") {
        _handle_export(conn, line)
    } else {
        _handle_query(conn, line)
    };
    if let Err(e) = result {
        println!("{}", e);
    }
}

struct SimpleWordCompleter {
    words: Vec<String>,
}

static BREAK_CHARS: [u8; 4] = [b' ', b'(', b')', b','];
impl SimpleWordCompleter {
    fn new(words: Vec<String>) -> SimpleWordCompleter {
        SimpleWordCompleter { words }
    }
}

impl rustyline::Helper for SimpleWordCompleter {}

impl rustyline::hint::Hinter for SimpleWordCompleter {
    fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context<'_>) -> Option<String> {
        None
    }
}

impl rustyline::highlight::Highlighter for SimpleWordCompleter {}

impl rustyline::completion::Completer for SimpleWordCompleter {
    type Candidate = String;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        _ctx: &rustyline::Context<'_>,
    ) -> rustyline::Result<(usize, Vec<String>)> {
        let (start, word) = rustyline::completion::extract_word(line, pos, None, &BREAK_CHARS);

        let matches = self
            .words
            .iter()
            .filter(|w| w.starts_with(word))
            .cloned()
            .collect();
        Ok((start, matches))
    }
}

fn main() {
    let mut paths = env::args().skip(1);

    let mut conn = rusqlite::Connection::open_in_memory().unwrap();

    let mut base_words = [
        "distinct", "select", "from", "group", "by", "order", "where", "count", "limit", "offset",
        ".export",
    ]
    .iter()
    .map(|s| s.to_string())
    .collect::<Vec<String>>();

    if paths.len() == 1 {
        let mut col_names = _load_table_from_path(&mut conn, "t", paths.next().unwrap());
        base_words.append(&mut col_names);
    } else {
        for (idx, path) in paths.enumerate() {
            let mut col_names = _load_table_from_path(&mut conn, &format!("t{}", idx + 1), path);
            base_words.append(&mut col_names);
        }
    }

    let completer = SimpleWordCompleter::new(base_words);
    let mut rl = rustyline::Editor::new();
    rl.set_helper(Some(completer));
    loop {
        match rl.readline("> ") {
            Ok(line) => {
                if line.trim().is_empty() {
                    continue;
                }
                _process_query(&mut conn, &line);
                rl.add_history_entry(line);
            }
            Err(rustyline::error::ReadlineError::Interrupted) => {
                println!("Interrupted");
                continue;
            }
            Err(rustyline::error::ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::path::Path;

#[cfg(feature = "debug")]
use std::env;

#[macro_use]
extern crate lazy_static;

use handlebars::{no_escape, Handlebars};

lazy_static! {
    static ref HOST: String = get_host();
}

const SITE_TITLE: &str = "Grauwoelfchen's Workroom";
const DST_DIR: &str = "./dst";

const LINKS: [Link; 4] = [
    Link {
        title: "Home",
        href: "/",
    },
    Link {
        title: "About",
        href: "/about.html",
    },
    Link {
        title: "Software",
        href: "/software.html",
    },
    Link {
        title: "Link",
        href: "/link.html",
    },
];

#[derive(Debug)]
enum Error {
    Io(io::Error),
    Parse(handlebars::TemplateError),
    Render(handlebars::RenderError),
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Self::Io(error)
    }
}

impl From<handlebars::TemplateError> for Error {
    fn from(error: handlebars::TemplateError) -> Self {
        Self::Parse(error)
    }
}

impl From<handlebars::RenderError> for Error {
    fn from(error: handlebars::RenderError) -> Self {
        Self::Render(error)
    }
}

struct Link {
    pub title: &'static str,
    pub href: &'static str,
}

fn main() -> Result<(), Error> {
    let mut reg = Handlebars::new();

    let layout = include_str!("./layout.hbs");
    reg.register_template_string("layout", layout)?;
    reg.register_escape_fn(no_escape);

    let dst = Path::new(&DST_DIR);

    // error
    let dat = include_str!("./error.hbs");
    fs::write(dst.join("error.html"), &dat.as_bytes())?;

    // index
    let mut page_data = BTreeMap::new();
    page_data.insert("host".to_string(), HOST.to_string());
    let page = reg.render_template(include_str!("./index.hbs"), &page_data)?;
    let mut data = load("Home", &page);
    data.append(&mut page_data);
    let result = reg.render("layout", &data)?;
    fs::write(dst.join("index.html"), result)?;

    // about
    let mut page_data = BTreeMap::new();
    page_data.insert("host".to_string(), HOST.to_string());
    let page = reg.render_template(include_str!("./about.hbs"), &page_data)?;
    let mut data = load("About", &page);
    data.append(&mut page_data);
    dbg!(&data);
    let result = reg.render("layout", &data)?;
    fs::write(dst.join("about.html"), result)?;

    // software
    let mut page_data = BTreeMap::new();
    page_data.insert("host".to_string(), HOST.to_string());
    let page =
        reg.render_template(include_str!("./software.hbs"), &page_data)?;
    let mut data = load("Software", &page);
    data.append(&mut page_data);
    let result = reg.render("layout", &data)?;
    fs::write(dst.join("software.html"), result)?;

    // link
    let mut page_data = BTreeMap::new();
    page_data.insert("host".to_string(), HOST.to_string());
    let page = reg.render_template(include_str!("./link.hbs"), &page_data)?;
    let mut data = load("Link", &page);
    data.append(&mut page_data);
    let result = reg.render("layout", &data)?;
    fs::write(dst.join("link.html"), result)?;

    reg.unregister_escape_fn();

    Ok(())
}

/// Returns built an entry for navigations list.
fn build_nav(heading: &str, link: &Link) -> String {
    let class = if link.title == heading {
        " class=\"active\""
    } else {
        ""
    };

    #[cfg(not(feature = "debug"))]
    let href = link.href.to_string();

    #[cfg(feature = "debug")]
    let href = format!(
        "{}{}",
        HOST.to_string(),
        if link.href == "/" {
            "/index.html"
        } else {
            link.href
        }
    );

    format!(
        r#"

  {}

"#,
        class, href, link.title,
    )
}

/// Returns the host part for href value of links.
fn get_host() -> String {
    #[cfg(not(feature = "debug"))]
    let host = "".to_string();

    // for local debugging
    #[cfg(feature = "debug")]
    let host =
        format!("{}/dst", env::current_dir().unwrap().display().to_string());
    host
}

/// Loads the content as a BTreeMap object that has navi, content
/// {page,site}_title as attributes.
fn load(heading: &str, content: &str) -> BTreeMap<String, String> {
    let mut data = BTreeMap::new();

    let mut nav = "".to_string();
    for link in LINKS {
        nav.push_str(&build_nav(heading, &link));
    }
    data.insert("navi".to_string(), nav);
    data.insert("content".to_string(), content.to_string());

    // meta
    if heading != "Home" {
        data.insert("page_title".to_string(), heading.to_string());
    }
    data.insert("site_title".to_string(), SITE_TITLE.to_string());
    data
}

#[cfg(test)]
mod test {
    use super::*;

    #[cfg(not(feature = "debug"))]
    #[test]
    fn test_build_nav() {
        let link = Link {
            title: "Foo",
            href: "/foo.html",
        };
        let nav = build_nav("Foo", &link);
        assert_eq!(
            nav,
            r#"

  Foo

"#
        );

        let link = Link {
            title: "Foo",
            href: "/foo.html",
        };
        let nav = build_nav("Bar", &link);
        assert_eq!(
            nav,
            r#"

  Foo

"#
        );
    }

    #[cfg(feature = "debug")]
    #[test]
    fn test_build_nav_debug() {
        let dir = Path::new("/tmp");
        assert!(env::set_current_dir(&dir).is_ok());

        let link = Link {
            title: "Foo",
            href: "/foo.html",
        };
        let nav = build_nav("Foo", &link);
        assert_eq!(
            nav,
            r#"

  Foo

"#
        );

        let link = Link {
            title: "Foo",
            href: "/foo.html",
        };
        let nav = build_nav("Bar", &link);
        assert_eq!(
            nav,
            r#"

  Foo

"#
        );
    }

    #[cfg(not(feature = "debug"))]
    #[test]
    fn test_get_host() {
        let host = get_host();
        assert_eq!(host, "");
    }

    #[cfg(feature = "debug")]
    #[test]
    fn test_get_host_debug() {
        let dir = Path::new("/tmp/");
        assert!(env::set_current_dir(&dir).is_ok());

        let host = get_host();
        assert_eq!(host, "/tmp/dst");
    }
}
<commit_msg>Remove dbg! macro<commit_after>use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::path::Path;

#[cfg(feature = "debug")]
use std::env;

#[macro_use]
extern crate lazy_static;

use handlebars::{no_escape, Handlebars};

lazy_static! {
    static ref HOST: String = get_host();
}

const SITE_TITLE: &str = "Grauwoelfchen's Workroom";
const DST_DIR: &str = "./dst";

const LINKS: [Link; 4] = [
    Link {
        title: "Home",
        href: "/",
    },
    Link {
        title: "About",
        href: "/about.html",
    },
    Link {
        title: "Software",
        href: "/software.html",
    },
    Link {
        title: "Link",
        href: "/link.html",
    },
];

#[derive(Debug)]
enum Error {
    Io(io::Error),
    Parse(handlebars::TemplateError),
    Render(handlebars::RenderError),
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Self::Io(error)
    }
}

impl From<handlebars::TemplateError> for Error {
    fn from(error: handlebars::TemplateError) -> Self {
        Self::Parse(error)
    }
}

impl From<handlebars::RenderError> for Error {
    fn from(error: handlebars::RenderError) -> Self {
        Self::Render(error)
    }
}

struct Link {
    pub title: &'static str,
    pub href: &'static str,
}

fn main() -> Result<(), Error> {
    let mut reg = Handlebars::new();

    let layout = include_str!("./layout.hbs");
    reg.register_template_string("layout", layout)?;
    reg.register_escape_fn(no_escape);

    let dst = Path::new(&DST_DIR);

    // error
    let dat = include_str!("./error.hbs");
    fs::write(dst.join("error.html"), &dat.as_bytes())?;

    // index
    let mut page_data = BTreeMap::new();
    page_data.insert("host".to_string(), HOST.to_string());
    let page = reg.render_template(include_str!("./index.hbs"), &page_data)?;
    let mut data = load("Home", &page);
    data.append(&mut page_data);
    let result = reg.render("layout", &data)?;
    fs::write(dst.join("index.html"), result)?;

    // about
    let mut page_data = BTreeMap::new();
    page_data.insert("host".to_string(), HOST.to_string());
    let page = reg.render_template(include_str!("./about.hbs"), &page_data)?;
    let mut data = load("About", &page);
    data.append(&mut page_data);
    let result = reg.render("layout", &data)?;
    fs::write(dst.join("about.html"), result)?;

    // software
    let mut page_data = BTreeMap::new();
    page_data.insert("host".to_string(), HOST.to_string());
    let page =
        reg.render_template(include_str!("./software.hbs"), &page_data)?;
    let mut data = load("Software", &page);
    data.append(&mut page_data);
    let result = reg.render("layout", &data)?;
    fs::write(dst.join("software.html"), result)?;

    // link
    let mut page_data = BTreeMap::new();
    page_data.insert("host".to_string(), HOST.to_string());
    let page = reg.render_template(include_str!("./link.hbs"), &page_data)?;
    let mut data = load("Link", &page);
    data.append(&mut page_data);
    let result = reg.render("layout", &data)?;
    fs::write(dst.join("link.html"), result)?;

    reg.unregister_escape_fn();

    Ok(())
}

/// Returns built an entry for navigations list.
fn build_nav(heading: &str, link: &Link) -> String {
    let class = if link.title == heading {
        " class=\"active\""
    } else {
        ""
    };

    #[cfg(not(feature = "debug"))]
    let href = link.href.to_string();

    #[cfg(feature = "debug")]
    let href = format!(
        "{}{}",
        HOST.to_string(),
        if link.href == "/" {
            "/index.html"
        } else {
            link.href
        }
    );

    format!(
        r#"

  {}

"#,
        class, href, link.title,
    )
}

/// Returns the host part for href value of links.
fn get_host() -> String {
    #[cfg(not(feature = "debug"))]
    let host = "".to_string();

    // for local debugging
    #[cfg(feature = "debug")]
    let host =
        format!("{}/dst", env::current_dir().unwrap().display().to_string());
    host
}

/// Loads the content as a BTreeMap object that has navi, content
/// {page,site}_title as attributes.
fn load(heading: &str, content: &str) -> BTreeMap<String, String> {
    let mut data = BTreeMap::new();

    let mut nav = "".to_string();
    for link in LINKS {
        nav.push_str(&build_nav(heading, &link));
    }
    data.insert("navi".to_string(), nav);
    data.insert("content".to_string(), content.to_string());

    // meta
    if heading != "Home" {
        data.insert("page_title".to_string(), heading.to_string());
    }
    data.insert("site_title".to_string(), SITE_TITLE.to_string());
    data
}

#[cfg(test)]
mod test {
    use super::*;

    #[cfg(not(feature = "debug"))]
    #[test]
    fn test_build_nav() {
        let link = Link {
            title: "Foo",
            href: "/foo.html",
        };
        let nav = build_nav("Foo", &link);
        assert_eq!(
            nav,
            r#"

  Foo

"#
        );

        let link = Link {
            title: "Foo",
            href: "/foo.html",
        };
        let nav = build_nav("Bar", &link);
        assert_eq!(
            nav,
            r#"

  Foo

"#
        );
    }

    #[cfg(feature = "debug")]
    #[test]
    fn test_build_nav_debug() {
        let dir = Path::new("/tmp");
        assert!(env::set_current_dir(&dir).is_ok());

        let link = Link {
            title: "Foo",
            href: "/foo.html",
        };
        let nav = build_nav("Foo", &link);
        assert_eq!(
            nav,
            r#"

  Foo

"#
        );

        let link = Link {
            title: "Foo",
            href: "/foo.html",
        };
        let nav = build_nav("Bar", &link);
        assert_eq!(
            nav,
            r#"

  Foo

"#
        );
    }

    #[cfg(not(feature = "debug"))]
    #[test]
    fn test_get_host() {
        let host = get_host();
        assert_eq!(host, "");
    }

    #[cfg(feature = "debug")]
    #[test]
    fn test_get_host_debug() {
        let dir = Path::new("/tmp/");
        assert!(env::set_current_dir(&dir).is_ok());

        let host = get_host();
        assert_eq!(host, "/tmp/dst");
    }
}
<|endoftext|>


<commit_before>extern crate regex;
extern crate readline;

use regex::Regex;
use std::collections::HashMap;
use std::io::prelude::*;
use std::iter::Iterator;
use std::string::String;

#[derive(Debug, PartialEq, Clone)]
enum Atom {
    Integer(i64),
    Float(f64),
    Symbol(String),
    Boolean(bool),
    Nil
}

type Function = Fn(&Vec<Atom>) -> AtomResult;
type FunctionMap = HashMap<String, Box<Function>>;

#[derive(Debug)]
enum Error {
    UnexpectedType,
    Parser,
    InvalidArguments,
    NotAFunction,
    NotImplemented
}

type AtomResult = Result<Atom, Error>;
type ParseResult = Result<Node, Error>;

fn nil_func(_: &Vec<Atom>) -> AtomResult {
    return Ok(Atom::Nil);
}

struct Env {
    func_map: FunctionMap,
    def_map: HashMap<String, Atom>
}

fn add(v: &Vec<Atom>) -> AtomResult {
    let mut result = 0;

    for i in v {
        if let Atom::Integer(d) =  *i {
            result += d
        } else {
            return Err(Error::UnexpectedType)
        }
    }

    return Ok(Atom::Integer(result));
}

fn sub(v: &Vec<Atom>) -> AtomResult {
    if v.len() < 1 {
        return Err(Error::InvalidArguments)
    }

    let mut result = match v[0] {
        Atom::Integer(d) => d,
        _ => return Err(Error::UnexpectedType)
    };

    for i in v.iter().skip(1) {
        match *i {
            Atom::Integer(d) => result -= d,
            _ => return Err(Error::UnexpectedType)
        }
    }

    return Ok(Atom::Integer(result));
}

fn equals(args: &Vec<Atom>) -> AtomResult {
    let v = &args[0];

    for i in args.iter().skip(1) {
        if v != i {
            return Ok(Atom::Boolean(false));
        }
    }
    Ok(Atom::Boolean(true))
}

fn default_env() -> Env {
    let mut env = Env{func_map: HashMap::new(), def_map: HashMap::new()};

    env.func_map.insert("nil".to_string(), Box::new(nil_func) as Box<Function>);
    env.func_map.insert("+".to_string(), Box::new(add) as Box<Function>);
    env.func_map.insert("-".to_string(), Box::new(sub) as Box<Function>);
    env.func_map.insert("=".to_string(), Box::new(equals) as Box<Function>);

    return env;
}

fn tokenize(line: &str) -> ParseResult {
    let r = Regex::new(r"\s+").unwrap();
    let l = line.replace("(", " ( ").replace(")", " ) ").trim().to_string();
    let iter = r.split(&l);
    return read_tokens(&mut iter.peekable());
}

fn atom(token: &str) -> Atom {
    match token {
        "true" => return Atom::Boolean(true),
        "false" => return Atom::Boolean(false),
        _ => ()
    }

    let i = token.parse::<i64>();
     match i {
         Ok(v) => Atom::Integer(v),
         _ => {
             let f = token.parse::<f64>();
             match f {
                 Ok(x) => Atom::Float(x),
                 _ => Atom::Symbol(token.to_string())
             }
         },
     }
}

#[derive(Debug,PartialEq)]
struct Node {
    atom: Atom,
    children: Vec<Node>
}

fn read_tokens(iter: &mut std::iter::Peekable<regex::RegexSplits>) -> ParseResult {
    let mut node = Node{ atom: Atom::Nil, children: vec![] };

    match iter.next() {
        Some("(") => {
            loop {
                match iter.peek() {
                    Some(&")") => break,
                    Some(_) => (),
                    None => return Err(Error::Parser)
                }
                let token = try!(read_tokens(iter));
                node.children.push(token);
            }
        },
        Some(")") => return Err(Error::Parser),
        Some(x) => {
            node.atom = atom(x);
        },
        None => return Err(Error::Parser)
    }
    return Ok(node);
}

fn define(args: Vec<Atom>, env: &mut Env) -> AtomResult {
    if args.len() != 2 {
        return Err(Error::InvalidArguments);
    }

    if let Atom::Symbol(ref key) = args[0] {
        env.def_map.insert(key.clone(), args[1].clone());
        Ok(Atom::Nil)
    } else {
        Err(Error::InvalidArguments)
    }
}

fn get(args: Vec<Atom>, env: &Env) -> AtomResult {
    if args.len() != 1 {
        return Err(Error::InvalidArguments);
    }

    if let Atom::Symbol(ref s) = args[0] {
        if let Some(a) = env.def_map.get(s) {
            Ok(a.clone())
        } else {
            Ok(Atom::Nil)
        }
    } else {
        Err(Error::InvalidArguments)
    }
}

fn eval_args(node: &Node, env: &mut Env) -> Result<Vec<Atom>, Error> {
    let mut args: Vec<Atom> = vec![];

    for i in node.children.iter().skip(1) {
        let atom = try!(eval(i, env));
        args.push(atom);
    }
    return Ok(args);
}

fn eval(node: &Node, env: &mut Env) -> AtomResult {
    if let Atom::Nil = node.atom {
        if let Atom::Symbol(ref s) = node.children[0].atom {
            match s.as_ref() {
                "define" => {
                    let args = try!(eval_args(node, env));
                    return define(args, env);
                },
                "get" => {
                    return get( try!(eval_args(node, env)), env);
                 },
                "if" => {
                    let predicate = try!(eval(&node.children[1], env));
                    if let Atom::Boolean(b) = predicate {
                        if b {
                            return eval(&node.children[2], env);
                        } else if node.children.len() > 2 {
                            return eval(&node.children[3], env);
                        }
                    }
                    return Err(Error::InvalidArguments)
                },
                _ => ()
            };

            let args = try!(eval_args(node, env));

            if let Some(f) = env.func_map.get(s) {
                f(&args)
            } else {
                Ok(Atom::Nil)
            }
        } else {
            Err(Error::NotAFunction)
        }
    } else {
        Ok(node.atom.clone())
    }
}

fn parse(line: &str) -> ParseResult {
    tokenize(line)
}

fn repl() {
    println!("Rust Lisp!");
    let mut env = default_env();

    loop {
        match readline::readline(">") {
            Some(s) => {
                if let Ok(p) = parse(&s) {
                    match eval(&p, &mut env) {
                        Ok(r) => println!("{:?}", r),
                        Err(e) => println!("Error in evaluation: {:?}", e)
                    }
                } else {
                    println!("Error in parsing");
                }
            },
            None => {
                println!("Exiting...");
                break;
            }
        }
    }
}

pub fn main() {
    repl();
}

#[cfg(test)]
mod test {
    use super::Node;
    use super::Atom;
    use super::atom;
    use super::parse;

    fn make_atom_node(s: &str) -> Node {
        return Node{ atom: atom(s), children: vec![]};
    }

    #[test]
    fn atom_test() {
        assert_eq!(Atom::Integer(32), atom("32"));
        assert_eq!(Atom::Symbol("symbol".to_string()), atom("symbol"));
    }

    #[test]
    fn simple_read_tokens() {
        let x = parse("(+ 1 2)").unwrap();

        assert_eq!(Atom::Nil, x.atom);
        assert_eq!(3, x.children.len());

        let xx : Vec<Node> = vec![make_atom_node("+"), make_atom_node("1"), make_atom_node("2")];

        for pair in xx.iter().zip(x.children.iter()) {
            assert_eq!(pair.0, pair.1);
        }
    }

    #[test]
    fn nested_read_tokens() {
        let x = parse("(+ 1 (* 2 2))").unwrap();

        assert_eq!( atom("+"), x.children[0].atom);
        assert_eq!( atom("1"), x.children[1].atom);
        assert_eq!( Atom::Nil, x.children[2].atom);

        assert_eq!( 3, x.children[2].children.len());
        assert_eq!( atom("*"), x.children[2].children[0].atom);
        assert_eq!( atom("2"), x.children[2].children[1].atom);
        assert_eq!( atom("2"), x.children[2].children[2].atom);
    }

    #[test]
    #[should_panic]
    fn unmatched_bracket() {
        parse("(+ 1").unwrap();
    }

    #[test]
    #[should_panic]
    fn eof() {
         parse("").unwrap();
    }
}
<commit_msg>fix parser for things after brackets<commit_after>extern crate regex;
extern crate readline;

use regex::Regex;
use std::collections::HashMap;
use std::io::prelude::*;
use std::iter::Iterator;
use std::string::String;

#[derive(Debug, PartialEq, Clone)]
enum Atom {
    Integer(i64),
    Float(f64),
    Symbol(String),
    Boolean(bool),
    Nil
}

type Function = Fn(&Vec<Atom>) -> AtomResult;
type FunctionMap = HashMap<String, Box<Function>>;

#[derive(Debug)]
enum Error {
    UnexpectedType,
    Parser,
    InvalidArguments,
    NotAFunction,
    NotImplemented
}

type AtomResult = Result<Atom, Error>;
type ParseResult = Result<Node, Error>;

fn nil_func(_: &Vec<Atom>) -> AtomResult {
    return Ok(Atom::Nil);
}

struct Env {
    func_map: FunctionMap,
    def_map: HashMap<String, Atom>
}

fn add(v: &Vec<Atom>) -> AtomResult {
    let mut result = 0;

    for i in v {
        if let Atom::Integer(d) =  *i {
            result += d
        } else {
            return Err(Error::UnexpectedType)
        }
    }

    return Ok(Atom::Integer(result));
}

fn sub(v: &Vec<Atom>) -> AtomResult {
    if v.len() < 1 {
        return Err(Error::InvalidArguments)
    }

    let mut result = match v[0] {
        Atom::Integer(d) => d,
        _ => return Err(Error::UnexpectedType)
    };

    for i in v.iter().skip(1) {
        match *i {
            Atom::Integer(d) => result -= d,
            _ => return Err(Error::UnexpectedType)
        }
    }

    return Ok(Atom::Integer(result));
}

fn equals(args: &Vec<Atom>) -> AtomResult {
    let v = &args[0];

    for i in args.iter().skip(1) {
        if v != i {
            return Ok(Atom::Boolean(false));
        }
    }
    Ok(Atom::Boolean(true))
}

fn default_env() -> Env {
    let mut env = Env{func_map: HashMap::new(), def_map: HashMap::new()};

    env.func_map.insert("nil".to_string(), Box::new(nil_func) as Box<Function>);
    env.func_map.insert("+".to_string(), Box::new(add) as Box<Function>);
    env.func_map.insert("-".to_string(), Box::new(sub) as Box<Function>);
    env.func_map.insert("=".to_string(), Box::new(equals) as Box<Function>);

    return env;
}

fn tokenize(line: &str) -> ParseResult {
    let r = Regex::new(r"\s+").unwrap();
    let l = line.replace("(", " ( ").replace(")", " ) ").trim().to_string();
    let iter = r.split(&l);
    return read_tokens(&mut iter.peekable());
}

fn atom(token: &str) -> Atom {
    match token {
        "true" => return Atom::Boolean(true),
        "false" => return Atom::Boolean(false),
        _ => ()
    }

    let i = token.parse::<i64>();
     match i {
         Ok(v) => Atom::Integer(v),
         _ => {
             let f = token.parse::<f64>();
             match f {
                 Ok(x) => Atom::Float(x),
                 _ => Atom::Symbol(token.to_string())
             }
         },
     }
}

#[derive(Debug,PartialEq)]
struct Node {
    atom: Atom,
    children: Vec<Node>
}

fn read_tokens(iter: &mut std::iter::Peekable<regex::RegexSplits>) -> ParseResult {
    let mut node = Node{ atom: Atom::Nil, children: vec![] };

    match iter.next() {
        Some("(") => {
            loop {
                match iter.peek() {
                    Some(&")") => {
                        iter.next();
                        break
                    },
                    Some(_) => (),
                    None => return Err(Error::Parser)
                }
                let token = try!(read_tokens(iter));
                node.children.push(token);
            }
        },
        Some(")") => return Err(Error::Parser),
        Some(x) => {
            node.atom = atom(x);
        },
        None => return Err(Error::Parser)
    }
    return Ok(node);
}

fn define(args: Vec<Atom>, env: &mut Env) -> AtomResult {
    if args.len() != 2 {
        return Err(Error::InvalidArguments);
    }

    if let Atom::Symbol(ref key) = args[0] {
        env.def_map.insert(key.clone(), args[1].clone());
        Ok(Atom::Nil)
    } else {
        Err(Error::InvalidArguments)
    }
}

fn get(args: Vec<Atom>, env: &Env) -> AtomResult {
    if args.len() != 1 {
        return Err(Error::InvalidArguments);
    }

    if let Atom::Symbol(ref s) = args[0] {
        if let Some(a) = env.def_map.get(s) {
            Ok(a.clone())
        } else {
            Ok(Atom::Nil)
        }
    } else {
        Err(Error::InvalidArguments)
    }
}

fn eval_args(node: &Node, env: &mut Env) -> Result<Vec<Atom>, Error> {
    let mut args: Vec<Atom> = vec![];

    for i in node.children.iter().skip(1) {
        let atom = try!(eval(i, env));
        args.push(atom);
    }
    return Ok(args);
}

fn eval(node: &Node, env: &mut Env) -> AtomResult {
    if let Atom::Nil = node.atom {
        if let Atom::Symbol(ref s) = node.children[0].atom {
            match s.as_ref() {
                "define" => {
                    let args = try!(eval_args(node, env));
                    return define(args, env);
                },
                "get" => {
                    return get( try!(eval_args(node, env)), env);
                 },
                "if" => {
                    let predicate = try!(eval(&node.children[1], env));
                    if let Atom::Boolean(b) = predicate {
                        if b {
                            return eval(&node.children[2], env);
                        } else if node.children.len() > 2 {
                            return eval(&node.children[3], env);
                        }
                    }
                    return Err(Error::InvalidArguments)
                },
                _ => ()
            };

            let args = try!(eval_args(node, env));

            if let Some(f) = env.func_map.get(s) {
                f(&args)
            } else {
                Ok(Atom::Nil)
            }
        } else {
            Err(Error::NotAFunction)
        }
    } else {
        Ok(node.atom.clone())
    }
}

fn parse(line: &str) -> ParseResult {
    tokenize(line)
}

fn repl() {
    println!("Rust Lisp!");
    let mut env = default_env();

    loop {
        match readline::readline(">") {
            Some(s) => {
                if let Ok(p) = parse(&s) {
                    match eval(&p, &mut env) {
                        Ok(r) => println!("{:?}", r),
                        Err(e) => println!("Error in evaluation: {:?}", e)
                    }
                } else {
                    println!("Error in parsing");
                }
            },
            None => {
                println!("Exiting...");
                break;
            }
        }
    }
}

pub fn main() {
    repl();
}

#[cfg(test)]
mod test {
    use super::Node;
    use super::Atom;
    use super::atom;
    use super::parse;

    fn make_atom_node(s: &str) -> Node {
        return Node{ atom: atom(s), children: vec![]};
    }

    #[test]
    fn atom_test() {
        assert_eq!(Atom::Integer(32), atom("32"));
        assert_eq!(Atom::Symbol("symbol".to_string()), atom("symbol"));
    }

    #[test]
    fn simple_read_tokens() {
        let x = parse("(+ 1 2)").unwrap();

        assert_eq!(Atom::Nil, x.atom);
        assert_eq!(3, x.children.len());

        let xx : Vec<Node> = vec![make_atom_node("+"), make_atom_node("1"), make_atom_node("2")];

        for pair in xx.iter().zip(x.children.iter()) {
            assert_eq!(pair.0, pair.1);
        }
    }

    #[test]
    fn nested_read_tokens() {
        let x = parse("(+ 1 (* 2 2))").unwrap();

        assert_eq!( atom("+"), x.children[0].atom);
        assert_eq!( atom("1"), x.children[1].atom);
        assert_eq!( Atom::Nil, x.children[2].atom);

        assert_eq!( 3, x.children[2].children.len());
        assert_eq!( atom("*"), x.children[2].children[0].atom);
        assert_eq!( atom("2"), x.children[2].children[1].atom);
        assert_eq!( atom("2"), x.children[2].children[2].atom);
    }

    #[test]
    fn subexp_token_test() {
        let x = parse("(+ 1 (+ 2 3) 4)").unwrap();

        assert_eq!( atom("+"), x.children[0].atom);
        assert_eq!( atom("1"), x.children[1].atom);
        assert_eq!( Atom::Nil, x.children[2].atom);

        assert_eq!( 3, x.children[2].children.len());
        assert_eq!( atom("+"), x.children[2].children[0].atom);
        assert_eq!( atom("2"), x.children[2].children[1].atom);
        assert_eq!( atom("3"), x.children[2].children[2].atom);

        assert_eq!(atom("4"), x.children[3].atom);
    }

    #[test]
    #[should_panic]
    fn unmatched_bracket() {
        parse("(+ 1").unwrap();
    }

    #[test]
    #[should_panic]
    fn eof() {
         parse("").unwrap();
    }
}
<|endoftext|>


<commit_before>#![feature(slice_patterns)]
#![allow(dead_code)]

extern crate artificial_genome;
extern crate rand;

use artificial_genome::{Genome, ProteinRegulator, GeneNetwork, GeneNetworkState};
use artificial_genome::base4::{Base4, B0, B1};
// use std::str::FromStr;
use std::mem;
use std::borrow::Cow;
use std::io::{self, Write};

#[derive(Debug, Clone)]
struct Edge {
    src_node: usize,
    dst_node: usize,
    length: f32,

    // there is a structural node, and each time it is activated,
    // this count is increased.
    type_count: usize,

    // Every edge has it's own regulatory network state embedded.
    network_state: GeneNetworkState,
}

const EDGE_DIFFERENTIATION: usize = 0;
const EDGE_SPLIT: usize = 1;
const EDGE_DUPLICATE: usize = 2;
const EDGE_SWAP: usize = 3;
const EDGE_GROW: usize = 4;
const EDGE_SHRINK: usize = 5;
const EDGE_TYPE: usize = 6;

impl Edge {
    fn transition_state(&mut self, network: &GeneNetwork) {
        // create an empty new state.
        let mut new_state = network.new_state();

        for (i, node) in network.nodes().iter().enumerate() {
            // determine the new state of ```node``` (position i in new_state)
            if node.sum_edges(&self.network_state) > 0 {
                // node is enabled
                new_state.state.insert(i);
            }
        }
        self.network_state = new_state;
    }

    fn develop(&mut self,
               network: &GeneNetwork,
               next_node_id: &mut usize,
               new_edges: &mut Vec<Edge>) {

        // first perform the state transition.
        self.transition_state(network);

        // perform the actions of all active nodes in the gene network.

        // first node is used for differentiation.
        // in a split/duplicate node, this will be set 0 or 1 in the children.
        // starting from second node, the nodes are mapped to graph grammar rules.

        if self.network_state.state.contains(EDGE_SPLIT) {
            let new_node = *next_node_id;
            *next_node_id += 1;

            // differentiate
            let mut child_state = self.network_state.clone();
            child_state.state.set(EDGE_DIFFERENTIATION,
                                  !self.network_state.state.contains(EDGE_DIFFERENTIATION));

            let new_edge = Edge {
                src_node: new_node,
                dst_node: self.dst_node,
                length: 0.5 * self.length,
                network_state: child_state,
                type_count: self.type_count, /* XXX: start with 0 or with the count of the current edge? */
            };
            self.dst_node = new_node;
            self.length /= 2.0;
            new_edges.push(new_edge);
        }

        if self.network_state.state.contains(EDGE_DUPLICATE) {
            // differentiate
            let mut child_state = self.network_state.clone();
            child_state.state.set(EDGE_DIFFERENTIATION,
                                  !self.network_state.state.contains(EDGE_DIFFERENTIATION));

            let new_edge = Edge {
                src_node: self.dst_node,
                dst_node: self.src_node,
                length: self.length,
                network_state: child_state,
                type_count: self.type_count, /* XXX: start with 0 or with the count of the current edge? */
            };
            new_edges.push(new_edge);
        }

        if self.network_state.state.contains(EDGE_SWAP) {
            mem::swap(&mut self.dst_node, &mut self.src_node);
        }

        if self.network_state.state.contains(EDGE_GROW) {
            self.length *= 1.25;
        }

        if self.network_state.state.contains(EDGE_SHRINK) {
            self.length *= 0.75;
        }

        if self.network_state.state.contains(EDGE_TYPE) {
            self.type_count += 1;
        }
    }
}

#[derive(Debug)]
struct GraphBuilder {
    edges: Vec<Edge>,
    next_node_id: usize,
    network: GeneNetwork,
}

impl GraphBuilder {
    fn new(network: GeneNetwork, zygote: GeneNetworkState) -> GraphBuilder {
        let initial_edge = Edge {
            src_node: 0,
            dst_node: 1,
            length: 1.0,
            type_count: 0,
            network_state: zygote,
        };

        GraphBuilder {
            edges: vec![initial_edge],
            next_node_id: 2,
            network: network,
        }
    }

    // During the process, some edges will be added (split), some others will be modified.
    fn next(&mut self) {
        let mut new_edges = Vec::new();
        for edge in self.edges.iter_mut() {
            edge.develop(&self.network, &mut self.next_node_id, &mut new_edges);
        }
        println!("next_node_id: {}", self.next_node_id);
        println!("new edges: {:?}", new_edges);
        self.edges.extend(new_edges);
    }

    fn write_dot<W: Write>(&self, wr: &mut W) -> io::Result<()> {
        try!(writeln!(wr, "digraph artificial {{"));

        for edge in self.edges.iter() {
            try!(writeln!(wr,
                          "{} -> {} [weight={} label=\"{}\"]",
                          edge.src_node,
                          edge.dst_node,
                          edge.length,
                          edge.type_count));
        }

        try!(writeln!(wr, "}}"));

        Ok(())
    }
}

fn main() {
    use std::fs::File;
    // let genome = Genome::<Base4>::from_str("...11 _0320_23 <0101> T:0311 2...3 _1022_ 133 <0101> \
    // W:3213 121...")
    let mut rng = rand::thread_rng();

    let genome = Genome::<Base4>::random(&mut rng, 10 * 256);

    // let promoter = BaseString::<Base4>::from_str("0101").unwrap();
    let promoter = [B0, B1, B0, B1];

    let genes: Vec<_> = genome.iter_genes(&promoter, 4).collect();
    println!("{:?}", genes);

    let network = genome.construct_network(&promoter,
                                           4,
                                           &|product| {
                                               if product.last() == Some(&B0) {
                                                   // Inhibitor
                                                   ProteinRegulator::inhibit()
                                               } else {
                                                   // Inhibitor
                                                   ProteinRegulator::enhance()
                                               }
                                           });

    println!("{:#?}", network);

    let mut zygote = network.new_state();
    zygote.state.set(0, true);
    zygote.state.set(1, true);

    let mut gb = GraphBuilder::new(network, zygote);
    println!("{:#?}", gb);

    for i in 0..3 {
        gb.next();
    }
    println!("{:#?}", gb);

    gb.write_dot(&mut File::create("example1.dot").unwrap());

    // XXX: Convert each edge into a node and then connect these new nodes.
}
<commit_msg>Convert graph into "node" graph<commit_after>#![feature(slice_patterns)]
#![allow(dead_code)]

extern crate artificial_genome;
extern crate rand;

use artificial_genome::{Genome, ProteinRegulator, GeneNetwork, GeneNetworkState};
use artificial_genome::base4::{Base4, B0, B1};
use std::mem;
use std::io::{self, Write};

#[derive(Debug, Clone)]
struct Edge {
    src_node: usize,
    dst_node: usize,
    length: f32,

    // there is a structural node, and each time it is activated,
    // this count is increased.
    type_count: usize,

    // Every edge has it's own regulatory network state embedded.
    network_state: GeneNetworkState,
}

const EDGE_DIFFERENTIATION: usize = 0;
const EDGE_SPLIT: usize = 1;
const EDGE_DUPLICATE: usize = 2;
const EDGE_SWAP: usize = 3;
const EDGE_GROW: usize = 4;
const EDGE_SHRINK: usize = 5;
const EDGE_TYPE: usize = 6;

const RESIZE_FACTOR: f32 = 0.25;

impl Edge {
    fn transition_state(&mut self, network: &GeneNetwork) {
        // create an empty new state.
        let mut new_state = network.new_state();

        for (i, node) in network.nodes().iter().enumerate() {
            // determine the new state of ```node``` (position i in new_state)
            if node.sum_edges(&self.network_state) > 0 {
                // node is enabled
                new_state.state.insert(i);
            }
        }
        self.network_state = new_state;
    }

    fn develop(&mut self,
               network: &GeneNetwork,
               next_node_id: &mut usize,
               new_edges: &mut Vec<Edge>) {

        // first perform the state transition.
        self.transition_state(network);

        // perform the actions of all active nodes in the gene network.

        // first node is used for differentiation.
        // in a split/duplicate node, this will be set 0 or 1 in the children.
        // starting from second node, the nodes are mapped to graph grammar rules.

        if self.network_state.state.contains(EDGE_SPLIT) {
            let new_node = *next_node_id;
            *next_node_id += 1;

            // differentiate
            let mut child_state = self.network_state.clone();
            child_state.state.set(EDGE_DIFFERENTIATION,
                                  !self.network_state.state.contains(EDGE_DIFFERENTIATION));

            let new_edge = Edge {
                src_node: new_node,
                dst_node: self.dst_node,
                length: 0.5 * self.length,
                network_state: child_state,
                type_count: self.type_count, /* XXX: start with 0 or with the count of the current edge? */
            };
            self.dst_node = new_node;
            self.length /= 2.0;
            new_edges.push(new_edge);
        }

        if self.network_state.state.contains(EDGE_DUPLICATE) {
            // differentiate
            let mut child_state = self.network_state.clone();
            child_state.state.set(EDGE_DIFFERENTIATION,
                                  !self.network_state.state.contains(EDGE_DIFFERENTIATION));

            let new_edge = Edge {
                src_node: self.dst_node,
                dst_node: self.src_node,
                length: self.length,
                network_state: child_state,
                type_count: self.type_count, /* XXX: start with 0 or with the count of the current edge? */
            };
            new_edges.push(new_edge);
        }

        if self.network_state.state.contains(EDGE_SWAP) {
            mem::swap(&mut self.dst_node, &mut self.src_node);
        }

        if self.network_state.state.contains(EDGE_GROW) {
            self.length += RESIZE_FACTOR * self.length;
        }

        if self.network_state.state.contains(EDGE_SHRINK) {
            self.length -= RESIZE_FACTOR * self.length;
        }

        if self.network_state.state.contains(EDGE_TYPE) {
            self.type_count += 1;
        }
    }
}

struct NodeInfo {
    length: f32,
    type_count: usize,
}

#[derive(Debug)]
struct GraphBuilder {
    edges: Vec<Edge>,
    next_node_id: usize,
    network: GeneNetwork,
}

impl GraphBuilder {
    fn new(network: GeneNetwork, zygote: GeneNetworkState) -> GraphBuilder {
        let initial_edge = Edge {
            src_node: 0,
            dst_node: 1,
            length: 1.0,
            type_count: 0,
            network_state: zygote,
        };

        GraphBuilder {
            edges: vec![initial_edge],
            next_node_id: 2,
            network: network,
        }
    }

    // During the process, some edges will be added (split), some others will be modified.
    fn next(&mut self) {
        let mut new_edges = Vec::new();
        for edge in self.edges.iter_mut() {
            edge.develop(&self.network, &mut self.next_node_id, &mut new_edges);
        }
        println!("next_node_id: {}", self.next_node_id);
        println!("new edges: {:?}", new_edges);
        self.edges.extend(new_edges);
    }

    // The result of the GraphBuilder is a graph where every edge represents an element (either a
    // Neuron or a Synapse), while the nodes represent connection points (they carry purely
    // structural information). Transform this "edged" graph into a graph where the edges become
    // nodes.
    fn into_node_graph(&self) -> (Vec<NodeInfo>, Vec<(usize, usize)>) {
        let mut node_out_edges: Vec<Vec<usize>> = (0..self.next_node_id)
                                                      .map(|_| Vec::new())
                                                      .collect();
        let mut node_in_edges: Vec<Vec<usize>> = (0..self.next_node_id)
                                                     .map(|_| Vec::new())
                                                     .collect();
        let mut new_nodes: Vec<NodeInfo> = Vec::with_capacity(self.edges.len());
        let mut new_edges: Vec<(usize, usize)> = Vec::new();

        // every edge becomes a node.
        // note that ```i``` matches the indices  we use for ```new_nodes```.
        for (i, edge) in self.edges.iter().enumerate() {
            let j = new_nodes.len();
            new_nodes.push(NodeInfo {
                length: edge.length,
                type_count: edge.type_count,
            });
            assert!(i == j);
            node_out_edges[edge.src_node].push(i);
            node_in_edges[edge.dst_node].push(i);
        }
        assert!(new_nodes.len() == self.edges.len());

        for (i, edge) in self.edges.iter().enumerate() {
            // connect i with all outgoing edges of dst_node

            // if node_out_edges[edge.dst_node].is_empty() {
            // println!("Empty node");
            // XXX: turn it into an output-node
            // }
            // if node_in_edges[edge.src_node].is_empty() {
            // println!("Empty node");
            // XXX: turn it into an input-node
            // }
            //

            for &out_i in node_out_edges[edge.dst_node].iter() {
                new_edges.push((i, out_i));
            }
            // connect i with all incoming edges of src_node
            for &in_i in node_in_edges[edge.src_node].iter() {
                new_edges.push((in_i, i));
            }
        }

        (new_nodes, new_edges)
    }

    fn write_dot<W: Write>(&self, wr: &mut W) -> io::Result<()> {
        try!(writeln!(wr, "digraph artificial {{"));

        for edge in self.edges.iter() {
            try!(writeln!(wr,
                          "{} -> {} [weight={} label=\"{}\"]",
                          edge.src_node,
                          edge.dst_node,
                          edge.length,
                          edge.type_count));
        }

        try!(writeln!(wr, "}}"));

        Ok(())
    }
}

fn write_node_graph_dot<W: Write>(node_graph: (Vec<NodeInfo>, Vec<(usize, usize)>),
                                  wr: &mut W)
                                  -> io::Result<()> {
    try!(writeln!(wr, "digraph artificial {{"));

    let (new_nodes, new_edges) = node_graph;

    // the edges are nodes in this graph.
    for (i, node) in new_nodes.iter().enumerate() {
        try!(writeln!(wr,
                      "{} [label=\"{}:{:.2}:{}\"]",
                      i,
                      i,
                      node.length,
                      node.type_count));
    }

    // now connect them
    for &(src_edge, dst_edge) in new_edges.iter() {
        try!(writeln!(wr, "{} -> {}", src_edge, dst_edge));
    }

    try!(writeln!(wr, "}}"));

    Ok(())
}

fn main() {
    use std::fs::File;
    // let genome = Genome::<Base4>::from_str("...11 _0320_23 <0101> T:0311 2...3 _1022_ 133 <0101> \
    // W:3213 121...")
    let mut rng = rand::thread_rng();

    let genome = Genome::<Base4>::random(&mut rng, 5 * 256);

    // let promoter = BaseString::<Base4>::from_str("0101").unwrap();
    let promoter = [B0, B1, B0, B1];

    let genes: Vec<_> = genome.iter_genes(&promoter, 4).collect();
    println!("{:?}", genes);

    let network = genome.construct_network(&promoter,
                                           4,
                                           &|product| {
                                               if product.last() == Some(&B0) {
                                                   // Inhibitor
                                                   ProteinRegulator::inhibit()
                                               } else {
                                                   // Inhibitor
                                                   ProteinRegulator::enhance()
                                               }
                                           });

    println!("{:#?}", network);

    let mut zygote = network.new_state();
    zygote.state.set(0, true);
    // zygote.state.set(1, true);

    let mut gb = GraphBuilder::new(network, zygote);
    println!("{:#?}", gb);

    for _ in 0..3 {
        gb.next();
    }
    println!("{:#?}", gb);

    // gb.write_dot(&mut File::create("example1.dot").unwrap());

    let node_graph = gb.into_node_graph();
    write_node_graph_dot(node_graph, &mut File::create("example1_node.dot").unwrap()).unwrap();
}
<|endoftext|>