<commit_before>extern crate regex;
use regex::Regex;

use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;
extern crate time;
use time::Tm;
use time::strptime;

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Request {
    id: i32,
    time: Tm,
    url: String,
}

pub struct Response {

}

fn open_logfile(path: &str) -> Result<Vec<Request>, io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();

    for line in f.lines() {
        let r = try!(parse_request_line(line.unwrap()));
        println!("{:?}", r);
        requests.push(r)
    }

    Ok(requests)
}

pub fn parse_request_line(log_line: String) -> Result<Request, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let url = parts[5];

    Ok(Request {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        url: url.to_string()
    })
}

fn main() {
    let requests = open_logfile("src/test/simple-1.log");

    match requests {
        Ok(requests) => println!("So many: {}", requests.len()),
        Err(e) => println!("Could not parse, error {}", e),
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    extern crate time;
    use time::strptime;

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();

        let expected = Request {
            id: 2,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = parse_request_line(line);

        assert_eq!(result.unwrap(), expected)
    }
}
<commit_msg>Parse response lines<commit_after>extern crate regex;
use regex::Regex;

use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;
extern crate time;
use time::Tm;
use time::strptime;
use time::Duration;

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Request {
    id: i32,
    time: Tm,
    url: String,
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Response {
    id: i32,
    time: Tm,
    mime_type: String,
    response_time: Duration,
}

fn open_logfile(path: &str) -> Result<Vec<Request>, io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();

    for line in f.lines() {
        let r = try!(parse_request_line(line.unwrap()));
        println!("{:?}", r);
        requests.push(r)
    }

    Ok(requests)
}

pub fn parse_request_line(log_line: String) -> Result<Request, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let url = parts[5];

    Ok(Request {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        url: url.to_string()
    })
}

pub fn parse_response_line(log_line: String) -> Result<Response, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let response_time = parts[parts.len()-1];
    let mime_type = parts[5];

    Ok(Response {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        response_time: Duration::milliseconds(response_time[0..response_time.len()-2].parse().unwrap()),
        mime_type: mime_type.to_string()
    })
}

