<commit_before>extern crate rustc_serialize;
extern crate sdl2;
extern crate sdl2_image;
extern crate sdl2_ttf;
extern crate toml;

use std::collections::BTreeMap;
use std::fs::{self, File};
use std::io::Read;
use std::path::Path;
use std::process::Command;
use std::time::Duration;

use sdl2::controller::{Axis, Button};
use sdl2::event::Event;
use sdl2::keyboard::Scancode;
use sdl2::mouse::Mouse;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};

use sdl2_image::LoadTexture;

struct Cursor {
    pub x: f32,
    pub y: f32,
    texture: Texture,
}

impl Cursor {
    pub fn new(renderer: &Renderer, image: &str) -> Cursor {
        Cursor {
            x: 0.0,
            y: 0.0,
            texture: renderer.load_texture(&Path::new(image)).unwrap(),
        }
    }

    pub fn set(&mut self, renderer: &Renderer, x: f32, y: f32) {
        self.x = x.max(0.0).min(renderer.output_size().unwrap().0 as f32);
        self.y = y.max(0.0).min(renderer.output_size().unwrap().1 as f32);
    }

    pub fn offset(&mut self, renderer: &Renderer, dx: f32, dy: f32) {
        let x = self.x + dx;
        let y = self.y + dy;
        self.set(renderer, x, y);
    }

    pub fn draw(&self, renderer: &mut Renderer) {
        renderer.copy(&self.texture, None, Rect::new((self.x - 26.0) as i32, (self.y - 4.0) as i32, 64, 64).unwrap());
    }
}

struct Font {
    font: sdl2_ttf::Font
}

impl Font {
    pub fn new(font: &str, size: i32) -> Font {
        Font {
            font: sdl2_ttf::Font::from_file(&Path::new(font), size).unwrap()
        }
    }

    pub fn render(&self, renderer: &Renderer, text: &str, color: Color) -> Texture {
        let surface = self.font.render(text, sdl2_ttf::blended(color)).unwrap();
        return renderer.create_texture_from_surface(&surface).unwrap();
    }
}

struct RomConfig {
    pub name: String,
    pub file: String,
    pub image: String,
}

struct Rom {
    name: Texture,
    image: Option<Texture>,
    config: RomConfig,
}

impl Rom {
    pub fn new(renderer: &Renderer, font: &Font, config: RomConfig) -> Rom {
        Rom {
            name: font.render(&renderer, &config.name, Color::RGBA(0, 0, 0, 255)),
            image: renderer.load_texture(&Path::new(&config.image)).ok(),
            config: config
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: u32, h: u32) {
        if let Some(ref image) = self.image {
            let query = image.query();
            let aspect = query.width as f32 / query.height as f32;
    		let w2 = (aspect * h as f32).min(w as f32) as u32;
    		let h2 = (w as f32 / aspect).min(h as f32) as u32;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + (h - h2) as i32/2;
            renderer.copy(&image, None, Rect::new(x2, y2, w2, h2).unwrap());
        }

        {
            let query = self.name.query();
            let w2 = query.width;
            let h2 = query.height;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + h as i32;
            renderer.copy(&self.name, None, Rect::new(x2, y2, w2, h2).unwrap());
        }
    }
}

#[derive(RustcDecodable)]
struct EmulatorConfig {
    pub name: String,
    pub image: String,
    pub roms: String,
    pub program: String,
    pub args: Vec<String>,
}

struct Emulator {
    name: Texture,
    image: Texture,
    roms: Vec<Rom>,
    config: EmulatorConfig
}

impl Emulator {
    pub fn new(renderer: &Renderer, font: &Font, config: EmulatorConfig) -> Emulator {
        let mut roms = Vec::new();
        if let Ok(read_dir) = fs::read_dir(&config.roms) {
            for entry_result in read_dir {
                if let Ok(entry) = entry_result {
                    if let Some(path) = entry.path().to_str() {
                        roms.push(Rom::new(renderer, font, RomConfig {
                            name: path.replace(&config.roms, "").trim_matches('/').to_string(),
                            file: path.to_string() + "/rom.bin",
                            image: path.to_string() + "/image.jpg"
                        }))
                    }
                }
            }
        }

        Emulator {
            name: font.render(&renderer, &config.name, Color::RGBA(0, 0, 0, 255)),
            image: renderer.load_texture(&Path::new(&config.image)).unwrap(),
            roms: roms,
            config: config
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: u32, h: u32) {
        {
            let query = self.image.query();
            let aspect = query.width as f32 / query.height as f32;
    		let w2 = (aspect * h as f32).min(w as f32) as u32;
    		let h2 = (w as f32 / aspect).min(h as f32) as u32;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + (h - h2) as i32/2;
            renderer.copy(&self.image, None, Rect::new(x2, y2, w2, h2).unwrap());
        }

        {
            let query = self.name.query();
            let w2 = query.width;
            let h2 = query.height;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + h as i32;
            renderer.copy(&self.name, None, Rect::new(x2, y2, w2, h2).unwrap());
        }
    }

    pub fn run(&self, rom: &Rom){
        let mut command = Command::new(&self.config.program);
        for arg in self.config.args.iter() {
            if arg == "%r" {
                command.arg(&rom.config.file);
            }else{
                command.arg(arg);
            }
        }

        println!("status: {}", command.status().unwrap());
    }
}

fn main(){
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let controller_subsystem = sdl_context.game_controller().unwrap();
    let _ttf_context = sdl2_ttf::init();

    let window = video_subsystem.window("emulition", 1024, 768)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    let mut controllers = Vec::new();
    for id in 0 .. controller_subsystem.num_joysticks().unwrap() {
        if controller_subsystem.is_game_controller(id) {
            controllers.push(controller_subsystem.open(id).unwrap());
        }
    }

    let mut cursor = Cursor::new(&renderer, "res/cursor.png");

    let font = Font::new("res/DroidSans.ttf", 24);

    let mut emulators = BTreeMap::new();

    if let Ok(mut file) = File::open("res/config.toml") {
        let mut toml = String::new();
        if let Ok(_) = file.read_to_string(&mut toml) {
            if let Some(parsed) = toml::Parser::new(&toml).parse() {
                for (key, value) in parsed {
                    if let Some(config) = toml::decode::<EmulatorConfig>(value) {
                        emulators.insert(key, Emulator::new(&renderer, &font, config));
                    }
                }
            }
        }
    }

    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut active_key: Option<String> = None;
    'running: loop {
        let mut forward = false;
        let mut backward = false;

        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { scancode: Some(Scancode::Escape), .. } => break 'running,
                Event::KeyDown { scancode: Some(Scancode::Return), .. } => forward = true,
                Event::KeyDown { scancode: Some(Scancode::Backspace), .. } => backward = true,
                Event::ControllerButtonDown { button: Button::A, .. } => forward = true,
                Event::ControllerButtonDown { button: Button::B, .. } => backward = true,
                Event::MouseButtonDown { mouse_btn: Mouse::Left, .. } => forward = true,
                Event::MouseButtonDown { mouse_btn: Mouse::Right, .. } => backward = true,
                Event::MouseMotion { x, y, .. } => cursor.set(&renderer, x as f32, y as f32),
                _ => {}
            }
        }

        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Left) {
            cursor.offset(&renderer, -8.0, 0.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Right) {
            cursor.offset(&renderer, 8.0, 0.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Up) {
            cursor.offset(&renderer, 0.0, -8.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Down) {
            cursor.offset(&renderer, 0.0, 8.0);
        }

        for controller in controllers.iter() {
            let dx = controller.axis(Axis::LeftX) as f32 / 32768.0;
            let dy = controller.axis(Axis::LeftY) as f32 / 32768.0;
            if (dx.powi(2) + dy.powi(2)).sqrt() > 0.2 {
                cursor.offset(&renderer, dx * 8.0, dy * 8.0);
            }

            if controller.button(Button::DPadLeft) {
                cursor.offset(&renderer, -8.0, 0.0);
            }
            if controller.button(Button::DPadRight) {
                cursor.offset(&renderer, 8.0, 0.0);
            }
            if controller.button(Button::DPadUp) {
                cursor.offset(&renderer, 0.0, -8.0);
            }
            if controller.button(Button::DPadDown) {
                cursor.offset(&renderer, 0.0, 8.0);
            }
        }

        renderer.set_draw_color(Color::RGB(255, 255, 255));
        renderer.clear();

        let mut x = 0;
        let mut y = 0;
        let s = renderer.output_size().unwrap().0 / 4;

        active_key = if let Some(ref key) = active_key {
            if let Some(emulator) = emulators.get_mut(key) {
                emulator.draw(&mut renderer, x, y, s, s - 64);

                x += s as i32;
                if x + s as i32 > renderer.output_size().unwrap().0 as i32 {
                    x = 0;
                    y += s as i32;
                }

                for rom in emulator.roms.iter() {
                    rom.draw(&mut renderer, x, y, s, s - 64);

                    if forward && cursor.x >= x as f32 && cursor.x < (x + s as i32) as f32 && cursor.y >= y as f32 && cursor.y < (y + s as i32) as f32 {
                        emulator.run(rom);
                    }

                    x += s as i32;
                    if x + s as i32 > renderer.output_size().unwrap().0 as i32 {
                        x = 0;
                        y += s as i32;
                    }
                }

                if backward {
                    None
                } else {
                    Some(key.clone())
                }
            } else {
                None
            }
        } else {
            let mut ret = None;

            for (key, emulator) in emulators.iter() {
                emulator.draw(&mut renderer, x, y, s, s - 64);

                if forward && cursor.x >= x as f32 && cursor.x < (x + s as i32) as f32 && cursor.y >= y as f32 && cursor.y < (y + s as i32) as f32 {
                    ret = Some(key.clone());
                }

                x += s as i32;
                if x + s as i32 > renderer.output_size().unwrap().0 as i32 {
                    x = 0;
                    y += s as i32;
                }
            }

            ret
        };

        cursor.draw(&mut renderer);

        renderer.present();

        std::thread::sleep(Duration::from_millis(1000/60));
    }
}
<commit_msg>Highlight selection<commit_after>extern crate rustc_serialize;
extern crate sdl2;
extern crate sdl2_image;
extern crate sdl2_ttf;
extern crate toml;

use std::collections::BTreeMap;
use std::fs::{self, File};
use std::io::Read;
use std::path::Path;
use std::process::Command;
use std::time::Duration;

use sdl2::controller::{Axis, Button};
use sdl2::event::Event;
use sdl2::keyboard::Scancode;
use sdl2::mouse::Mouse;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};

use sdl2_image::LoadTexture;

struct Cursor {
    pub x: f32,
    pub y: f32,
    texture: Texture,
}