fn main() {
    let requests = open_logfile("src/test/simple-1.log");

    match requests {
        Ok(requests) => println!("So many: {}", requests.len()),
        Err(e) => println!("Could not parse, error {}", e),
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    extern crate time;
    use time::strptime;
    use::time::Duration;

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();

        let expected = Request {
            id: 2,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = parse_request_line(line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            time: strptime("08/Apr/2016:09:58:48 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(10),
        };

        let result = parse_response_line(line);

        assert_eq!(result.unwrap(), expected)
    }
}
<|endoftext|>


<commit_before>#![feature(asm)]
#![feature(alloc)]
#![feature(heap_api)]
#![feature(box_syntax)]
#![feature(field_init_shorthand)]
#![allow(dead_code)]
#![allow(non_camel_case_types)]

extern crate alloc;
extern crate byteorder;
extern crate capstone;
extern crate docopt;
extern crate libc;
extern crate rustc_serialize;
extern crate time;

#[cfg(target_os = "windows")]
extern crate winapi;

#[cfg(target_os = "windows")]
extern crate kernel32;

macro_rules! offset_of {
    ($ty:ty, $field:ident) => {
        unsafe { &(*(0 as *const $ty)).$field as *const _ as usize }
    }
}

mod ast;
mod baseline;
mod class;
mod cpu;
mod ctxt;
mod driver;
mod dseg;
mod error;
mod execstate;
mod gc;
mod handle;
mod interner;
mod lexer;
mod masm;
mod mem;
mod mir;
mod object;
mod os;
mod os_cpu;
mod parser;
mod semck;
mod stacktrace;
mod stdlib;
mod sym;
mod timer;
mod ty;
mod vtable;

#[cfg(test)]
mod test;

#[cfg(not(test))]
use std::process::exit;

#[cfg(not(test))]
fn main() {
    os::mem::init_page_size();
    exit(driver::start());
}
<commit_msg>remove field_init_shorthand feature<commit_after>#![feature(asm)]
#![feature(alloc)]
#![feature(heap_api)]
#![feature(box_syntax)]
#![allow(dead_code)]
#![allow(non_camel_case_types)]

extern crate alloc;
extern crate byteorder;
extern crate capstone;
extern crate docopt;
extern crate libc;
extern crate rustc_serialize;
extern crate time;

#[cfg(target_os = "windows")]
extern crate winapi;

#[cfg(target_os = "windows")]
extern crate kernel32;

macro_rules! offset_of {
    ($ty:ty, $field:ident) => {
        unsafe { &(*(0 as *const $ty)).$field as *const _ as usize }
    }
}

mod ast;
mod baseline;
mod class;
mod cpu;
mod ctxt;
mod driver;
mod dseg;
mod error;
mod execstate;
mod gc;
mod handle;
mod interner;
mod lexer;
mod masm;
mod mem;
mod mir;
mod object;
mod os;
mod os_cpu;
mod parser;
mod semck;
mod stacktrace;
mod stdlib;
mod sym;
mod timer;
mod ty;
mod vtable;

#[cfg(test)]
mod test;

#[cfg(not(test))]
use std::process::exit;

#[cfg(not(test))]
fn main() {
    os::mem::init_page_size();
    exit(driver::start());
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate rustfbp;
extern crate capnp;

use self::rustfbp::scheduler::{Scheduler};
use self::rustfbp::loader::{ComponentBuilder};
use self::rustfbp::ports::Ports;

use self::rustfbp::allocator::{Allocator, HeapSenders, HeapIP, HeapIPReceiver};

use std::thread;
use std::env;

mod contracts {
    include!("/nix/store/df7r145xfifhvaxh3j52izpqmqqk4kby-path/src/contract_capnp.rs");
}
use contracts::path;

pub fn main() {
    println!("Hello, fractalide!");

    let config = env::var("FRACTALIDE_CONFIG").expect("cannot read FRACTALIDE_CONFIG");
    println!("location of config = {}", config);

    let file = ComponentBuilder::new("/nix/store/f5s69qwlf8jysgcwlrpj7g4wgih8l09l-file_open/lib/libcomponent.so");
    let print = ComponentBuilder::new("/nix/store/gy89qycq12r4j370q9cggj70yd5zhb3y-file_print/lib/libcomponent.so");
    let lex = ComponentBuilder::new("/nix/store/7m5rf0m88mxqshbf8rvgpakvhdm64s7f-development_parser_fbp_lexical/lib/libcomponent.so");
    let semantic = ComponentBuilder::new("/nix/store/a4y0fbx4asq1pnzg4w2miw31d90lgys2-development_parser_fbp_semantic/lib/libcomponent.so");
    let graph_print = ComponentBuilder::new("/nix/store/ln3pahm77pqfj85md1z7454qsnzmh37j-development_parser_fbp_print_graph/lib/libcomponent.so");

    let mut sched = Scheduler::new();
    sched.add_component("open".into(), &file);
    sched.add_component("print".into(), &print);
    sched.add_component("lex".into(), &lex);
    sched.add_component("sem".into(), &semantic);
    sched.add_component("graph_print".into(), &graph_print);

    let senders = (sched.allocator.senders.create)();
    let mut p = Ports::new("exterior".into(), &sched.allocator, senders,
                           vec!["r".into()],
                           vec![],
                           vec!["s".into()],
                           vec![]).expect("cannot create");
    let hs = HeapSenders::from_raw(senders);
    sched.inputs.insert("exterior".into(), hs);

    p.connect("s".into(), sched.get_sender("open".into(), "input".into()).unwrap()).expect("unable to connect");
    sched.connect("open".into(), "output".into(), "print".into(), "input".into()).expect("cannot connect");
    sched.connect("print".into(), "output".into(), "lex".into(), "input".into()).expect("cannot connect");
    sched.connect("lex".into(), "output".into(), "sem".into(), "input".into()).expect("cannot connect");
    sched.connect("sem".into(), "output".into(), "graph_print".into(), "input".into()).expect("cannot connect");

    let mut msg = capnp::message::Builder::new_default();
    {
        let mut number = msg.init_root::<path::Builder>();
        number.set_path("/home/denis/test.fbp");
    }

    let mut ip = sched.allocator.ip.build_empty();
    ip.write_builder(&mut msg);

    p.send("s".into(), ip).expect("unable to send to comp");

    sched.join();

}
<commit_msg>with fvm<commit_after>#[macro_use]
extern crate rustfbp;
extern crate capnp;

use self::rustfbp::scheduler::{Scheduler};
use self::rustfbp::loader::{ComponentBuilder};
use self::rustfbp::ports::Ports;

use self::rustfbp::allocator::{Allocator, HeapSenders, HeapIP, HeapIPReceiver};

use std::thread;
use std::env;

mod contracts {
    include!("/nix/store/df7r145xfifhvaxh3j52izpqmqqk4kby-path/src/contract_capnp.rs");
}
use contracts::path;

pub fn main() {
    println!("Hello, fractalide!");

    let config = env::var("FRACTALIDE_CONFIG").expect("cannot read FRACTALIDE_CONFIG");
    println!("location of config = {}", config);

    let file = ComponentBuilder::new("/nix/store/lm3ifc11q1klhmjynsr2ydpv7h1i71dg-file_open/lib/libcomponent.so");
    let print = ComponentBuilder::new("/nix/store/qni6z9axvi03x0sb4bsmfbzk8r8zvqva-file_print/lib/libcomponent.so");
    let lex = ComponentBuilder::new("/nix/store/9jrnlc77jr3q7l8yd270viax0f0v4j1y-development_parser_fbp_lexical/lib/libcomponent.so");
    let semantic = ComponentBuilder::new("/nix/store/ga08s5fkzm4fwbbjjgwwv4617c6mbz24-development_parser_fbp_semantic/lib/libcomponent.so");
    let graph_print = ComponentBuilder::new("/nix/store/viswq7bqqg6i5vh1y5p2178zdjpw7b1i-development_parser_fbp_print_graph/lib/libcomponent.so");
    let fvm = ComponentBuilder::new("/nix/store/5ln6671q25hz4lsqgj3p20330i689sk0-development_fvm/lib/libcomponent.so");

    let mut sched = Scheduler::new();
    sched.add_component("open".into(), &file);
    sched.add_component("print".into(), &print);
    sched.add_component("lex".into(), &lex);
    sched.add_component("sem".into(), &semantic);
    sched.add_component("graph_print".into(), &graph_print);
    sched.add_component("fvm".into(), &fvm);

    let senders = (sched.allocator.senders.create)();
    let mut p = Ports::new("exterior".into(), &sched.allocator, senders,
                           vec!["r".into()],
                           vec![],
                           vec!["s".into()],
                           vec![]).expect("cannot create");
    let hs = HeapSenders::from_raw(senders);
    sched.inputs.insert("exterior".into(), hs);

    p.connect("s".into(), sched.get_sender("open".into(), "input".into()).unwrap()).expect("unable to connect");
    sched.connect("open".into(), "output".into(), "print".into(), "input".into()).expect("cannot connect");
    sched.connect("print".into(), "output".into(), "lex".into(), "input".into()).expect("cannot connect");
    sched.connect("lex".into(), "output".into(), "sem".into(), "input".into()).expect("cannot connect");
    sched.connect("sem".into(), "output".into(), "graph_print".into(), "input".into()).expect("cannot connect");

    sched.connect("open".into(), "error".into(), "fvm".into(), "file_error".into());
    sched.connect("sem".into(), "error".into(), "fvm".into(), "semantic_error".into());

    let mut msg = capnp::message::Builder::new_default();
    {
        let mut number = msg.init_root::<path::Builder>();
        number.set_path("/home/denis/tst.fbp");
    }

    let mut ip = sched.allocator.ip.build_empty();
    ip.write_builder(&mut msg);

    p.send("s".into(), ip).expect("unable to send to comp");

    sched.join();

}
<|endoftext|>


<commit_before>use std::io::fs::rename;
use std::io::fs::PathExtensions;
use std::collections::HashMap;
use std::os;
use std::io;
use std::vec::Vec;
use std::str::StrSlice;

mod util;
mod server;

fn main() {

    let args = os::args();

    if args.len() == 2 && args[1] == "--server".to_string() {
        serve();
        return;
    }

    if args.len() == 2 && args[1] == "--start-server".to_string() {
        server::start_server();
        return;
    }

    let use_external_process = args.contains(&String::from_str("--use-external-process"));

    if args.len() < 3 {
        println!("Usage: {} <original-directory> <new-directory>", args[0]);
        return;
    }

    let remote_source = args[1].as_slice().contains(":");
    let dry_run = args.contains(&"--dry-run".to_string());
    let mut ssh_port = 22i;
    let host: String;
    let source_path: String;

    if args.contains(&"--ssh-port".to_string()) {
        let index = args.iter().position(|a| a == &"--ssh-port".to_string()).unwrap();
        ssh_port = from_str(args.get(index + 1).as_slice()).unwrap();
    }

    if remote_source {
        let components: Vec<&str> = args[1].as_slice().split(':').collect();
        host = components[0].to_string();
        source_path = components[1].to_string();
    }
    else {
        source_path = args[1].clone();
        host = "".to_string();
    }

    let src_directory = Path::new(source_path);
    let dst_directory = Path::new(args[2].clone());

    if remote_source || use_external_process {
        let mut server = server::StdServer::new(host, ssh_port);
        let mut dest_server = server::InMemoryServer::new();
        sync_renames(dry_run, &mut server, &mut dest_server, &src_directory, &dst_directory);
    }
    else {
        let mut server = server::InMemoryServer::new();
        let mut dest_server = server::InMemoryServer::new();
        sync_renames(dry_run, &mut server, &mut dest_server, &src_directory, &dst_directory);
    }
}

fn sync_renames(dry_run: bool, source_server: &mut server::Server, dest_server: &mut server::Server, src_directory: &Path, dst_directory: &Path) {

    let src_map = source_server.get_metadata(src_directory);
    let new_map = dest_server.get_metadata(dst_directory);

    for (&(size, mod_date), paths) in new_map.iter() {

        let mut matching_paths = 0i;

        for (i, value) in paths.iter().enumerate() {

            let new_path = Path::new(value.clone());
            let src_path :Path;

            match find_matching_file(&new_path, size, mod_date, &src_map, source_server, dest_server) {
                Some(p) => src_path = p,
                None => continue
            }

            let mut file_path = dst_directory.clone();
            file_path.push(src_path.path_relative_from(src_directory).unwrap());

            let src_relative_path = src_path.path_relative_from(src_directory).unwrap();
            let dst_relative_path = new_path.path_relative_from(dst_directory).unwrap();

            // No point in comparing a file with itself
            if src_relative_path == dst_relative_path {
                continue;
            }

            matching_paths += 1;

            if matching_paths > 1 && file_path.exists() && source_server.get_checksum(&src_path) == dest_server.get_checksum(&file_path) {
                println!("{} was copied to {}", dst_relative_path.display(), src_relative_path.display());

            } else {
                println!("{} was renamed to {}", dst_relative_path.display(), src_relative_path.display());
                if !dry_run {
                    rename(
                        &dst_directory.join(dst_relative_path),
                        &dst_directory.join(src_relative_path)).unwrap();
                }
            }
        }
    }
}

fn find_matching_file(file: &Path, size: u64, modification_date: u64, master_metadata: &HashMap<(u64, u64), Vec<String>>, src_server: &mut server::Server, dest_server: &mut server::Server) -> Option<Path> {

    // Check whether there are any files in master with the same
    // size and modification date of the sought file
    match master_metadata.find(&(size, modification_date)) {

        Some(matches) => {

            // Compare the file contents until we find a match, or return None if
            // we don't find one
            for potential_match in matches.iter() {
                let path = Path::new(potential_match.clone());
                if src_server.get_checksum(&path) == dest_server.get_checksum(file) {
                    return Some(path);
                }
            }

            return None

        }

        None => return None
    }

}

fn serve() {

    for line in io::stdin().lines() {
        let unwrapped_line = line.unwrap();
        let line_slice = unwrapped_line.as_slice().trim_right_chars('\n');
        if line_slice.starts_with("checksum ") {
           let file_name = line_slice.slice_from(9);
           println!("{}", util::get_file_checksum(&Path::new(file_name)));
           println!("\0");
        }

        if line_slice.starts_with("metadata ") {
            let path = line_slice.slice_from(9);
            for metadata in util::MetadataReader::new(&Path::new(path)) {
                println!("{}", metadata.path);
                println!("{}", metadata.size);
                println!("{}", metadata.modified);
            }
            println!("\0");

        }

    }
}
<commit_msg>Add a verbose switch<commit_after>use std::io::fs::rename;
use std::io::fs::PathExtensions;
use std::collections::HashMap;
use std::os;
use std::io;
use std::vec::Vec;
use std::str::StrSlice;

mod util;
mod server;

fn main() {

    let args = os::args();

    if args.len() == 2 && args[1] == "--server".to_string() {
        serve();
        return;
    }

    if args.len() == 2 && args[1] == "--start-server".to_string() {
        server::start_server();
        return;
    }

    let use_external_process = args.contains(&String::from_str("--use-external-process"));

    if args.len() < 3 {
        println!("Usage: {} <original-directory> <new-directory>", args[0]);
        return;
    }

    let remote_source = args[1].as_slice().contains(":");
    let dry_run = args.contains(&"--dry-run".to_string());
    let mut ssh_port = 22i;
    let host: String;
    let source_path: String;
    let verbose = args.contains(&"--verbose".to_string());

    if args.contains(&"--ssh-port".to_string()) {
        let index = args.iter().position(|a| a == &"--ssh-port".to_string()).unwrap();
        ssh_port = from_str(args.get(index + 1).as_slice()).unwrap();
    }

    if remote_source {
        let components: Vec<&str> = args[1].as_slice().split(':').collect();
        host = components[0].to_string();
        source_path = components[1].to_string();
    }
    else {
        source_path = args[1].clone();
        host = "".to_string();
    }

    let src_directory = Path::new(source_path);
    let dst_directory = Path::new(args[2].clone());

    if remote_source || use_external_process {
        let mut server = server::StdServer::new(host, ssh_port);
        let mut dest_server = server::InMemoryServer::new();
        sync_renames(verbose, dry_run, &mut server, &mut dest_server, &src_directory, &dst_directory);
    }
    else {
        let mut server = server::InMemoryServer::new();
        let mut dest_server = server::InMemoryServer::new();
        sync_renames(verbose, dry_run, &mut server, &mut dest_server, &src_directory, &dst_directory);
    }
}

fn sync_renames(verbose: bool, dry_run: bool, source_server: &mut server::Server, dest_server: &mut server::Server, src_directory: &Path, dst_directory: &Path) {

    if verbose {
        println!("Getting source metadata...");
    }
    let src_map = source_server.get_metadata(src_directory);
    if verbose {
        println!("Getting destination metadata...");
    }
    let new_map = dest_server.get_metadata(dst_directory);

    for (&(size, mod_date), paths) in new_map.iter() {

        let mut matching_paths = 0i;

        for (i, value) in paths.iter().enumerate() {

            let new_path = Path::new(value.clone());
            let src_path :Path;

            match find_matching_file(verbose, &new_path, size, mod_date, &src_map, source_server, dest_server) {
                Some(p) => src_path = p,
                None => continue
            }

            let mut file_path = dst_directory.clone();
            file_path.push(src_path.path_relative_from(src_directory).unwrap());

            let src_relative_path = src_path.path_relative_from(src_directory).unwrap();
            let dst_relative_path = new_path.path_relative_from(dst_directory).unwrap();

            // No point in comparing a file with itself
            if src_relative_path == dst_relative_path {
                continue;
            }

            matching_paths += 1;

            if matching_paths > 1 && file_path.exists() && source_server.get_checksum(&src_path) == dest_server.get_checksum(&file_path) {
                println!("{} was copied to {}", dst_relative_path.display(), src_relative_path.display());

            } else {
                println!("{} was renamed to {}", dst_relative_path.display(), src_relative_path.display());
                if !dry_run {
                    rename(
                        &dst_directory.join(dst_relative_path),
                        &dst_directory.join(src_relative_path)).unwrap();
                }
            }
        }
    }
}

fn find_matching_file(verbose: bool, file: &Path, size: u64, modification_date: u64, master_metadata: &HashMap<(u64, u64), Vec<String>>, src_server: &mut server::Server, dest_server: &mut server::Server) -> Option<Path> {

    // Check whether there are any files in master with the same
    // size and modification date of the sought file
    match master_metadata.find(&(size, modification_date)) {

        Some(matches) => {

            // Compare the file contents until we find a match, or return None if
            // we don't find one
            for potential_match in matches.iter() {
                let path = Path::new(potential_match.clone());
                if verbose {
                    println!("Getting checksum for source file '{}'...", path.as_str());
                }
                let src_checksum = src_server.get_checksum(&path);
                if verbose {
                    println!("Getting checksum for destination file '{}'...", path.as_str());
                }
                let dest_checksum = dest_server.get_checksum(file);
                if src_checksum == dest_checksum {
                    return Some(path);
                }
            }

            return None

        }

        None => return None
    }

}

fn serve() {

    for line in io::stdin().lines() {
        let unwrapped_line = line.unwrap();
        let line_slice = unwrapped_line.as_slice().trim_right_chars('\n');
        if line_slice.starts_with("checksum ") {
           let file_name = line_slice.slice_from(9);
           println!("{}", util::get_file_checksum(&Path::new(file_name)));
           println!("\0");
        }

        if line_slice.starts_with("metadata ") {
            let path = line_slice.slice_from(9);
            for metadata in util::MetadataReader::new(&Path::new(path)) {
                println!("{}", metadata.path);
                println!("{}", metadata.size);
                println!("{}", metadata.modified);
            }
            println!("\0");

        }

    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(plugin, link)] extern crate log;

const U4_LOW:  u8 = 0b00001111;
const U4_HIGH: u8 = 0b11110000;

#[deriving(PartialEq,Show)]
enum CpuState {
	Continue,
	Halt,
}

/// The P150 virtual machine
struct P150Cpu {
	ip:  u8,
	ir: u16,

	reg: [u8, ..16],
	mem: [u8, ..256],
}

impl P150Cpu {
	/// Initializes the P150 CPU
	///
	/// NOTE: This implementation 0s memory; but this is not guaranteed
	/// by the machine specification.
	///
	/// Programs must start at memory address 0x00
	fn new() -> P150Cpu {
		P150Cpu {
			ip:  0x00,
			ir:  0x0000,

			reg: [0u8, ..16],
			mem: [0u8, ..256],
		}
	}

	/// Prints the current machine state to the console window
	/// This includes the IP, IR, and all registers.
	/// (Registers will be formatted as 2s complement numbers.)
	fn dump(&self) {
		println!("IP: 0x{:02X}, IR: 0x{:04X}", self.ip, self.ir);

		println!("Registers")
		for (addr, cell) in self.reg.iter().enumerate() {
			println!("{}: {}", addr, cell)
		}

	#[cfg(test)]
	fn get_reg(&self) -> &[u8] {
		self.reg
	}

	/// Read an array of instructions into memory
	/// This reads two bytes at a time from the `memory` array
	/// and loads them into the P150s RAM bank, starting from address 0.
	fn init_mem(&mut self, memory: &[u16]) {
		let mut next_cell = 0x00;

		for op in memory.iter() {
			let byte_1 = (*op >> 8) as u8;
			let byte_2 = *op as u8;

			self.mem[next_cell]     = byte_1;
			self.mem[next_cell + 1] = byte_2;
			next_cell += 2
		}
	}

	/// Runs the entire fetch, execute, decode cycle against the current IP
	fn tick(&mut self) -> CpuState {
		self.fetch();

		// decode
		let op = (self.ir >> 12) as u8;
		match op {
			0x0   => { // ADDB
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);   // first input: lower nibble of first byte
				let rloc_i1 = h_nibble(self.ir as u8);          // second input: upper nibble of second byte
				let rloc_o0 = l_nibble(self.ir as u8);          // output: lower nibble of second byte

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] + self.reg[rloc_i1 as uint];
				Continue
			},

			0x3   => { // AND
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let rloc_i1 = h_nibble(self.ir as u8);
				let rloc_o0 = l_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] & self.reg[rloc_i1 as uint];
				Continue
			},

			0x4   => { // OR
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let rloc_i1 = h_nibble(self.ir as u8);
				let rloc_o0 = l_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] | self.reg[rloc_i1 as uint];
				Continue
			},

			0x5   => { // XOR
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let rloc_i1 = h_nibble(self.ir as u8);
				let rloc_o0 = l_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] ^ self.reg[rloc_i1 as uint];
				Continue
			},

			0x6   => { // MLOAD
				let rloc_o0 = l_nibble((self.ir >> 8) as u8);
				let mloc_i0 = self.ir as u8;

				self.reg[rloc_o0 as uint] = self.mem[mloc_i0 as uint];
				Continue
			},

			0x7   => { // MSTOR
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let mloc_o0 = self.ir as u8;

				self.mem[mloc_o0 as uint] = self.reg[rloc_i0 as uint];
				Continue
			},

			0x8   => { // RMOV
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let rloc_o0 = h_nibble(self.ir as u8);

				debug!("moving from {:02X} to {:02X}", rloc_i0, rloc_o0)
				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint];
				Continue
			},

			0x9   => { // RSET
				let rloc = ((self.ir >> 8) as u8) & U4_LOW;  // lower nibble of first byte
				let rval = self.ir as u8;                    // value is entire second byte

				self.reg[rloc as uint] = rval;               // store value in register
				Continue
			},

			0xA   => { // JMPEQ
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let next_ip = self.ir as u8;

				if self.reg[rloc_i0 as uint] == self.reg[0] { self.ip = next_ip }
				Continue
			},

			0xB   => { Halt },
			_     => { debug!("halt, cpu on fire: {}", op); Halt },
		}
	}

	/// Load the instruction at `IP` and advance the pointer by two bytes.
	/// The instruction is packed into a single `u16` and stored in the instruction register.
	fn fetch(&mut self) {
		// load PC -> IR
		let byte_1 = self.mem[(self.ip+0) as uint];
		let byte_2 = self.mem[(self.ip+1) as uint];

		self.ir  = (byte_1 as u16 << 8) | (byte_2 as u16);
		self.ip += 2;

		debug!("IR set to 0x{:04X} ({:02X},{:02X})", self.ir, byte_1, byte_2)
	}
}