impl Cursor {
    pub fn new(renderer: &Renderer, image: &str) -> Cursor {
        Cursor {
            x: 0.0,
            y: 0.0,
            texture: renderer.load_texture(&Path::new(image)).unwrap(),
        }
    }

    pub fn set(&mut self, renderer: &Renderer, x: f32, y: f32) {
        self.x = x.max(0.0).min(renderer.output_size().unwrap().0 as f32);
        self.y = y.max(0.0).min(renderer.output_size().unwrap().1 as f32);
    }

    pub fn offset(&mut self, renderer: &Renderer, dx: f32, dy: f32) {
        let x = self.x + dx;
        let y = self.y + dy;
        self.set(renderer, x, y);
    }

    pub fn draw(&self, renderer: &mut Renderer) {
        renderer.copy(&self.texture, None, Rect::new((self.x - 26.0) as i32, (self.y - 4.0) as i32, 64, 64).unwrap());
    }
}

struct Font {
    font: sdl2_ttf::Font
}

impl Font {
    pub fn new(font: &str, size: i32) -> Font {
        Font {
            font: sdl2_ttf::Font::from_file(&Path::new(font), size).unwrap()
        }
    }

    pub fn render(&self, renderer: &Renderer, text: &str, color: Color) -> Texture {
        let surface = self.font.render(text, sdl2_ttf::blended(color)).unwrap();
        return renderer.create_texture_from_surface(&surface).unwrap();
    }
}

struct RomConfig {
    pub name: String,
    pub file: String,
    pub image: String,
}

struct Rom {
    name: Texture,
    image: Option<Texture>,
    config: RomConfig,
}

impl Rom {
    pub fn new(renderer: &Renderer, font: &Font, config: RomConfig) -> Rom {
        Rom {
            name: font.render(&renderer, &config.name, Color::RGBA(0, 0, 0, 255)),
            image: renderer.load_texture(&Path::new(&config.image)).ok(),
            config: config
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: u32, h: u32) {
        if let Some(ref image) = self.image {
            let query = image.query();
            let aspect = query.width as f32 / query.height as f32;
    		let w2 = (aspect * h as f32).min(w as f32) as u32;
    		let h2 = (w as f32 / aspect).min(h as f32) as u32;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + (h - h2) as i32/2;
            renderer.copy(&image, None, Rect::new(x2, y2, w2, h2).unwrap());
        }

        {
            let query = self.name.query();
            let w2 = query.width;
            let h2 = query.height;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + h as i32;
            renderer.copy(&self.name, None, Rect::new(x2, y2, w2, h2).unwrap());
        }
    }
}

#[derive(RustcDecodable)]
struct EmulatorConfig {
    pub name: String,
    pub image: String,
    pub roms: String,
    pub program: String,
    pub args: Vec<String>,
}

struct Emulator {
    name: Texture,
    image: Texture,
    roms: Vec<Rom>,
    config: EmulatorConfig
}

impl Emulator {
    pub fn new(renderer: &Renderer, font: &Font, config: EmulatorConfig) -> Emulator {
        let mut roms = Vec::new();
        if let Ok(read_dir) = fs::read_dir(&config.roms) {
            for entry_result in read_dir {
                if let Ok(entry) = entry_result {
                    if let Some(path) = entry.path().to_str() {
                        roms.push(Rom::new(renderer, font, RomConfig {
                            name: path.replace(&config.roms, "").trim_matches('/').to_string(),
                            file: path.to_string() + "/rom.bin",
                            image: path.to_string() + "/image.jpg"
                        }))
                    }
                }
            }
        }

        Emulator {
            name: font.render(&renderer, &config.name, Color::RGBA(0, 0, 0, 255)),
            image: renderer.load_texture(&Path::new(&config.image)).unwrap(),
            roms: roms,
            config: config
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: u32, h: u32) {
        {
            let query = self.image.query();
            let aspect = query.width as f32 / query.height as f32;
    		let w2 = (aspect * h as f32).min(w as f32) as u32;
    		let h2 = (w as f32 / aspect).min(h as f32) as u32;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + (h - h2) as i32/2;
            renderer.copy(&self.image, None, Rect::new(x2, y2, w2, h2).unwrap());
        }

        {
            let query = self.name.query();
            let w2 = query.width;
            let h2 = query.height;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + h as i32;
            renderer.copy(&self.name, None, Rect::new(x2, y2, w2, h2).unwrap());
        }
    }

    pub fn run(&self, rom: &Rom){
        let mut command = Command::new(&self.config.program);
        for arg in self.config.args.iter() {
            if arg == "%r" {
                command.arg(&rom.config.file);
            }else{
                command.arg(arg);
            }
        }

        println!("status: {}", command.status().unwrap());
    }
}

#[derive(Clone, PartialEq)]
enum View {
    //Rom(String, usize),
    Emulator(String),
    Overview
}

fn main(){
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let controller_subsystem = sdl_context.game_controller().unwrap();
    let _ttf_context = sdl2_ttf::init();

    let window = video_subsystem.window("emulition", 1024, 768)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    let mut controllers = Vec::new();
    for id in 0 .. controller_subsystem.num_joysticks().unwrap() {
        if controller_subsystem.is_game_controller(id) {
            controllers.push(controller_subsystem.open(id).unwrap());
        }
    }

    let mut cursor = Cursor::new(&renderer, "res/cursor.png");

    let font = Font::new("res/DroidSans.ttf", 24);

    let mut emulators = BTreeMap::new();

    if let Ok(mut file) = File::open("res/config.toml") {
        let mut toml = String::new();
        if let Ok(_) = file.read_to_string(&mut toml) {
            if let Some(parsed) = toml::Parser::new(&toml).parse() {
                for (key, value) in parsed {
                    if let Some(config) = toml::decode::<EmulatorConfig>(value) {
                        emulators.insert(key, Emulator::new(&renderer, &font, config));
                    }
                }
            }
        }
    }

    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut view = View::Overview;
    'running: loop {
        let mut forward = false;
        let mut backward = false;

        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { scancode: Some(Scancode::Escape), .. } => break 'running,
                Event::KeyDown { scancode: Some(Scancode::Return), .. } => forward = true,
                Event::KeyDown { scancode: Some(Scancode::Backspace), .. } => backward = true,
                Event::ControllerButtonDown { button: Button::A, .. } => forward = true,
                Event::ControllerButtonDown { button: Button::B, .. } => backward = true,
                Event::MouseButtonDown { mouse_btn: Mouse::Left, .. } => forward = true,
                Event::MouseButtonDown { mouse_btn: Mouse::Right, .. } => backward = true,
                Event::MouseMotion { x, y, .. } => cursor.set(&renderer, x as f32, y as f32),
                _ => {}
            }
        }

        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Left) {
            cursor.offset(&renderer, -8.0, 0.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Right) {
            cursor.offset(&renderer, 8.0, 0.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Up) {
            cursor.offset(&renderer, 0.0, -8.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Down) {
            cursor.offset(&renderer, 0.0, 8.0);
        }

        for controller in controllers.iter() {
            let dx = controller.axis(Axis::LeftX) as f32 / 32768.0;
            let dy = controller.axis(Axis::LeftY) as f32 / 32768.0;
            if (dx.powi(2) + dy.powi(2)).sqrt() > 0.2 {
                cursor.offset(&renderer, dx * 8.0, dy * 8.0);
            }

            if controller.button(Button::DPadLeft) {
                cursor.offset(&renderer, -8.0, 0.0);
            }
            if controller.button(Button::DPadRight) {
                cursor.offset(&renderer, 8.0, 0.0);
            }
            if controller.button(Button::DPadUp) {
                cursor.offset(&renderer, 0.0, -8.0);
            }
            if controller.button(Button::DPadDown) {
                cursor.offset(&renderer, 0.0, 8.0);
            }
        }

        renderer.set_draw_color(Color::RGB(255, 255, 255));
        renderer.clear();

        let mut x = 0;
        let mut y = 0;
        let s = renderer.output_size().unwrap().0 / 4;

        let mut new_view = view.clone();
        match view {
            View::Emulator(ref key) => {
                if let Some(emulator) = emulators.get_mut(key) {
                    emulator.draw(&mut renderer, x + 8, y + 8, s - 16, s - 64);

                    x += s as i32;
                    if x + s as i32 > renderer.output_size().unwrap().0 as i32 {
                        x = 0;
                        y += s as i32;
                    }

                    for rom in emulator.roms.iter() {
                        if cursor.x >= x as f32 && cursor.x < (x + s as i32) as f32 && cursor.y >= y as f32 && cursor.y < (y + s as i32) as f32 {
                            renderer.set_draw_color(Color::RGBA(192, 192, 192, 255));
                            renderer.fill_rect(Rect::new(x, y, s, s).unwrap().unwrap());

                            if forward {
                                emulator.run(rom);
                            }
                        }

                        rom.draw(&mut renderer, x + 8, y + 8, s - 16, s - 64);

                        x += s as i32;
                        if x + s as i32 > renderer.output_size().unwrap().0 as i32 {
                            x = 0;
                            y += s as i32;
                        }
                    }

                    if backward {
                        new_view = View::Overview
                    }
                } else {
                    new_view = View::Overview
                }
            },
            View::Overview => {
                for (key, emulator) in emulators.iter() {
                    if cursor.x >= x as f32 && cursor.x < (x + s as i32) as f32 && cursor.y >= y as f32 && cursor.y < (y + s as i32) as f32 {
                        renderer.set_draw_color(Color::RGBA(192, 192, 192, 255));
                        renderer.fill_rect(Rect::new(x, y, s, s).unwrap().unwrap());

                        if forward {
                            new_view = View::Emulator(key.clone());
                        }
                    }

                    emulator.draw(&mut renderer, x + 8, y + 8, s - 16, s - 64);

                    x += s as i32;
                    if x + s as i32 > renderer.output_size().unwrap().0 as i32 {
                        x = 0;
                        y += s as i32;
                    }
                }
            }
        };

        cursor.draw(&mut renderer);

        renderer.present();

        if new_view != view {
            view = new_view;
        } else {
            std::thread::sleep(Duration::from_millis(1000/60));
        }
    }
}
<|endoftext|>


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

extern crate artificial_genome;
extern crate rand;
extern crate fixedbitset;

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

#[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 NodeGraph {
    nodes: Vec<NodeInfo>,
    edges: BTreeSet<(usize, usize)>,
}

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

#[derive(Debug)]
struct FoundPath {
    target_node: usize,
    length: f32,
}