/// Take the lower nibble of a byte
fn l_nibble(byte: u8) -> u8 {
	(byte & U4_LOW)
}

/// Take the upper nibble of a byte and shift it
/// towards the least significant bits.
fn h_nibble(byte: u8) -> u8 {
	(byte & U4_HIGH) >> 4
}

fn main() {
	let mut cpu = P150Cpu::new();
	let program = [0x902A, 0x911E, 0x920C, 0x0123, 0x8310, 0xA106, 0xB000];
	cpu.init_mem(program);

	loop {
		match cpu.tick() {
			Continue => { continue; },
			Halt => { println!("CPU halted."); cpu.dump(); break; },
		}
	}
}

#[test]
fn test_hammer_time() {
	// cpu should stop
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0xB000]);

	assert_eq!(cpu.tick(), Halt)
}

#[test]
fn test_registers() {
	// cpu should set, move registers accordingly
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9110, 0x8130, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x3], 16)
}

#[test]
fn test_memory() {
	// memory sets and memory stores should read back successfully
	// uninitialized registers should not match initialized registers
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9120, 0x9330, 0x7140, 0x6240, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert!(cpu.get_reg()[0x1] == cpu.get_reg()[0x2]);
	assert!(cpu.get_reg()[0x1] != cpu.get_reg()[0x3]);
}

#[test]
fn test_bin() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9121, 0x9222, 0x3123, 0x4124, 0x5125, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert!(cpu.get_reg()[0x3] == (0x21 & 0x22));
	assert!(cpu.get_reg()[0x4] == (0x21 | 0x22));
	assert!(cpu.get_reg()[0x5] == (0x21 ^ 0x22));
}

#[test]
fn test_math() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9120, 0x920A, 0x0123, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x3], cpu.get_reg()[0x1] + cpu.get_reg()[0x2])
}

#[test]
fn test_branch() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9021, 0x9121, 0xA108, 0xB000, 0x922A, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x2], 0x2A)
}

<commit_msg>Modify debugging and documentation info<commit_after>#![feature(phase)]
#[phase(plugin, link)] extern crate log;

const U4_LOW:  u8 = 0b00001111;
const U4_HIGH: u8 = 0b11110000;

#[deriving(PartialEq,Show)]
enum CpuState {
	Continue,
	Halt,
}

/// The P150 virtual machine
struct P150Cpu {
	ip:  u8,
	ir: u16,

	reg: [u8, ..16],
	mem: [u8, ..256],
}

impl P150Cpu {
	/// Initializes the P150 CPU
	///
	/// NOTE: This implementation 0s memory; but this is not guaranteed
	/// by the machine specification.
	///
	/// Programs must start at memory address 0x00
	fn new() -> P150Cpu {
		P150Cpu {
			ip:  0x00,
			ir:  0x0000,

			reg: [0u8, ..16],
			mem: [0u8, ..256],
		}
	}