// Find all paths from current_node to connected nodes.
fn path_finder(current_node: usize,
               current_length: f32,
               neighborhood: &Vec<Vec<usize>>,
               lengths: &Vec<f32>,
               targets: &BTreeSet<usize>,
               visited: &mut FixedBitSet,
               paths: &mut Vec<FoundPath>) {
    for &ni in neighborhood[current_node].iter() {
        if targets.contains(&ni) {
            // we found a target
            paths.push(FoundPath {
                target_node: ni,
                length: current_length,
            });
        } else if !visited.contains(ni) {
            // the node ``ni`` is not a target node and we haven't
            // visited yet.

            // we haven't visited ```ni``` yet. recurse down
            visited.set(ni, true);
            path_finder(ni,
                        current_length + lengths[ni],
                        neighborhood,
                        lengths,
                        targets,
                        visited,
                        paths);
            visited.set(ni, false);
        }
    }
}

#[derive(Debug)]
struct StructuredNode {
    length: f32,
    type_count: usize,
    connections: Vec<FoundPath>,
}

// There are two types of nodes. Processing nodes (e.g. Neuron) or
// connecting nodes (e.g. Synapses). Keep the processing nodes as
// nodes in the graph, while the connecting nodes become edges.
#[derive(Debug)]
struct StructuredGraph {
    nodes: BTreeMap<usize, StructuredNode>,
}

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

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

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

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

        Ok(())
    }

    fn into_structured_graph(&self, type_processing: usize) -> StructuredGraph {
        // determine max type_count.
        // lets say every type_count >= 3 is a neuron for now.
        // everything else is a synapse
        // If two synapses are directly connected to each other,
        // treat them as one synapse with the summation of it's lengths.
        // XXX: remove synapses which are too long.
        // If two neurons are next to each other, what to do?
        // a) Treat them as one big neuron
        // b) Connect them with a minimum synapse?
        // We do b) for now, because it's easier.

        let mut neighbors: BTreeMap<usize, BTreeSet<usize>> = BTreeMap::new();
        for &(src, dst) in self.edges.iter() {
            neighbors.entry(src).or_insert_with(|| BTreeSet::new()).insert(dst);
        }

        let mut processing_nodes: BTreeSet<usize> = BTreeSet::new();

        for (i, node) in self.nodes.iter().enumerate() {
            if node.type_count >= type_processing {
                processing_nodes.insert(i);
            }
        }

        let neighborhood: Vec<Vec<usize>> = self.nodes
                                                .iter()
                                                .enumerate()
                                                .map(|(i, _)| {
                                                    neighbors.get(&i)
                                                             .map(|v| v.iter().cloned().collect())
                                                             .unwrap_or(Vec::new())
                                                })
                                                .collect();

        let lengths: Vec<f32> = self.nodes
                                    .iter()
                                    .map(|n| n.length)
                                    .collect();

        // now follow all possible directed paths from each processing_node, until
        // we arrive at another processing_node.
        // (Or repetetively merge consecutive elements which have same type).

        let mut visited = FixedBitSet::with_capacity(self.nodes.len());

        let mut g = StructuredGraph { nodes: BTreeMap::new() };

        for &pnode in processing_nodes.iter() {
            // find all processing elements connected either directly or through intermediate
            // synapses.
            let mut snode = StructuredNode {
                length: self.nodes[pnode].length,
                type_count: self.nodes[pnode].type_count,
                connections: Vec::new(),
            };

            visited.clear();
            path_finder(pnode,
                        0.0,
                        &neighborhood,
                        &lengths,
                        &processing_nodes,
                        &mut visited,
                        &mut snode.connections);

            g.nodes.insert(pnode, snode);
        }

        g
    }
}

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

        for (&i, node) in self.nodes.iter() {
            if node.connections.is_empty() {
                // XXX
                continue;
            }
            try!(writeln!(wr,
                          "{} [label=\"{} {:.2} {}\"]",
                          i,
                          i,
                          node.length,
                          node.type_count));
        }

        // now connect them
        for (src, node) in self.nodes.iter() {
            for dst in node.connections.iter() {
                if dst.length == 0.0 {
                    // XXX
                    continue;
                }
                try!(writeln!(wr,
                              "{} -> {} [weight={} label=\"{}\"]",
                              src,
                              dst.target_node,
                              dst.length,
                              dst.length));
            }
        }

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

        Ok(())
    }
}

#[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) -> NodeGraph {
        let mut node_graph = NodeGraph {
            nodes: Vec::with_capacity(self.edges.len()),
            edges: BTreeSet::new(),
        };

        let mut node_out_edges: Vec<Vec<usize>> = (0..self.next_node_id)
                                                      .map(|_| Vec::new())
                                                      .collect();

        // 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 = node_graph.nodes.len();
            node_graph.nodes.push(NodeInfo {
                length: edge.length,
                type_count: edge.type_count,
            });
            assert!(i == j);
            node_out_edges[edge.src_node].push(i);
        }
        assert!(node_graph.nodes.len() == self.edges.len());

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

            for &out_i in node_out_edges[edge.dst_node].iter() {
                node_graph.edges.insert((i, out_i));
            }
        }

        node_graph
    }

    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.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 _ in 0..5 {
        gb.next();
    }
    println!("{:#?}", gb);

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

    let node_graph = gb.into_node_graph();
    node_graph.write_dot(&mut File::create("example1_node.dot").unwrap()).unwrap();
    let g = node_graph.into_structured_graph(1);
    println!("g: {:?}", g);
    g.write_dot(&mut File::create("example1_struct.dot").unwrap()).unwrap();
}
<commit_msg>Node compete against each other<commit_after>#![feature(slice_patterns)]
#![allow(dead_code)]

extern crate artificial_genome;
extern crate rand;
extern crate fixedbitset;

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

#[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 NodeGraph {
    nodes: Vec<NodeInfo>,
    edges: BTreeSet<(usize, usize)>,
}

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

#[derive(Debug)]
struct FoundPath {
    target_node: usize,
    length: f32,
}

// Find all paths from current_node to connected nodes.
fn path_finder(current_node: usize,
               current_length: f32,
               neighborhood: &Vec<Vec<usize>>,
               lengths: &Vec<f32>,
               targets: &BTreeSet<usize>,
               visited: &mut FixedBitSet,
               paths: &mut Vec<FoundPath>) {
    for &ni in neighborhood[current_node].iter() {
        if targets.contains(&ni) {
            // we found a target
            paths.push(FoundPath {
                target_node: ni,
                length: current_length,
            });
        } else if !visited.contains(ni) {
            // the node ``ni`` is not a target node and we haven't
            // visited yet.

            // we haven't visited ```ni``` yet. recurse down
            visited.set(ni, true);
            path_finder(ni,
                        current_length + lengths[ni],
                        neighborhood,
                        lengths,
                        targets,
                        visited,
                        paths);
            visited.set(ni, false);
        }
    }
}

#[derive(Debug)]
struct StructuredNode {
    length: f32,
    type_count: usize,
    connections: Vec<FoundPath>,
}

// There are two types of nodes. Processing nodes (e.g. Neuron) or
// connecting nodes (e.g. Synapses). Keep the processing nodes as
// nodes in the graph, while the connecting nodes become edges.
#[derive(Debug)]
struct StructuredGraph {
    nodes: BTreeMap<usize, StructuredNode>,
}

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

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

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

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

        Ok(())
    }

    fn into_structured_graph(&self, type_processing: usize) -> StructuredGraph {
        // determine max type_count.
        // lets say every type_count >= 3 is a neuron for now.
        // everything else is a synapse
        // If two synapses are directly connected to each other,
        // treat them as one synapse with the summation of it's lengths.
        // XXX: remove synapses which are too long.
        // If two neurons are next to each other, what to do?
        // a) Treat them as one big neuron
        // b) Connect them with a minimum synapse?
        // We do b) for now, because it's easier.

        let mut neighbors: BTreeMap<usize, BTreeSet<usize>> = BTreeMap::new();
        for &(src, dst) in self.edges.iter() {
            neighbors.entry(src).or_insert_with(|| BTreeSet::new()).insert(dst);
        }

        let mut processing_nodes: BTreeSet<usize> = BTreeSet::new();

        for (i, node) in self.nodes.iter().enumerate() {
            if node.type_count >= type_processing {
                processing_nodes.insert(i);
            }
        }

        let neighborhood: Vec<Vec<usize>> = self.nodes
                                                .iter()
                                                .enumerate()
                                                .map(|(i, _)| {
                                                    neighbors.get(&i)
                                                             .map(|v| v.iter().cloned().collect())
                                                             .unwrap_or(Vec::new())
                                                })
                                                .collect();

        let lengths: Vec<f32> = self.nodes
                                    .iter()
                                    .map(|n| n.length)
                                    .collect();

        // now follow all possible directed paths from each processing_node, until
        // we arrive at another processing_node.
        // (Or repetetively merge consecutive elements which have same type).

        let mut visited = FixedBitSet::with_capacity(self.nodes.len());

        let mut g = StructuredGraph { nodes: BTreeMap::new() };

        for &pnode in processing_nodes.iter() {
            // find all processing elements connected either directly or through intermediate
            // synapses.
            let mut snode = StructuredNode {
                length: self.nodes[pnode].length,
                type_count: self.nodes[pnode].type_count,
                connections: Vec::new(),
            };

            visited.clear();
            path_finder(pnode,
                        0.0,
                        &neighborhood,
                        &lengths,
                        &processing_nodes,
                        &mut visited,
                        &mut snode.connections);

            g.nodes.insert(pnode, snode);
        }

        g
    }
}

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

        for (&i, node) in self.nodes.iter() {
            if node.connections.is_empty() {
                // XXX
                continue;
            }
            try!(writeln!(wr,
                          "{} [label=\"{} {:.2} {}\"]",
                          i,
                          i,
                          node.length,
                          node.type_count));
        }

        // now connect them
        for (src, node) in self.nodes.iter() {
            for dst in node.connections.iter() {
                if dst.length == 0.0 {
                    // XXX
                    continue;
                }
                try!(writeln!(wr,
                              "{} -> {} [weight={} label=\"{}\"]",
                              src,
                              dst.target_node,
                              dst.length,
                              dst.length));
            }
        }

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

        Ok(())
    }
}