	/// Prints the current machine state to the console window
	/// This includes the IP, IR, and all registers.
	/// (Registers will be formatted as 2s complement numbers.)
	fn dump(&self) {
		println!("IP: 0x{:02X}, IR: 0x{:04X}", self.ip, self.ir);

		println!("---\nRegisters\n---")
		for (addr, cell) in self.reg.iter().enumerate() {
			println!("{:01X}: {}", addr, cell)
		}

	#[cfg(test)]
	fn get_reg(&self) -> &[u8] {
		self.reg
	}

	/// Read an array of instructions into main memory
	/// This reads two bytes at a time from the `memory` array
	/// and loads them into the P150s RAM bank, starting from address 0.
	fn init_mem(&mut self, memory: &[u16]) {
		let mut next_cell = 0x00;

		for op in memory.iter() {
			let byte_1 = (*op >> 8) as u8;
			let byte_2 = *op as u8;

			self.mem[next_cell]     = byte_1;
			self.mem[next_cell + 1] = byte_2;
			next_cell += 2
		}
	}

	/// Runs the entire fetch, execute, decode cycle against the current IP
	fn tick(&mut self) -> CpuState {
		self.fetch();

		// decode
		let op = (self.ir >> 12) as u8;
		match op {
			0x0   => { // ADDB
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);   // first input: lower nibble of first byte
				let rloc_i1 = h_nibble(self.ir as u8);          // second input: upper nibble of second byte
				let rloc_o0 = l_nibble(self.ir as u8);          // output: lower nibble of second byte

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] + self.reg[rloc_i1 as uint];
				Continue
			},

			0x3   => { // AND
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let rloc_i1 = h_nibble(self.ir as u8);
				let rloc_o0 = l_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] & self.reg[rloc_i1 as uint];
				Continue
			},

			0x4   => { // OR
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let rloc_i1 = h_nibble(self.ir as u8);
				let rloc_o0 = l_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] | self.reg[rloc_i1 as uint];
				Continue
			},

			0x5   => { // XOR
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let rloc_i1 = h_nibble(self.ir as u8);
				let rloc_o0 = l_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] ^ self.reg[rloc_i1 as uint];
				Continue
			},

			0x6   => { // MLOAD
				let rloc_o0 = l_nibble((self.ir >> 8) as u8);
				let mloc_i0 = self.ir as u8;

				self.reg[rloc_o0 as uint] = self.mem[mloc_i0 as uint];
				Continue
			},

			0x7   => { // MSTOR
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let mloc_o0 = self.ir as u8;

				self.mem[mloc_o0 as uint] = self.reg[rloc_i0 as uint];
				Continue
			},

			0x8   => { // RMOV
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let rloc_o0 = h_nibble(self.ir as u8);

				debug!("moving from {:02X} to {:02X}", rloc_i0, rloc_o0)
				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint];
				Continue
			},

			0x9   => { // RSET
				let rloc = l_nibble((self.ir >> 8) as u8);  // lower nibble of first byte
				let rval = self.ir as u8;                    // value is entire second byte

				self.reg[rloc as uint] = rval;               // store value in register
				Continue
			},

			0xA   => { // JMPEQ
				let rloc_i0 = l_nibble((self.ir >> 8) as u8);
				let next_ip = self.ir as u8;

				if self.reg[rloc_i0 as uint] == self.reg[0] { self.ip = next_ip }
				Continue
			},

			0xB   => { Halt },
			_     => { debug!("halt, cpu on fire: {}", op); Halt },
		}
	}

	/// Load the instruction at `IP` and advance the pointer by two bytes.
	/// The instruction is packed into a single `u16` and stored in the instruction register.
	fn fetch(&mut self) {
		// load PC -> IR
		let byte_1 = self.mem[(self.ip+0) as uint];
		let byte_2 = self.mem[(self.ip+1) as uint];

		self.ir  = (byte_1 as u16 << 8) | (byte_2 as u16);
		self.ip += 2;

		debug!("IR set to 0x{:04X} ({:02X},{:02X})", self.ir, byte_1, byte_2)
	}
}

/// Take the lower nibble of a byte
fn l_nibble(byte: u8) -> u8 {
	(byte & U4_LOW)
}

/// Take the upper nibble of a byte and shift it
/// towards the least significant bits.
fn h_nibble(byte: u8) -> u8 {
	(byte & U4_HIGH) >> 4
}

fn main() {
	let mut cpu = P150Cpu::new();
	let program = [0x911E, 0x920C, 0x0123, 0x7340, 0x6040, 0xA310, 0x9500, 0xB000, 0x9501, 0xB000];
	cpu.init_mem(program);

	loop {
		match cpu.tick() {
			Continue => { continue; },
			Halt => { println!("CPU halted."); cpu.dump(); break; },
		}
	}
}

#[test]
fn test_hammer_time() {
	// cpu should stop
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0xB000]);

	assert_eq!(cpu.tick(), Halt)
}

#[test]
fn test_registers() {
	// cpu should set, move registers accordingly
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9110, 0x8130, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x3], 16)
}

#[test]
fn test_memory() {
	// memory sets and memory stores should read back successfully
	// uninitialized registers should not match initialized registers
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9120, 0x9330, 0x7140, 0x6240, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert!(cpu.get_reg()[0x1] == cpu.get_reg()[0x2]);
	assert!(cpu.get_reg()[0x1] != cpu.get_reg()[0x3]);
}

#[test]
fn test_bin() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9121, 0x9222, 0x3123, 0x4124, 0x5125, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert!(cpu.get_reg()[0x3] == (0x21 & 0x22));
	assert!(cpu.get_reg()[0x4] == (0x21 | 0x22));
	assert!(cpu.get_reg()[0x5] == (0x21 ^ 0x22));
}

#[test]
fn test_math() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9120, 0x920A, 0x0123, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x3], cpu.get_reg()[0x1] + cpu.get_reg()[0x2])
}

#[test]
fn test_branch() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9021, 0x9121, 0xA108, 0xB000, 0x922A, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x2], 0x2A)
}

<|endoftext|>


<commit_before>extern crate libc;
extern crate termios;

use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, winsize};
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::env;
use std::fs::File;
use std::io::{self, Stdin, Stdout, Read, BufRead, BufReader, Error, ErrorKind, Write};

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

#[derive(PartialEq, Clone, Copy)]
enum Key {
    Character(u8),
    ArrowLeft,
    ArrowRight,
    ArrowUp,
    ArrowDown,
    DeleteKey,
    HomeKey,
    EndKey,
    PageUp,
    PageDown,
}

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct RawMode {
    orig_term: Termios
}

struct Row(String);

struct Editor {
    _mode: RawMode,
    cx: usize,
    cy: usize,
    rowoff: usize,
    coloff: usize,
    screenrows: usize,
    screencols: usize,
    rows: Option<Vec<Row>>,
    stdin: Stdin,
    stdout: Stdout,
}

impl RawMode {
    fn enable_raw_mode() -> io::Result<RawMode> {
        let mut term = Termios::from_fd(STDIN_FILENO)?;
        let mode = RawMode { orig_term: term.clone() };

        term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        term.c_oflag &= !OPOST;
        term.c_cflag |= CS8;
        term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
        term.c_cc[VMIN] = 0;
        term.c_cc[VTIME] = 1;

        tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
        Ok(mode)
    }
}

impl Drop for RawMode {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

fn read_non_blocking<R : Read>(r : &mut R, buf: &mut [u8]) -> usize {
    r.read(buf)
     .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
     .expect("read_non_blocking")
}

fn byte_slice(s: &str, offset: usize, max_len: usize) -> &[u8] {
    if s.len() > max_len + offset {
        s[offset..(max_len + offset)].as_bytes()
    } else if s.len() > offset {
        s[offset..].as_bytes()
    } else {
        s[0..0].as_bytes()
    }
}

fn editor_read_key(stdin: &mut Stdin) -> Key {
    let mut buf = [0; 1];
    loop {
        let n = read_non_blocking(stdin, &mut buf);
        if n == 1 {
            if buf[0] == b'\x1b' {
                let mut seq = [0; 2];
                let n = read_non_blocking(stdin, &mut seq);
                if n == 2 && seq[0] == b'[' {
                    if seq[1] >= b'0' && seq[1] <= b'9' {
                        let mut last = [0; 1];
                        let n = read_non_blocking(stdin, &mut last);
                        if n == 1 {
                            if last[0] == b'~' {
                                return match seq[1] {
                                    b'1' => Key::HomeKey,
                                    b'3' => Key::DeleteKey,
                                    b'4' => Key::EndKey,
                                    b'5' => Key::PageUp,
                                    b'6' => Key::PageDown,
                                    b'7' => Key::HomeKey,
                                    b'8' => Key::EndKey,
                                    _ => Key::Character(b'\x1b'),
                                }
                            }
                        }
                    } else {
                        return match seq[1] {
                            b'A' => Key::ArrowUp,
                            b'B' => Key::ArrowDown,
                            b'C' => Key::ArrowRight,
                            b'D' => Key::ArrowLeft,
                            b'H' => Key::HomeKey,
                            b'F' => Key::EndKey,
                            _ => Key::Character(b'\x1b'),
                        }
                    }
                } else if n == 2 && seq[0] == b'O' {
                    return match seq[1] {
                        b'H' => Key::HomeKey,
                        b'F' => Key::EndKey,
                        _ => Key::Character(b'\x1b'),
                    }
                }
                return Key::Character(b'\x1b');
            } else {
                return Key::Character(buf[0]);
            }
        }
    }
}

fn get_window_size() -> io::Result<(u16, u16)> {
    let ws = winsize { ws_col: 0, ws_row: 0, ws_xpixel: 0, ws_ypixel: 0 };
    unsafe {
        if libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0 {
            return Err(Error::new(ErrorKind::Other, "get_window_size: ioctl failed"))
        }
    }
    Ok((ws.ws_row, ws.ws_col))
}

impl Editor {
    fn new() -> io::Result<Editor> {
        let mode = RawMode::enable_raw_mode()?;
        let (rows, cols) = get_window_size()?;
        let stdin = io::stdin();
        let stdout = io::stdout();
        Ok(Editor {
            _mode: mode,
            cx: 0,
            cy: 0,
            rowoff: 0,
            coloff: 0,
            screenrows: rows as usize,
            screencols: cols as usize,
            rows: None,
            stdin: stdin,
            stdout: stdout
        })
    }

    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }

    fn scroll(&mut self) {
        if self.cy < self.rowoff {
            self.rowoff = self.cy;
        }
        if self.cy >= self.rowoff + self.screenrows {
            self.rowoff = self.cy - self.screenrows + 1;
        }
        if self.cx < self.coloff {
            self.coloff = self.cx;
        }
        if self.cx >= self.coloff + self.screencols {
            self.coloff = (1 + self.cx) - self.screencols;
        }
    }

    fn numrows(&self) -> usize {
        self.rows.as_ref().map_or(0, |v| v.len())
    }

    fn draw_rows(&mut self) -> io::Result<()> {
        let numrows = self.numrows();
        for y in 0..(self.screenrows) {
            let filerow = y + self.rowoff;
            if filerow >= numrows {
                if self.rows.is_none() && y == self.screenrows / 3 {
                    let mut msg = format!("Kilo-rs editor -- version {}", VERSION);
                    msg.truncate(self.screencols);
                    let padding = (self.screencols - msg.len()) / 2;
                    if padding > 0 {
                        self.write(b"~")?;
                        for _ in 1..padding {
                            self.write(b" ")?;
                        }
                    }
                    self.write(msg.as_bytes())?;
                } else {
                    self.write(b"~")?;
                }
            } else {
                let rows = self.rows.as_ref().unwrap();
                self.stdout.write(byte_slice(&rows[filerow].0, self.coloff, self.screencols))?;
            }

            self.write(b"\x1b[K")?;
            if y < self.screenrows - 1 {
                self.write(b"\r\n")?;
            }
        }
        Ok(())
    }

    fn refresh_screen(&mut self) -> io::Result<()> {
        self.scroll();

        self.write(b"\x1b[?25l")?;
        self.write(b"\x1b[H")?;

        self.draw_rows()?;

        let move_cursor = format!("\x1b[{};{}H",
                                  (self.cy - self.rowoff) + 1,
                                  (self.cx - self.coloff) + 1)
                                 .into_bytes();
        self.write(&move_cursor)?;

        self.write(b"\x1b[?25h")?;
        self.flush()
    }

    fn move_cursor(&mut self, k: Key) {
        match k {
            Key::ArrowUp => {
                if self.cy > 0 {
                    self.cy -= 1;
                }
            },
            Key::ArrowDown => {
                if self.cy < self.numrows() {
                    self.cy += 1;
                }
            },
            Key::ArrowLeft => {
                if self.cx > 0 {
                    self.cx -= 1;
                }
            },
            Key::ArrowRight => {
                self.cx += 1;
            },
            _ => (),
        }
    }

    fn process_keypress(&mut self) -> bool {
        let c = editor_read_key(&mut self.stdin);

        match c {
            Key::Character(k) if k == ctrl_key!(b'q') => {
                return false
            },
            Key::HomeKey => {
                self.cx = 0;
            },
            Key::EndKey => {
                self.cx = self.screencols - 1;
            },
            Key::PageUp | Key::PageDown => {
                let key = if c == Key::PageUp { Key::ArrowUp } else { Key::ArrowDown };
                for _ in 0..(self.screenrows) {
                    self.move_cursor(key);
                }
            },
            Key::ArrowUp | Key::ArrowDown | Key::ArrowLeft | Key::ArrowRight => {
                self.move_cursor(c);
            },
            _ => ()
        };
        true
    }

    fn open(&mut self, filename: &str) -> io::Result<()> {
        let f = File::open(filename)?;
        let file = BufReader::new(&f);
        let results: io::Result<Vec<Row>> = file.lines()
                        .map(|r| r.map(|l| Row(l)))
                        .collect();
        self.rows = Some(results?);
        Ok(())
    }
}

impl Drop for Editor {
    fn drop(&mut self) {
        // clear screen
        self.stdout.write(b"\x1b[2J").unwrap();
        self.stdout.write(b"\x1b[H").unwrap();
        self.stdout.flush().unwrap();
    }
}

fn main() {
    let mut editor = Editor::new().unwrap();
    if let Some(filename) = env::args().nth(1) {
        editor.open(&filename).unwrap();
    }

    editor.refresh_screen().unwrap();
    while editor.process_keypress() {
        editor.refresh_screen().unwrap();
    }
}
<commit_msg>Move cursor left and right at the end of a line<commit_after>extern crate libc;
extern crate termios;

use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, winsize};
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::env;
use std::fs::File;
use std::io::{self, Stdin, Stdout, Read, BufRead, BufReader, Error, ErrorKind, Write};

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

#[derive(PartialEq, Clone, Copy)]
enum Key {
    Character(u8),
    ArrowLeft,
    ArrowRight,
    ArrowUp,
    ArrowDown,
    DeleteKey,
    HomeKey,
    EndKey,
    PageUp,
    PageDown,
}

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct RawMode {
    orig_term: Termios
}

struct Row(String);

struct Editor {
    _mode: RawMode,
    cx: usize,
    cy: usize,
    rowoff: usize,
    coloff: usize,
    screenrows: usize,
    screencols: usize,
    rows: Option<Vec<Row>>,
    stdin: Stdin,
    stdout: Stdout,
}

impl RawMode {
    fn enable_raw_mode() -> io::Result<RawMode> {
        let mut term = Termios::from_fd(STDIN_FILENO)?;
        let mode = RawMode { orig_term: term.clone() };

        term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        term.c_oflag &= !OPOST;
        term.c_cflag |= CS8;
        term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
        term.c_cc[VMIN] = 0;
        term.c_cc[VTIME] = 1;

        tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
        Ok(mode)
    }
}

impl Drop for RawMode {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

fn read_non_blocking<R : Read>(r : &mut R, buf: &mut [u8]) -> usize {
    r.read(buf)
     .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
     .expect("read_non_blocking")
}

fn byte_slice(s: &str, offset: usize, max_len: usize) -> &[u8] {
    if s.len() > max_len + offset {
        s[offset..(max_len + offset)].as_bytes()
    } else if s.len() > offset {
        s[offset..].as_bytes()
    } else {
        s[0..0].as_bytes()
    }
}

fn editor_read_key(stdin: &mut Stdin) -> Key {
    let mut buf = [0; 1];
    loop {
        let n = read_non_blocking(stdin, &mut buf);
        if n == 1 {
            if buf[0] == b'\x1b' {
                let mut seq = [0; 2];
                let n = read_non_blocking(stdin, &mut seq);
                if n == 2 && seq[0] == b'[' {
                    if seq[1] >= b'0' && seq[1] <= b'9' {
                        let mut last = [0; 1];
                        let n = read_non_blocking(stdin, &mut last);
                        if n == 1 {
                            if last[0] == b'~' {
                                return match seq[1] {
                                    b'1' => Key::HomeKey,
                                    b'3' => Key::DeleteKey,
                                    b'4' => Key::EndKey,
                                    b'5' => Key::PageUp,
                                    b'6' => Key::PageDown,
                                    b'7' => Key::HomeKey,
                                    b'8' => Key::EndKey,
                                    _ => Key::Character(b'\x1b'),
                                }
                            }
                        }
                    } else {
                        return match seq[1] {
                            b'A' => Key::ArrowUp,
                            b'B' => Key::ArrowDown,
                            b'C' => Key::ArrowRight,
                            b'D' => Key::ArrowLeft,
                            b'H' => Key::HomeKey,
                            b'F' => Key::EndKey,
                            _ => Key::Character(b'\x1b'),
                        }
                    }
                } else if n == 2 && seq[0] == b'O' {
                    return match seq[1] {
                        b'H' => Key::HomeKey,
                        b'F' => Key::EndKey,
                        _ => Key::Character(b'\x1b'),
                    }
                }
                return Key::Character(b'\x1b');
            } else {
                return Key::Character(buf[0]);
            }
        }
    }
}

fn get_window_size() -> io::Result<(u16, u16)> {
    let ws = winsize { ws_col: 0, ws_row: 0, ws_xpixel: 0, ws_ypixel: 0 };
    unsafe {
        if libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0 {
            return Err(Error::new(ErrorKind::Other, "get_window_size: ioctl failed"))
        }
    }
    Ok((ws.ws_row, ws.ws_col))
}

impl Editor {
    fn new() -> io::Result<Editor> {
        let mode = RawMode::enable_raw_mode()?;
        let (rows, cols) = get_window_size()?;
        let stdin = io::stdin();
        let stdout = io::stdout();
        Ok(Editor {
            _mode: mode,
            cx: 0,
            cy: 0,
            rowoff: 0,
            coloff: 0,
            screenrows: rows as usize,
            screencols: cols as usize,
            rows: None,
            stdin: stdin,
            stdout: stdout
        })
    }

    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }

    fn scroll(&mut self) {
        if self.cy < self.rowoff {
            self.rowoff = self.cy;
        }
        if self.cy >= self.rowoff + self.screenrows {
            self.rowoff = self.cy - self.screenrows + 1;
        }
        if self.cx < self.coloff {
            self.coloff = self.cx;
        }
        if self.cx >= self.coloff + self.screencols {
            self.coloff = (1 + self.cx) - self.screencols;
        }
    }

    fn numrows(&self) -> usize {
        self.rows.as_ref().map_or(0, |v| v.len())
    }

    fn draw_rows(&mut self) -> io::Result<()> {
        let numrows = self.numrows();
        for y in 0..(self.screenrows) {
            let filerow = y + self.rowoff;
            if filerow >= numrows {
                if self.rows.is_none() && y == self.screenrows / 3 {
                    let mut msg = format!("Kilo-rs editor -- version {}", VERSION);
                    msg.truncate(self.screencols);
                    let padding = (self.screencols - msg.len()) / 2;
                    if padding > 0 {
                        self.write(b"~")?;
                        for _ in 1..padding {
                            self.write(b" ")?;
                        }
                    }
                    self.write(msg.as_bytes())?;
                } else {
                    self.write(b"~")?;
                }
            } else {
                let rows = self.rows.as_ref().unwrap();
                self.stdout.write(byte_slice(&rows[filerow].0, self.coloff, self.screencols))?;
            }

            self.write(b"\x1b[K")?;
            if y < self.screenrows - 1 {
                self.write(b"\r\n")?;
            }
        }
        Ok(())
    }