#[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) -> NodeGraph {
        // only keep one directed edge between each pair of nodes.
        // keep the edge with highest type_count.

        let mut e: Vec<Edge> = Vec::new();
        {
            // stores (src_node, dst_node) => edge idx
            let mut selected_edges: BTreeMap<(usize, usize), usize> = BTreeMap::new();
            for (i, edge) in self.edges.iter().enumerate() {
                let idx_ref = selected_edges.entry((edge.src_node, edge.dst_node)).or_insert(i);
                let old = &self.edges[*idx_ref];
                if edge.type_count > old.type_count {
                    *idx_ref = i;
                }
            }
            for (_, &edge_idx) in selected_edges.iter() {
                e.push(self.edges[edge_idx].clone());
            }
        }

        let mut node_graph = NodeGraph {
            nodes: Vec::with_capacity(e.len()),
            edges: BTreeSet::new(),
        };

        let mut node_out_edges: BTreeMap<usize, Vec<usize>> = BTreeMap::new();

        // every edge becomes a node.
        // note that ```i``` matches the indices  we use for ```new_nodes```.
        for (i, edge) in e.iter().enumerate() {
            let j = node_graph.nodes.len();
            node_graph.nodes.push(NodeInfo {
                length: edge.length,
                type_count: edge.type_count,
            });
            assert!(i == j);
            node_out_edges.entry(edge.src_node).or_insert(Vec::new()).push(i);
        }
        assert!(node_graph.nodes.len() == e.len());

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

            if let Some(list) = node_out_edges.get(&edge.dst_node) {
                for &out_i in list.iter() {
                    node_graph.edges.insert((i, out_i));
                }
            }
        }

        node_graph
    }

    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.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 _ in 0..5 {
        gb.next();
    }
    println!("{:#?}", gb);

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

    let node_graph = gb.into_node_graph();
    node_graph.write_dot(&mut File::create("example1_node.dot").unwrap()).unwrap();
    let g = node_graph.into_structured_graph(1);
    println!("g: {:?}", g);
    g.write_dot(&mut File::create("example1_struct.dot").unwrap()).unwrap();
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 - 2018 Markus Kohlhase <mail@markus-kohlhase.de>

#![feature(plugin, custom_derive, test, transpose_result)]
#![plugin(rocket_codegen)]
#![allow(proc_macro_derive_resolution_fallback)]

extern crate chrono;

extern crate clap;

extern crate csv;

#[macro_use]
extern crate diesel;

#[macro_use]
extern crate diesel_migrations;

extern crate dotenv;

extern crate env_logger;

extern crate fast_chemail;

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate pwhash;

#[macro_use]
extern crate quick_error;

extern crate quoted_printable;

extern crate regex;

extern crate rocket;

extern crate rocket_contrib;

#[macro_use]
extern crate serde;

extern crate serde_json;

extern crate toml;

extern crate url;

extern crate uuid;

#[cfg(test)]
extern crate test;

mod adapters;
mod core;
mod infrastructure;
mod ports;

fn main() {
    env_logger::init();
    ports::cli::run();
}
<commit_msg>remove unnecessary 'extern crate' statementes<commit_after>// Copyright (c) 2015 - 2018 Markus Kohlhase <mail@markus-kohlhase.de>

#![feature(plugin, custom_derive, test, transpose_result)]
#![plugin(rocket_codegen)]
#![allow(proc_macro_derive_resolution_fallback)]

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_migrations;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate serde;
#[cfg(test)]
extern crate test;

mod adapters;
mod core;
mod infrastructure;
mod ports;

fn main() {
    env_logger::init();
    ports::cli::run();
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

const COMMITTER_ERROR_MESSAGE: &'static str = r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    if github::can_release(&config) {
        logger::stdout("Creating GitHub release");
        github::release(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
    } else {
        logger::stdout("Project not hosted on GitHub. Skipping release step");
    }
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn write_changelog(repository_path: &str, version: &Version, new_version: &str) {
    logger::stdout("Writing Changelog");
    changelog::write(repository_path, &version.to_string(), &new_version)
        .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn package_crate(config: &config::Config, repository_path: &str, new_version: &str) {
    if config.release_mode {
        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }
    }

    git::commit_files(&config, &new_version)
        .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

    logger::stdout("Package crate");
    if !cargo::package(repository_path) {
        print_exit!("`cargo package` failed. See above for the cargo error message.");
    }
}

fn get_repo(repository_path: &str) -> git2::Repository {
    match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    }
}

fn get_repository_path(args: &Args) -> String {
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is
                                                            not a directory"));
    let repo_path = path.to_str().unwrap_or_else(|| print_exit!("Path is not valid unicode"));
    repo_path.to_string()
}

fn get_signature<'a>(repository_path: String) -> git2::Signature<'a> {
    let repo = get_repo(&repository_path);
    let signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(COMMITTER_ERROR_MESSAGE);
                process::exit(1);
            }
    };

    signature.to_owned()
}

fn get_user_and_repo(repository_path: &str) -> (Option<String>, Option<String>) {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            let (user, repo_name) = user_repo_from_url(&url)
                .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));

            (Some(user), Some(repo_name))
        },
        Err(err) => {
            logger::warn(format!("Could not determine the origin remote url: {:?}", err));
            logger::warn("semantic-rs can't push changes or create a release on GitHub");
            (None, None)
        }
    }
}

fn get_github_token(repository_path: &str) -> Option<String> {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            if github::is_github_url(&url) {
                match env::var("GH_TOKEN") {
                    Ok(token) => Some(token),
                    Err(_) => None
                }
            } else {
                None
            }
        },
        Err(_) => None
    }
}

fn get_cargo_token() -> Option<String> {
    match env::var("CARGO_TOKEN") {
        Ok(token) => Some(token),
        Err(_) => None
    }
}

fn validate_config(config: &config::Config) {
    if config.write_mode && config.release_mode {
        //When we want to write and release, certain properties must be in place
        if config.user.is_none() {
            print_exit!("User is not present");
        }
        if config.repository_name.is_none() {
            print_exit!("Repository name is not present");
        }

        if config.gh_token.is_none() {
            print_exit!("GH_TOKEN environment variable is not set");
        }

        if config.cargo_token.is_none() {
            print_exit!("CARGO_TOKEN environment variable is not set");
        }
    }
}