    fn refresh_screen(&mut self) -> io::Result<()> {
        self.scroll();

        self.write(b"\x1b[?25l")?;
        self.write(b"\x1b[H")?;

        self.draw_rows()?;

        let move_cursor = format!("\x1b[{};{}H",
                                  (self.cy - self.rowoff) + 1,
                                  (self.cx - self.coloff) + 1)
                                 .into_bytes();
        self.write(&move_cursor)?;

        self.write(b"\x1b[?25h")?;
        self.flush()
    }

    fn rowlen(&self, index: usize) -> usize {
        let row = self.rows.as_ref().and_then(|r| r.get(index));
        row.map_or(0, |r| r.0.len())
    }

    fn move_cursor(&mut self, k: Key) {
        match k {
            Key::ArrowUp => {
                if self.cy > 0 {
                    self.cy -= 1;
                }
            },
            Key::ArrowDown => {
                if self.cy < self.numrows() {
                    self.cy += 1;
                }
            },
            Key::ArrowLeft => {
                if self.cx > 0 {
                    self.cx -= 1;
                } else if self.cy > 0 {
                    self.cy -= 1;
                    self.cx = self.rowlen(self.cy);
                }
            },
            Key::ArrowRight => {
                let row = self.rows.as_ref().and_then(|r| r.get(self.cy));
                let rowlen = row.map_or(0, |r| r.0.len());
                if self.cx < rowlen {
                    self.cx += 1;
                } else if row.is_some() && self.cx == rowlen {
                    self.cx = 0;
                    self.cy += 1;
                }
            },
            _ => (),
        }

        let rowlen = self.rowlen(self.cy);
        if self.cx > rowlen {
            self.cx = rowlen;
        }
    }

    fn process_keypress(&mut self) -> bool {
        let c = editor_read_key(&mut self.stdin);

        match c {
            Key::Character(k) if k == ctrl_key!(b'q') => {
                return false
            },
            Key::HomeKey => {
                self.cx = 0;
            },
            Key::EndKey => {
                self.cx = self.screencols - 1;
            },
            Key::PageUp | Key::PageDown => {
                let key = if c == Key::PageUp { Key::ArrowUp } else { Key::ArrowDown };
                for _ in 0..(self.screenrows) {
                    self.move_cursor(key);
                }
            },
            Key::ArrowUp | Key::ArrowDown | Key::ArrowLeft | Key::ArrowRight => {
                self.move_cursor(c);
            },
            _ => ()
        };
        true
    }

    fn open(&mut self, filename: &str) -> io::Result<()> {
        let f = File::open(filename)?;
        let file = BufReader::new(&f);
        let results: io::Result<Vec<Row>> = file.lines()
                        .map(|r| r.map(|l| Row(l)))
                        .collect();
        self.rows = Some(results?);
        Ok(())
    }
}

impl Drop for Editor {
    fn drop(&mut self) {
        // clear screen
        self.stdout.write(b"\x1b[2J").unwrap();
        self.stdout.write(b"\x1b[H").unwrap();
        self.stdout.flush().unwrap();
    }
}

fn main() {
    let mut editor = Editor::new().unwrap();
    if let Some(filename) = env::args().nth(1) {
        editor.open(&filename).unwrap();
    }

    editor.refresh_screen().unwrap();
    while editor.process_keypress() {
        editor.refresh_screen().unwrap();
    }
}
<|endoftext|>


<commit_before>// Import the Ordering Enum from the Rust Standard Library
// http://doc.rust-lang.org/std/cmp/enum.Ordering.html
// Note: Enums are a powerful tool for data representation and 'generics' may be used
// to make Enums generic across Types to become even more powerful.
// Ordering Enum form used for Comparisons
// enum Ordering {
//   Less,
//   Equal,
//   Greater,
// }
// :: Symbol indicates a Namespace
// 'std' Module contains Sub-Module 'cmp' containing Sub-Sub-Module 'Ordering'
// The Enum Variants (Equal, Less, Greater) have been imported instead of just the Ordering
// Namespace itself to achieve compactness and convenience. This avoids full scoping and
// avoids long notation (i.e. use just Equal instead of Ordering::Equal), however name conflicts
// may arise (so it is best practice to import and apply the Namespace combined with a
// Variants together)
use std::cmp::Ordering;

// Configure the Ordering Enum
// 'cmp' is a Function that compares two given parameters and returns an Ordering
// where depending on the difference between these parameters, the returned Ordering
// may be Ordering:Less, Ordering::Greater, or Ordering::Equal
// Variants of the Enum (Ordering) are Namespaced under the Enum itself.
fn cmp(a: i32, b: i32) -> Ordering {
  if a < b      { Ordering::Less }
  else if a > b { Ordering::Greater }
  else          { Ordering::Equal }
}

/*
  Rust is an Expression-Based Systems language.
  Declare main function
*/
fn main() {

    // Metaprogramming calling a macro and passing staticaly allocated string arg
    println!("Hello, world!");

    /*
      Variable binding expression.
      <let expression> is Full Pattern for Pattern Matching.
      Rust's Type Inference automatically determines an expression's Type.
      Alternatively be explicit and supply an expression's Type after a colon.
      Or simply annotate the Type in a comment to understand the Type Rust infers.
      Rust Bindings are immutable by Default.
      Mutable Bindings require 'mut' prefix for Type Safety.
    */
    let a: i32 = 5; // assign immutable binding 'a' with Type i32 and Value 5

    // If statement based on Branch concept
    if a == 5 {
      println!("x is five");
    } else {
      println!("x is not five");
    }

    let (x, y, z) = (1, 2, 3);  // assign immutable bindings x, y, z to 1, 2, 3

    let mut b: i32 = 10; // assign mutable binding 'b'
    b = 20; // re-assign to new Value

    /*
      Rust's 'If' is an Expression. It returns a value to initialise
      the 'Let' binding ("Declaration Statement") with. 'Let' is assigned an Expression.
      Assignment to an already bound variable is an Expression.
      Assignment Value in Rust is the special Unit Type () in Rust's Type System.
      In other languages 'If' is a Statements (does not return value).

      Rust's "Expression Statement" converts an Expression into a Statement.
      Semi-colons separate each Expression.
      Semi-colons convert Expression into Statement, throws Value away, returns Unit Type ()
      (causing Type Error when expect a different Type such as i32 to be valid)
    */
    let c: i32 = if b == 20 { 25 } else { 15 };  // Explicitely specify 'c' as of Integer Type
    println!("b is: {}", b); // Interpret interpolation with moustaches
    println!("c is: {}", c);

    incr(b, c); // Call Fn passing Args
    print_sum(b, c); // Call Fn passing Args

    // Compound Data Structure Type Tuple is simple fixed size ordered list where &str is String Slice
    let tuple1: (i32, &str) = (1, "hello"); // Mutable. Type annotated. Without: let x = (1, "hello");
    let mut tuple2: (i32, i32) = (1, 2); // Type annotated. Without: let x = (1, "hello");
    let tuple3: (i32, i32) = (3, 4);

    // Note that for a Tuple with a Single value the trailing commas are mandatory
    let (x,): (i32,) = (1,);

    if tuple2 != tuple3 {
        tuple2 = tuple3; // Assign Tuple to another Mutable Tuple (with same Arity and Types)
        println!("tuple2 and tuple3 not equal so assigning");
    } else {
        println!("tuple2 and tuple3 do not have same Arity, Types, Order, and Value, not assigning");
    }

    /*
      Destructuring Let (access parameters of Tuple).
      Let statement performs Multiple Bindings assignments when LHS and RHS Pattern Match
    */
    let (t1, t2) = tuple2;

    println!("t1 and t2 is {}", t1 + t2); // Note: Expects t1 and t2 to be same Type

    // Tuples to return Multiple Values from a Function
    let (tnt1, tnt2) = next_two(5);
    println!("tnt1, tnt2 = {}, {}", tnt1, tnt2);

    try_structs();

    try_tuple_structs();

    try_newtype();

    try_enums();

    try_pattern_matching();

    let n: i32 = 4;
    let mut o: i32 = 0;

    o = try_loops(n);

    println!("o is {}", o);

    // String Slices (aka String Literals) of Type &str are Statically Allocated (i.e. saved inside compiled program
    // and existing for entire run duration). The have fixed size and cannot be mutated (i.e. let mut).
    // stringLiteral is a binding reference to another string that is the Statically Allocated String Slice.
    let mut stringLiteral: &str = "String Slice / String Literal"; // Should not be able to declare as 'let mut'

    // In-Memory Strings (formerly StrBuf) allocate memorary and control their data. They cannot be converted into
    // the equivalent of a &'static str (Static String) like a &str, as In-Memory Strings are not pre-configured
    // to live for the entire lifetime of the compiled program like &str. They can infact be converted from String
    // into a slice &'a str by using the as_slice() method (retaining its original String lifetime)
    let mut stringInMemory: String = "In-Memory String".to_string();

    stringLiteral = try_strings(stringLiteral, stringInMemory);

    println!("stringLiteral is {}", stringLiteral);

}

/*
  Rust Structs are of Record Type form similar to Tuples.
  Elements in Structs each have a Name (aka Field/Member) and a Type
*/

// Declare a Struct with a name (camel case). Values Immutable by Default
struct PointInSpace {
    x: i32,
    y: i32
}

fn try_structs() {
  // Create Instance (Mutable) of Struct using Let and set each Field (any order)
  let mut origin = PointInSpace { x: 0, y: 0 }; // origin: PointInSpace

  origin.x = 20;

  println!("Origin is at ({}, {})", origin.x, origin.y); // Dot Notation to access Fields
}

/*
  Rust Tuple Structs are a hybrid data type similar to Tuple and Struct.
  Elements in Tuple Structs DO NOT have a Name, they only have a Type.
  Structs are preferred over Tuple Structs since they have actual Names for Elements.
  Note: Tuple Structs must use round brackets and colon
*/

struct Color (
  i32,
  i32,
  i32
);

struct PointInWater (
  i32,
  i32,
  i32
);

fn try_tuple_structs() {
  let black = Color(0, 0, 0);
  let origin = PointInWater(0, 0, 0);
}

// Declare a Newtype.
struct Inches (i32);

// Newtypes are Tuple Structs with only One Element. Beneficial for creating
// New Types similar to others (Cloning)
fn try_newtype() {
  let length = Inches(10);
  // Extract inner Integer Type by Desctructing the Let
  let Inches(integer_length) = length; // Assigns length to integer_length
  println!("Newtype length is {} inches", integer_length);
}

// Enums variants are a "Sum Type" that ties a set of alternatives to a specific name
fn try_enums() {
  // Define a Character to be either a Digit or Other. The alternative names may be
  // used via their fully scoped names i.e. Character::Other
  // Sub-datastructure types allowable in an Enum include Structs, Tuple Structs
  // Enums do not have access to operators: ==, !=, *, +, <, >=

  enum Character {
    Digit(i32), // Character::Digit is a name tied to an i32
    Missing, // Missing is just a name (not tied to a specific Type)
  }

  // Assignment
  let a  = Character::Digit(10);
  let b = Character::Missing;

  // Retrieve values contained in Enums, Error Handling where function output does not
  // match the expected Type
  match a {
    Character::Digit(n) => print!("\na is {} ", n),
    Character::Missing => print!("\na is missing"),
  }

  match b {
    Character::Digit(n) => print!("\nb is {}", n),
    Character::Missing => print!("\nb is missing"),
  }

  let x = 5;
  let y = 10;

  // // 'ordering' variable is of Ordering type (ordering: Ordering)
  // // so it contains one of the three values defined in the 'cmp' Function
  // // and we discover which one by using Conditional Operators to check
  // let ordering = cmp(x, y);

  // if ordering == Ordering::Less { println!("Less"); }
  // else if ordering == Ordering::Greater { println!("Greater"); }
  // else if ordering == Ordering::Equal { println!("Equal"); }

  // Replace the above 'if/else' with 'match' Pattern Matching expression instead
  // with less noise and which supports Exhaustiveness Checking across all
  // possible Variants of the Ordering Enum
  println!("{}", match cmp(x, y) {
    Ordering::Less => "\nless",
    Ordering::Greater => "\ngreater",
    Ordering::Equal => "\nequal",
  });

  // Equivalent to the above
  match cmp(x, y) {
    Ordering::Less => println!("\nless"),
    Ordering::Greater => println!("\ngreater"),
    Ordering::Equal => println!("\nequal"),
  };
}

// Pattern Matching is implemented by 'match' and allows elegantly deconstructing Enums
// (known as the Sum Type in Type Theory) whilst using the 'match' keyword
// instead of avoiding 'if/else' (for multiple or a complex set of option cases)
fn try_pattern_matching() {
  let x = 5;

  // 'match' takes Expression and branches into Arms depending on its Value.
  // Evaluation is performed only on the matching Arm.
  match x {
    // Arms are of the form 'val => expression'
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    4 => println!("four"),
    5 => println!("five"),
    // Exhaustiveness Checking ensures all possible patterns of Values are covered
    // with the '_' Catch-all- Arm so if any Expression used that were not included
    // (not all bases covered) it prevents a compile error (as '_' will match)
    _ => println!("something else"),
  }
}

fn try_loops(n: i32) -> (i32) {
  let mut o = 0;
  // For Loop with variable m over Iterator Expression 0..n (with start and end positions)
  // that produces a series of elements where each element is a loop itertion fetched from
  // the Iterator and bound to the variable m during each cycle through the loop body.
  // The Iterator is upper bound exclusive and so prints 0 through to n-1

  for m in 0..n {
    if m % 2 == 0 { continue; } // Approval condition only processes even iterations.
    println!("for loop with: {}", m);
    o += m;
  }

  let mut done: bool = false;

  println!("{}", o);

  while !done {
    o += o - 2;
    println!("while loop with: {}", o);
    if o % 3 == 0 { done = true; }
  }

  // Infinite Loop intentionally
  loop { // Use this instead of while true {}
    o -= 1;
    if o % 7 == 0 { break; } // End iteration early
  }

  return o;
}

// Strings are a re-sizable Data Structure.
// String is a sequence of Unicode scalar values validly encoded as a stream of UTF-8 bytes.
// Strings may contain null bytes (as they are not null-terminated)

fn try_strings(s_l: &str, s_m: String) -> &str {

  let mut _s_m: String = s_m; // Declare s_m as a mutable local variable _s_m

  let mut _s: String = s_l.to_string();
  _s.push_str(", is a growable string and is guaranteed to be UTF-8");
  println!("{}", _s);

  // Function to Convert Type &str into Type String (cheap solution that allocates it to memory)
  fn convert_to_string_in_memory_taking_string_in_memory(memory_string: String) {
    let memory_string_slice: &str = memory_string.as_slice();
    println!("Converted Type &str (String Slice / Literal) into Type String (In-Memory String): {}", memory_string_slice);
  }

  // Coerce Type &str into Type String
  convert_to_string_in_memory_taking_string_in_memory(_s_m);

  // Function to Convert Type String into Type &str
  fn convert_to_string_literal_taking_string_slice(slice: &str) {
    println!("Converted Type String (In-Memory String) into Type &str (String Slice / Literal): {}", slice);
  }

  // Coerce Type String into Type &str by prefixing with an & symbol
  convert_to_string_literal_taking_string_slice(&_s);

  // The following attempt to perform the same conversion with the parameter passed into the function gives error:
  //   - error: use of moved value: `s_m`
  //   - note: `s_m` moved here because it has type `collections::string::String`, which is moved by default
  //   - use `ref` to override

  // let mut _s_m2: String = s_m.to_string(); // Declare s_m as a mutable local variable _s_m

  // convert_to_string_literal_taking_string_slice(&_s_m2);

  return s_l;
}

// Returned Tuple is a Single Value (containing Multiple Values)
fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) }

// All control paths must Return a Value
fn incr(m: i32, n: i32) -> i32 {
    if n < m {
      return n; // Early Return
    } else {
      println!("incrementing n from: {}", n);
      return n + 10;
    }
}

/*
  Rust forces Declare expected Type of incoming Fn Args. Type Inference in Fn body.
  Rust allows Declaration expected Type of outgoing Return Value after '->'
*/
fn print_sum(m: i32, n: i32) -> () { // Return the Unit Type () (aka nil)
    if m < n {
      println!("sum with m < n is: {}", m + n);
    } else {
      println!("sum with m >= n is: {}", m + n);
    }
}<commit_msg>Demonstrated ability to mutate String Slice / Literal when not meant to be able to<commit_after>// Import the Ordering Enum from the Rust Standard Library
// http://doc.rust-lang.org/std/cmp/enum.Ordering.html
// Note: Enums are a powerful tool for data representation and 'generics' may be used
// to make Enums generic across Types to become even more powerful.
// Ordering Enum form used for Comparisons
// enum Ordering {
//   Less,
//   Equal,
//   Greater,
// }
// :: Symbol indicates a Namespace
// 'std' Module contains Sub-Module 'cmp' containing Sub-Sub-Module 'Ordering'
// The Enum Variants (Equal, Less, Greater) have been imported instead of just the Ordering
// Namespace itself to achieve compactness and convenience. This avoids full scoping and
// avoids long notation (i.e. use just Equal instead of Ordering::Equal), however name conflicts
// may arise (so it is best practice to import and apply the Namespace combined with a
// Variants together)
use std::cmp::Ordering;

// Configure the Ordering Enum
// 'cmp' is a Function that compares two given parameters and returns an Ordering
// where depending on the difference between these parameters, the returned Ordering
// may be Ordering:Less, Ordering::Greater, or Ordering::Equal
// Variants of the Enum (Ordering) are Namespaced under the Enum itself.
fn cmp(a: i32, b: i32) -> Ordering {
  if a < b      { Ordering::Less }
  else if a > b { Ordering::Greater }
  else          { Ordering::Equal }
}