fn assemble_configuration(args: Args) -> config::Config {
    let mut config_builder = ConfigBuilder::new();

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);
    let repository_path = get_repository_path(&args);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch.clone());
    config_builder.repository_path(repository_path.clone());
    config_builder.signature(get_signature(repository_path.clone()));
    let (user, repo) = get_user_and_repo(&repository_path);
    if user.is_some() {
        config_builder.user(user.unwrap());
    }
    if repo.is_some() {
        config_builder.repository_name(repo.unwrap());
    }
    let gh_token = get_github_token(&repository_path);
    if gh_token.is_some() {
        config_builder.gh_token(gh_token.unwrap());
    }

    let cargo_token = get_cargo_token();
    if cargo_token.is_some() {
        config_builder.cargo_token(cargo_token.unwrap());
    }
    config_builder.repository(get_repo(&repository_path));
    config_builder.build()
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");
    println!("semantic.rs 🚀");

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let config = assemble_configuration(args);
    validate_config(&config);
}
<commit_msg>refactor: Move old code back in place<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

const COMMITTER_ERROR_MESSAGE: &'static str = r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    if github::can_release(&config) {
        logger::stdout("Creating GitHub release");
        github::release(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
    } else {
        logger::stdout("Project not hosted on GitHub. Skipping release step");
    }
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn write_changelog(repository_path: &str, version: &Version, new_version: &str) {
    logger::stdout("Writing Changelog");
    changelog::write(repository_path, &version.to_string(), &new_version)
        .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn package_crate(config: &config::Config, repository_path: &str, new_version: &str) {
    if config.release_mode {
        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }
    }

    git::commit_files(&config, &new_version)
        .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

    logger::stdout("Package crate");
    if !cargo::package(repository_path) {
        print_exit!("`cargo package` failed. See above for the cargo error message.");
    }
}

fn get_repo(repository_path: &str) -> git2::Repository {
    match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    }
}

fn get_repository_path(args: &Args) -> String {
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is
                                                            not a directory"));
    let repo_path = path.to_str().unwrap_or_else(|| print_exit!("Path is not valid unicode"));
    repo_path.to_string()
}

fn get_signature<'a>(repository_path: String) -> git2::Signature<'a> {
    let repo = get_repo(&repository_path);
    let signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(COMMITTER_ERROR_MESSAGE);
                process::exit(1);
            }
    };

    signature.to_owned()
}

fn get_user_and_repo(repository_path: &str) -> (Option<String>, Option<String>) {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            let (user, repo_name) = user_repo_from_url(&url)
                .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));

            (Some(user), Some(repo_name))
        },
        Err(err) => {
            logger::warn(format!("Could not determine the origin remote url: {:?}", err));
            logger::warn("semantic-rs can't push changes or create a release on GitHub");
            (None, None)
        }
    }
}

fn get_github_token(repository_path: &str) -> Option<String> {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            if github::is_github_url(&url) {
                match env::var("GH_TOKEN") {
                    Ok(token) => Some(token),
                    Err(_) => None
                }
            } else {
                None
            }
        },
        Err(_) => None
    }
}

fn get_cargo_token() -> Option<String> {
    match env::var("CARGO_TOKEN") {
        Ok(token) => Some(token),
        Err(_) => None
    }
}

fn validate_config(config: &config::Config) {
    if config.write_mode && config.release_mode {
        //When we want to write and release, certain properties must be in place
        if config.user.is_none() {
            print_exit!("User is not present");
        }
        if config.repository_name.is_none() {
            print_exit!("Repository name is not present");
        }

        if config.gh_token.is_none() {
            print_exit!("GH_TOKEN environment variable is not set");
        }

        if config.cargo_token.is_none() {
            print_exit!("CARGO_TOKEN environment variable is not set");
        }
    }
}

fn assemble_configuration(args: Args) -> config::Config {
    let mut config_builder = ConfigBuilder::new();

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);
    let repository_path = get_repository_path(&args);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch.clone());
    config_builder.repository_path(repository_path.clone());
    config_builder.signature(get_signature(repository_path.clone()));
    let (user, repo) = get_user_and_repo(&repository_path);
    if user.is_some() {
        config_builder.user(user.unwrap());
    }
    if repo.is_some() {
        config_builder.repository_name(repo.unwrap());
    }
    let gh_token = get_github_token(&repository_path);
    if gh_token.is_some() {
        config_builder.gh_token(gh_token.unwrap());
    }

    let cargo_token = get_cargo_token();
    if cargo_token.is_some() {
        config_builder.cargo_token(cargo_token.unwrap());
    }
    config_builder.repository(get_repo(&repository_path));
    config_builder.build()
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");
    println!("semantic.rs 🚀");

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let config = assemble_configuration(args);
    validate_config(&config);

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
                Err(travis_after_all::Error::FailedBuilds) => {
                    print_exit!("Some builds failed. Stopping here.");
                },
                Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if config.write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
            None => {
                logger::stdout("No version bump. Nothing to do.");
                process::exit(0);
            }
    };

    if !config.write_mode {
        let changelog = generate_changelog(&config.repository_path, &version, &new_version);
        print_changelog(&changelog);
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(&config.repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        write_changelog(&config.repository_path, &version, &new_version);
        package_crate(&config, &config.repository_path, &new_version);

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(&config.repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            push_to_github(&config, &tag_name);
            release_on_github(&config, &tag_message, &tag_name);
            release_on_cratesio(&config);
            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<|endoftext|>