/*
  Rust is an Expression-Based Systems language.
  Declare main function
*/
fn main() {

    // Metaprogramming calling a macro and passing staticaly allocated string arg
    println!("Hello, world!");

    /*
      Variable binding expression.
      <let expression> is Full Pattern for Pattern Matching.
      Rust's Type Inference automatically determines an expression's Type.
      Alternatively be explicit and supply an expression's Type after a colon.
      Or simply annotate the Type in a comment to understand the Type Rust infers.
      Rust Bindings are immutable by Default.
      Mutable Bindings require 'mut' prefix for Type Safety.
    */
    let a: i32 = 5; // assign immutable binding 'a' with Type i32 and Value 5

    // If statement based on Branch concept
    if a == 5 {
      println!("x is five");
    } else {
      println!("x is not five");
    }

    let (x, y, z) = (1, 2, 3);  // assign immutable bindings x, y, z to 1, 2, 3

    let mut b: i32 = 10; // assign mutable binding 'b'
    b = 20; // re-assign to new Value

    /*
      Rust's 'If' is an Expression. It returns a value to initialise
      the 'Let' binding ("Declaration Statement") with. 'Let' is assigned an Expression.
      Assignment to an already bound variable is an Expression.
      Assignment Value in Rust is the special Unit Type () in Rust's Type System.
      In other languages 'If' is a Statements (does not return value).

      Rust's "Expression Statement" converts an Expression into a Statement.
      Semi-colons separate each Expression.
      Semi-colons convert Expression into Statement, throws Value away, returns Unit Type ()
      (causing Type Error when expect a different Type such as i32 to be valid)
    */
    let c: i32 = if b == 20 { 25 } else { 15 };  // Explicitely specify 'c' as of Integer Type
    println!("b is: {}", b); // Interpret interpolation with moustaches
    println!("c is: {}", c);

    incr(b, c); // Call Fn passing Args
    print_sum(b, c); // Call Fn passing Args

    // Compound Data Structure Type Tuple is simple fixed size ordered list where &str is String Slice
    let tuple1: (i32, &str) = (1, "hello"); // Mutable. Type annotated. Without: let x = (1, "hello");
    let mut tuple2: (i32, i32) = (1, 2); // Type annotated. Without: let x = (1, "hello");
    let tuple3: (i32, i32) = (3, 4);

    // Note that for a Tuple with a Single value the trailing commas are mandatory
    let (x,): (i32,) = (1,);

    if tuple2 != tuple3 {
        tuple2 = tuple3; // Assign Tuple to another Mutable Tuple (with same Arity and Types)
        println!("tuple2 and tuple3 not equal so assigning");
    } else {
        println!("tuple2 and tuple3 do not have same Arity, Types, Order, and Value, not assigning");
    }

    /*
      Destructuring Let (access parameters of Tuple).
      Let statement performs Multiple Bindings assignments when LHS and RHS Pattern Match
    */
    let (t1, t2) = tuple2;

    println!("t1 and t2 is {}", t1 + t2); // Note: Expects t1 and t2 to be same Type

    // Tuples to return Multiple Values from a Function
    let (tnt1, tnt2) = next_two(5);
    println!("tnt1, tnt2 = {}, {}", tnt1, tnt2);

    try_structs();

    try_tuple_structs();

    try_newtype();

    try_enums();

    try_pattern_matching();

    let n: i32 = 4;
    let mut o: i32 = 0;

    o = try_loops(n);

    println!("o is {}", o);

    // String Slices (aka String Literals) of Type &str are Statically Allocated (i.e. saved inside compiled program
    // and existing for entire run duration). The have fixed size and cannot be mutated (i.e. let mut).
    // stringLiteral is a binding reference to another string that is the Statically Allocated String Slice.
    let mut stringLiteral: &str = "String Slice / String Literal"; // Should not be able to declare as 'let mut'

    // In-Memory Strings (formerly StrBuf) allocate memorary and control their data. They cannot be converted into
    // the equivalent of a &'static str (Static String) like a &str, as In-Memory Strings are not pre-configured
    // to live for the entire lifetime of the compiled program like &str. They can infact be converted from String
    // into a slice &'a str by using the as_slice() method (retaining its original String lifetime)
    let mut stringInMemory: String = "In-Memory String".to_string();

    stringLiteral = try_strings(stringLiteral, stringInMemory);

    println!("stringLiteral is {}", stringLiteral);

}

/*
  Rust Structs are of Record Type form similar to Tuples.
  Elements in Structs each have a Name (aka Field/Member) and a Type
*/

// Declare a Struct with a name (camel case). Values Immutable by Default
struct PointInSpace {
    x: i32,
    y: i32
}

fn try_structs() {
  // Create Instance (Mutable) of Struct using Let and set each Field (any order)
  let mut origin = PointInSpace { x: 0, y: 0 }; // origin: PointInSpace

  origin.x = 20;

  println!("Origin is at ({}, {})", origin.x, origin.y); // Dot Notation to access Fields
}

/*
  Rust Tuple Structs are a hybrid data type similar to Tuple and Struct.
  Elements in Tuple Structs DO NOT have a Name, they only have a Type.
  Structs are preferred over Tuple Structs since they have actual Names for Elements.
  Note: Tuple Structs must use round brackets and colon
*/

struct Color (
  i32,
  i32,
  i32
);

struct PointInWater (
  i32,
  i32,
  i32
);

fn try_tuple_structs() {
  let black = Color(0, 0, 0);
  let origin = PointInWater(0, 0, 0);
}

// Declare a Newtype.
struct Inches (i32);

// Newtypes are Tuple Structs with only One Element. Beneficial for creating
// New Types similar to others (Cloning)
fn try_newtype() {
  let length = Inches(10);
  // Extract inner Integer Type by Desctructing the Let
  let Inches(integer_length) = length; // Assigns length to integer_length
  println!("Newtype length is {} inches", integer_length);
}

// Enums variants are a "Sum Type" that ties a set of alternatives to a specific name
fn try_enums() {
  // Define a Character to be either a Digit or Other. The alternative names may be
  // used via their fully scoped names i.e. Character::Other
  // Sub-datastructure types allowable in an Enum include Structs, Tuple Structs
  // Enums do not have access to operators: ==, !=, *, +, <, >=

  enum Character {
    Digit(i32), // Character::Digit is a name tied to an i32
    Missing, // Missing is just a name (not tied to a specific Type)
  }

  // Assignment
  let a  = Character::Digit(10);
  let b = Character::Missing;

  // Retrieve values contained in Enums, Error Handling where function output does not
  // match the expected Type
  match a {
    Character::Digit(n) => print!("\na is {} ", n),
    Character::Missing => print!("\na is missing"),
  }

  match b {
    Character::Digit(n) => print!("\nb is {}", n),
    Character::Missing => print!("\nb is missing"),
  }

  let x = 5;
  let y = 10;

  // // 'ordering' variable is of Ordering type (ordering: Ordering)
  // // so it contains one of the three values defined in the 'cmp' Function
  // // and we discover which one by using Conditional Operators to check
  // let ordering = cmp(x, y);

  // if ordering == Ordering::Less { println!("Less"); }
  // else if ordering == Ordering::Greater { println!("Greater"); }
  // else if ordering == Ordering::Equal { println!("Equal"); }

  // Replace the above 'if/else' with 'match' Pattern Matching expression instead
  // with less noise and which supports Exhaustiveness Checking across all
  // possible Variants of the Ordering Enum
  println!("{}", match cmp(x, y) {
    Ordering::Less => "\nless",
    Ordering::Greater => "\ngreater",
    Ordering::Equal => "\nequal",
  });

  // Equivalent to the above
  match cmp(x, y) {
    Ordering::Less => println!("\nless"),
    Ordering::Greater => println!("\ngreater"),
    Ordering::Equal => println!("\nequal"),
  };
}

// Pattern Matching is implemented by 'match' and allows elegantly deconstructing Enums
// (known as the Sum Type in Type Theory) whilst using the 'match' keyword
// instead of avoiding 'if/else' (for multiple or a complex set of option cases)
fn try_pattern_matching() {
  let x = 5;

  // 'match' takes Expression and branches into Arms depending on its Value.
  // Evaluation is performed only on the matching Arm.
  match x {
    // Arms are of the form 'val => expression'
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    4 => println!("four"),
    5 => println!("five"),
    // Exhaustiveness Checking ensures all possible patterns of Values are covered
    // with the '_' Catch-all- Arm so if any Expression used that were not included
    // (not all bases covered) it prevents a compile error (as '_' will match)
    _ => println!("something else"),
  }
}

fn try_loops(n: i32) -> (i32) {
  let mut o = 0;
  // For Loop with variable m over Iterator Expression 0..n (with start and end positions)
  // that produces a series of elements where each element is a loop itertion fetched from
  // the Iterator and bound to the variable m during each cycle through the loop body.
  // The Iterator is upper bound exclusive and so prints 0 through to n-1

  for m in 0..n {
    if m % 2 == 0 { continue; } // Approval condition only processes even iterations.
    println!("for loop with: {}", m);
    o += m;
  }

  let mut done: bool = false;

  println!("{}", o);

  while !done {
    o += o - 2;
    println!("while loop with: {}", o);
    if o % 3 == 0 { done = true; }
  }

  // Infinite Loop intentionally
  loop { // Use this instead of while true {}
    o -= 1;
    if o % 7 == 0 { break; } // End iteration early
  }

  return o;
}

// Strings are a re-sizable Data Structure.
// String is a sequence of Unicode scalar values validly encoded as a stream of UTF-8 bytes.
// Strings may contain null bytes (as they are not null-terminated)

fn try_strings(s_l: &str, s_m: String) -> &str {

  let mut _s_m: String = s_m; // Declare s_m as a mutable local variable _s_m

  let mut _s: String = s_l.to_string();
  _s.push_str(", is a growable string and is guaranteed to be UTF-8");
  println!("{}", _s);

  // Function to Convert Type &str into Type String (cheap solution that allocates it to memory)
  fn convert_to_string_in_memory_taking_string_in_memory(memory_string: String) {
    let memory_string_slice: &str = memory_string.as_slice();
    println!("Converted Type &str (String Slice / Literal) into Type String (In-Memory String): {}", memory_string_slice);
  }

  // Coerce Type &str into Type String
  convert_to_string_in_memory_taking_string_in_memory(_s_m);

  // Function to Convert Type String into Type &str
  fn convert_to_string_literal_taking_string_slice(slice: &str) {
    println!("Converted Type String (In-Memory String) into Type &str (String Slice / Literal): {}", slice);
  }

  // Coerce Type String into Type &str by prefixing with an & symbol
  convert_to_string_literal_taking_string_slice(&_s);

  // The following attempt to perform the same conversion with the parameter passed into the function gives error:
  //   - error: use of moved value: `s_m`
  //   - note: `s_m` moved here because it has type `collections::string::String`, which is moved by default
  //   - use `ref` to override

  // let mut _s_m2: String = s_m.to_string(); // Declare s_m as a mutable local variable _s_m

  // convert_to_string_literal_taking_string_slice(&_s_m2);

  return "Test String to override the value of the String Slice / Literal that we are supposedly not meant to be able to mutate";
}

// Returned Tuple is a Single Value (containing Multiple Values)
fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) }

// All control paths must Return a Value
fn incr(m: i32, n: i32) -> i32 {
    if n < m {
      return n; // Early Return
    } else {
      println!("incrementing n from: {}", n);
      return n + 10;
    }
}

/*
  Rust forces Declare expected Type of incoming Fn Args. Type Inference in Fn body.
  Rust allows Declaration expected Type of outgoing Return Value after '->'
*/
fn print_sum(m: i32, n: i32) -> () { // Return the Unit Type () (aka nil)
    if m < n {
      println!("sum with m < n is: {}", m + n);
    } else {
      println!("sum with m >= n is: {}", m + n);
    }
}<|endoftext|>