<commit_before>// Copyright (c) 2015 - 2016, Alberto Corona <ac@albertocorona.com>
// All rights reserved. This file is part of yabs, distributed under the BSD
// 3-Clause license. For full terms please see the LICENSE file.

extern crate toml;
extern crate rustc_serialize;
extern crate walkdir;
extern crate rpf;

use toml::decode;
use error::YabsError;
use rpf::*;
use ext::*;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, json};
use walkdir::{WalkDir, WalkDirIterator, DirEntry};

use std::ffi::OsStr;
use std::fs::File;
use std::io::Write;

#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone,PartialEq)]
pub struct Profile {
    name: String,
    proj_desc: Option<ProjDesc>,
    inst_desc: Option<InstallDesc>,
    doc_desc: Option<DocDesc>,
}

#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone)]
pub struct BuildFile {
    profiles: Vec<Profile>,
}

impl BuildFile {
    pub fn from_file(file: &str) -> Result<BuildFile, Vec<YabsError>> {
        let mut build_file: BuildFile = Default::default();
        parse_toml_file(file)
            .and_then(|toml| {
                for (key, table) in toml {
                    let mut profile: Profile = Default::default();
                    profile.name = key.clone();
                    match table {
                        toml::Value::Table(inner_table) => {
                            for (key, table) in inner_table {
                                match key.as_ref() {
                                    "project" => {
                                        profile.proj_desc = ProjDesc::from_toml_table(table).ok()
                                    }
                                    "install" => {
                                        profile.inst_desc = InstallDesc::from_toml_table(table).ok()
                                    }
                                    "doc" => {
                                        profile.doc_desc = DocDesc::from_toml_table(table).ok()
                                    }
                                    _ => (),
                                }
                            }
                        }
                        _ => (),
                    };
                    build_file.profiles.push(profile);
                }
                Ok(build_file)
            })
            .map_err(|err| err)
    }

    pub fn print_as_json(&self) {
        for profile in &self.profiles {
            profile.print_json();
        }
    }

    pub fn print_available_profiles(&self) {
        for profile in &self.profiles {
            print!("{} ", profile.name);
        }
        print!("\n");
    }

    pub fn print_profile_as_json(&self, name: String) {
        for profile in &self.profiles {
            if profile.name == name {
                profile.print_json();
            }
        }
    }

    pub fn gen_make(&self, name: String) -> Result<(), YabsError> {
        if let Some(index) = self.profiles.iter().position(|ref profile| profile.name == name) {
            try!(self.profiles[index.clone()]
                     .clone()
                     .proj_desc
                     .unwrap_or(ProjDesc::new())
                     .gen_file_list());
            try!(try!(File::create("Makefile")).write_all(try!(self.profiles[index.clone()]
                                                                   .clone()
                                                                   .proj_desc
                                                                   .unwrap_or(ProjDesc::new())
                                                                   .gen_make())
                                                              .as_bytes()));
            Ok(())
        } else {
            Err(YabsError::NoDesc(name))
        }
    }

    pub fn print_sources(&mut self) -> Result<(), YabsError> {
        for profile in &mut self.profiles {
            if let &mut Some(ref mut proj) = &mut profile.proj_desc {
                println!("{}", profile.name.bold());
                try!(proj.gen_file_list());
                if let Some(set_sources) = proj.src.as_ref() {
                    for src in set_sources {
                        println!("{}", src);
                    }
                }
            }
        }
        Ok(())
    }
}

#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone,PartialEq)]
pub struct ProjDesc {
    name: Option<String>,
    target: Option<String>,
    lang: Option<String>,
    os: Option<String>,
    version: Option<String>,
    compiler: Option<String>,
    src: Option<Vec<String>>,
    libs: Option<Vec<String>>,
    lib_dir: Option<Vec<String>>,
    inc: Option<Vec<String>>,
    inc_dir: Option<Vec<String>>,
    cflags: Option<Vec<String>>,
    explicit_cflags: Option<String>,
    lflags: Option<String>,
    ignore: Option<Vec<String>>,
    before_script: Option<Vec<String>>,
    after_script: Option<Vec<String>>,
    lib: Option<bool>,
    ar: Option<String>,
    clean: Option<Vec<String>>,
}

impl ProjDesc {
    fn concat_clean(&self) -> String {
        self.prepend_op_vec(&self.clean, "".to_string())
    }

    fn is_in_ignore(&self, entry: &DirEntry) -> bool {
        if let Some(ignore) = self.ignore.as_ref() {
            for path in ignore {
                let entry = entry.path().as_string();
                if entry.find(path).is_some() {
                    return true;
                }
            }
        }
        false
    }

    fn gen_file_list(&mut self) -> Result<(), YabsError> {
        if self.src.is_some() {
            return Ok(());
        }
        let mut sources = Vec::new();
        let walk_dir = WalkDir::new(".").into_iter();
        for entry in walk_dir.filter_entry(|e| !&self.is_in_ignore(e)) {
            let entry = try!(entry);
            if entry.path().is_file() {
                let file_ext = entry.path().extension().unwrap_or(OsStr::new(""));
                if let Some(ext) = file_ext.to_str() {
                    if let Some(lang) = self.lang.as_ref() {
                        if ext == lang {
                            let mut entry = entry.path().as_string();
                            if entry.len() > 2 {
                                entry.remove(0);
                                entry.remove(0);
                            }
                            sources.push(entry);
                        }
                    }
                }
            }
        }
        self.src = Some(sources);
        Ok(())
    }

    fn prepend_op_vec(&self, list: &Option<Vec<String>>, prepend: String) -> String {
        let mut horrid_string = String::new();
        if let Some(items) = list.as_ref() {
            if let Some(split_last) = items.split_last() {
                for sub_item in split_last.1 {
                    if self.is_command(&sub_item) {
                        horrid_string.push_str(&format!("{} ", sub_item));
                    } else {
                        horrid_string.push_str(&format!("{}{} ", prepend, sub_item));
                    }
                }
                if self.is_command(&split_last.0) {
                    horrid_string.push_str(&format!("{} ", split_last.0));
                } else {
                    horrid_string.push_str(&format!("{}{}", prepend, split_last.0.clone()));
                }
            }
        }
        return horrid_string;
    }

    fn is_command(&self, string: &String) -> bool {
        if string.starts_with("`") {
            return true;
        } else {
            return false;
        }
    }

    fn gen_make_lib_list(&self) -> String {
        return self.prepend_op_vec(&self.libs, "-l".to_string());
    }

    fn gen_make_cflags_list(&self) -> String {
        return self.prepend_op_vec(&self.cflags, "-".to_string());
    }

    fn gen_make_src_list(&self) -> String {
        let mut horrid_string: String = "SRC\t= ".to_owned();
        let mut lang = self.lang.clone().unwrap_or("cpp".to_owned());
        lang.insert(0, '.');
        if let Some(source_list) = self.src.as_ref() {
            if let Some(split_first) = source_list.split_first() {
                horrid_string.push_str(&format!("{} \\\n", split_first.0));
                if let Some(split_last) = split_first.1.clone().split_last() {
                    for src in split_last.1 {
                        horrid_string.push_str(&format!("\t{} \\\n", src));
                    }
                    horrid_string.push_str(&format!("\t{}\n", split_last.0));
                }
            }
            let mut parsed_obj_list = Vec::new();
            for obj in source_list {
                parsed_obj_list.push(obj.replace(&lang, ".o"));
            }
            horrid_string.push_str("OBJ\t= ");
            if let Some(split_first) = parsed_obj_list.split_first() {
                horrid_string.push_str(&format!("{} \\\n", split_first.0));
                if let Some(split_last) = split_first.1.clone().split_last() {
                    for src in split_last.1 {
                        horrid_string.push_str(&format!("\t{} \\\n", src));
                    }
                    horrid_string.push_str(&format!("\t{}", split_last.0));
                }
            }
        }
        return horrid_string;
    }

    fn gen_make_src_deps(&self) -> String {
        let mut horrid_string = String::new();
        let mut lang = self.lang.clone().unwrap_or("cpp".to_owned());
        lang.insert(0, '.');
        if let Some(source_list) = self.src.as_ref() {
            for src in source_list {
                horrid_string.push_str(&format!("{0}: {1}\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \
                                                 {0} {1}\n\n",
                                                src.replace(&lang, ".o"),
                                                src));
            }
        }
        return horrid_string;
    }

    fn gen_make_inc_list(&self) -> String {
        return self.prepend_op_vec(&self.inc, "-I".to_string());
    }

    fn gen_make(&mut self) -> Result<String, YabsError> {
        try!(self.gen_file_list());
        Ok(format!(
            "INSTALL\t= /usr/bin/env install\n\
                AR\t= /usr/bin/env ar\n\
                DEST\t=\n\
                PREFIX\t=\n\
                CC\t= {compiler}\n\
                BINDIR\t=\n\
                LIBDIR\t=\n\
                TARGET\t= {target}\n\
                LINK\t=\n\
                CFLAGS\t= {cflags}\n\
                LFLAGS\t=\n\
                LIBS\t= {libs}\n\
                INCDIR\t= {incdir}\n\
                LIBDIR\t=\n\
                CLEAN\t=\n\
                DEL\t= rm -f\n\
                {srcs}\n\n\
                first: all\n\n\
                .PHONY: doc clean\n\n\
                .SUFFIXES: .o .c .cpp .cc .cxx .C\n\n\
                .cpp.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\
                .cc.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                .cxx.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                .C.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                .c.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                all: $(TARGET)\n\n\
                $(TARGET): $(OBJ)\n\
                \t$(CC) $(LFLAGS) -o $(TARGET) $(OBJ) $(LIBS)\n\n\
                {dep_list}\n\
                clean:\n\
                \t$(DEL) $(OBJ)\n\
                \t$(DEL) {target}\n\
                \t$(DEL) {clean_list}\n\
                ",
                compiler = &self.compiler.clone().unwrap_or("gcc".to_owned()),
                target = &self.target.clone().unwrap_or("a".to_owned()),
                cflags = &self.gen_make_cflags_list(),
                libs = &self.gen_make_lib_list(),
                incdir = &self.gen_make_inc_list(),
                srcs = &self.gen_make_src_list(),
                dep_list = &self.gen_make_src_deps(),
                clean_list = &self.concat_clean()))
    }
}

// Descibe how to install this project
#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone,PartialEq)]
pub struct InstallDesc {
    prefix: Option<String>,
}

#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone,PartialEq)]
pub struct DocDesc {
    doc: Option<String>,
}

// General trait for any descriptions
pub trait Desc<T> {
    fn new() -> T;
    fn from_file(file: &str, name: &str) -> Result<T, Vec<YabsError>>;
    fn from_toml_table(table: toml::Value) -> Result<T, YabsError>;
    fn print_json(&self);
}

impl<T: Decodable + Encodable + Default> Desc<T> for T {
    fn new() -> T {
        Default::default()
    }

    fn from_file(file: &str, name: &str) -> Result<T, Vec<YabsError>> {
        parse_toml_file(file).and_then(|toml| {
            toml.get(name)
                .ok_or(vec![YabsError::NoDesc(name.to_owned())])
                .and_then(|desc| {
                    Decodable::decode(&mut toml::Decoder::new(desc.clone()))
                        .map_err(|err| vec![YabsError::TomlDecode(err)])
                })
        })
    }

    fn from_toml_table(table: toml::Value) -> Result<T, YabsError> {
        Ok(try!(Decodable::decode(&mut toml::Decoder::new(table.clone()))))
    }

    fn print_json(&self) {
        println!("{}", json::as_pretty_json(&self));
    }
}
<commit_msg>yabs: support static libs<commit_after>// Copyright (c) 2015 - 2016, Alberto Corona <ac@albertocorona.com>
// All rights reserved. This file is part of yabs, distributed under the BSD
// 3-Clause license. For full terms please see the LICENSE file.

extern crate toml;
extern crate rustc_serialize;
extern crate walkdir;
extern crate rpf;

use toml::decode;
use error::YabsError;
use rpf::*;
use ext::*;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, json};
use walkdir::{WalkDir, WalkDirIterator, DirEntry};

use std::ffi::OsStr;
use std::fs::File;
use std::io::Write;

#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone,PartialEq)]
pub struct Profile {
    name: String,
    proj_desc: Option<ProjDesc>,
    inst_desc: Option<InstallDesc>,
    doc_desc: Option<DocDesc>,
}

#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone)]
pub struct BuildFile {
    profiles: Vec<Profile>,
}

impl BuildFile {
    pub fn from_file(file: &str) -> Result<BuildFile, Vec<YabsError>> {
        let mut build_file: BuildFile = Default::default();
        parse_toml_file(file)
            .and_then(|toml| {
                for (key, table) in toml {
                    let mut profile: Profile = Default::default();
                    profile.name = key.clone();
                    match table {
                        toml::Value::Table(inner_table) => {
                            for (key, table) in inner_table {
                                match key.as_ref() {
                                    "project" => {
                                        profile.proj_desc = ProjDesc::from_toml_table(table).ok()
                                    }
                                    "install" => {
                                        profile.inst_desc = InstallDesc::from_toml_table(table).ok()
                                    }
                                    "doc" => {
                                        profile.doc_desc = DocDesc::from_toml_table(table).ok()
                                    }
                                    _ => (),
                                }
                            }
                        }
                        _ => (),
                    };
                    build_file.profiles.push(profile);
                }
                Ok(build_file)
            })
            .map_err(|err| err)
    }

    pub fn print_as_json(&self) {
        for profile in &self.profiles {
            profile.print_json();
        }
    }

    pub fn print_available_profiles(&self) {
        for profile in &self.profiles {
            print!("{} ", profile.name);
        }
        print!("\n");
    }

    pub fn print_profile_as_json(&self, name: String) {
        for profile in &self.profiles {
            if profile.name == name {
                profile.print_json();
            }
        }
    }

    pub fn gen_make(&self, name: String) -> Result<(), YabsError> {
        if let Some(index) = self.profiles.iter().position(|ref profile| profile.name == name) {
            try!(self.profiles[index.clone()]
                     .clone()
                     .proj_desc
                     .unwrap_or(ProjDesc::new())
                     .gen_file_list());
            try!(try!(File::create("Makefile")).write_all(try!(self.profiles[index.clone()]
                                                                   .clone()
                                                                   .proj_desc
                                                                   .unwrap_or(ProjDesc::new())
                                                                   .gen_make())
                                                              .as_bytes()));
            Ok(())
        } else {
            Err(YabsError::NoDesc(name))
        }
    }

    pub fn print_sources(&mut self) -> Result<(), YabsError> {
        for profile in &mut self.profiles {
            if let &mut Some(ref mut proj) = &mut profile.proj_desc {
                println!("{}", profile.name.bold());
                try!(proj.gen_file_list());
                if let Some(set_sources) = proj.src.as_ref() {
                    for src in set_sources {
                        println!("{}", src);
                    }
                }
            }
        }
        Ok(())
    }
}

#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone,PartialEq)]
pub struct ProjDesc {
    name: Option<String>,
    target: Option<String>,
    lang: Option<String>,
    os: Option<String>,
    version: Option<String>,
    compiler: Option<String>,
    src: Option<Vec<String>>,
    libs: Option<Vec<String>>,
    lib_dir: Option<Vec<String>>,
    inc: Option<Vec<String>>,
    inc_dir: Option<Vec<String>>,
    cflags: Option<Vec<String>>,
    explicit_cflags: Option<String>,
    lflags: Option<String>,
    ignore: Option<Vec<String>>,
    before_script: Option<Vec<String>>,
    after_script: Option<Vec<String>>,
    static_lib: Option<bool>,
    ar: Option<String>,
    arflags: Option<String>,
    clean: Option<Vec<String>>,
}

impl ProjDesc {
    fn concat_clean(&self) -> String {
        self.prepend_op_vec(&self.clean, "".to_string())
    }

    fn is_in_ignore(&self, entry: &DirEntry) -> bool {
        if let Some(ignore) = self.ignore.as_ref() {
            for path in ignore {
                let entry = entry.path().as_string();
                if entry.find(path).is_some() {
                    return true;
                }
            }
        }
        false
    }

    fn gen_file_list(&mut self) -> Result<(), YabsError> {
        if self.src.is_some() {
            return Ok(());
        }
        let mut sources = Vec::new();
        let walk_dir = WalkDir::new(".").into_iter();
        for entry in walk_dir.filter_entry(|e| !&self.is_in_ignore(e)) {
            let entry = try!(entry);
            if entry.path().is_file() {
                let file_ext = entry.path().extension().unwrap_or(OsStr::new(""));
                if let Some(ext) = file_ext.to_str() {
                    if let Some(lang) = self.lang.as_ref() {
                        if ext == lang {
                            let mut entry = entry.path().as_string();
                            if entry.len() > 2 {
                                entry.remove(0);
                                entry.remove(0);
                            }
                            sources.push(entry);
                        }
                    }
                }
            }
        }
        self.src = Some(sources);
        Ok(())
    }

    fn prepend_op_vec(&self, list: &Option<Vec<String>>, prepend: String) -> String {
        let mut horrid_string = String::new();
        if let Some(items) = list.as_ref() {
            if let Some(split_last) = items.split_last() {
                for sub_item in split_last.1 {
                    if self.is_command(&sub_item) {
                        horrid_string.push_str(&format!("{} ", sub_item));
                    } else {
                        horrid_string.push_str(&format!("{}{} ", prepend, sub_item));
                    }
                }
                if self.is_command(&split_last.0) {
                    horrid_string.push_str(&format!("{} ", split_last.0));
                } else {
                    horrid_string.push_str(&format!("{}{}", prepend, split_last.0.clone()));
                }
            }
        }
        return horrid_string;
    }

    fn is_command(&self, string: &String) -> bool {
        if string.starts_with("`") {
            return true;
        } else {
            return false;
        }
    }

    fn gen_make_lib_list(&self) -> String {
        return self.prepend_op_vec(&self.libs, "-l".to_string());
    }

    fn gen_make_cflags_list(&self) -> String {
        return self.prepend_op_vec(&self.cflags, "-".to_string());
    }

    fn gen_make_src_list(&self) -> String {
        let mut horrid_string: String = "SRC\t= ".to_owned();
        let mut lang = self.lang.clone().unwrap_or("cpp".to_owned());
        lang.insert(0, '.');
        if let Some(source_list) = self.src.as_ref() {
            if let Some(split_first) = source_list.split_first() {
                horrid_string.push_str(&format!("{} \\\n", split_first.0));
                if let Some(split_last) = split_first.1.clone().split_last() {
                    for src in split_last.1 {
                        horrid_string.push_str(&format!("\t{} \\\n", src));
                    }
                    horrid_string.push_str(&format!("\t{}\n", split_last.0));
                }
            }
            let mut parsed_obj_list = Vec::new();
            for obj in source_list {
                parsed_obj_list.push(obj.replace(&lang, ".o"));
            }
            horrid_string.push_str("OBJ\t= ");
            if let Some(split_first) = parsed_obj_list.split_first() {
                horrid_string.push_str(&format!("{} \\\n", split_first.0));
                if let Some(split_last) = split_first.1.clone().split_last() {
                    for src in split_last.1 {
                        horrid_string.push_str(&format!("\t{} \\\n", src));
                    }
                    horrid_string.push_str(&format!("\t{}", split_last.0));
                }
            }
        }
        return horrid_string;
    }

    fn gen_make_src_deps(&self) -> String {
        let mut horrid_string = String::new();
        let mut lang = self.lang.clone().unwrap_or("cpp".to_owned());
        lang.insert(0, '.');
        if let Some(source_list) = self.src.as_ref() {
            for src in source_list {
                horrid_string.push_str(&format!("{0}: {1}\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \
                                                 {0} {1}\n\n",
                                                src.replace(&lang, ".o"),
                                                src));
            }
        }
        return horrid_string;
    }

    fn gen_make_inc_list(&self) -> String {
        return self.prepend_op_vec(&self.inc, "-I".to_string());
    }

    fn gen_make(&mut self) -> Result<String, YabsError> {
        try!(self.gen_file_list());
        let mut target_string = String::new();
        if let Some(static_lib) = self.static_lib {
            if static_lib == true {
                target_string = format!("$(TARGET): $(OBJ)\n\
                \t$(AR) $(ARFLAGS) $(TARGET) $(OBJ)\n\n");
            } else {
                target_string = format!("$(TARGET): $(OBJ)\n\
                \t$(CC) $(LFLAGS) -o $(TARGET) $(OBJ) $(LIBS)\n\n");
            }
        };
        Ok(format!(
            "INSTALL\t= /usr/bin/env install\n\
                AR\t= {ar}\n\
                ARFLAGS\t= {ar_flags}\n\
                DEST\t=\n\
                PREFIX\t=\n\
                CC\t= {compiler}\n\
                BINDIR\t=\n\
                LIBDIR\t=\n\
                TARGET\t= {target}\n\
                LINK\t=\n\
                CFLAGS\t= {cflags}\n\
                LFLAGS\t=\n\
                LIBS\t= {libs}\n\
                INCDIR\t= {incdir}\n\
                LIBDIR\t=\n\
                CLEAN\t=\n\
                DEL\t= rm -f\n\
                {srcs}\n\n\
                first: all\n\n\
                .PHONY: doc clean\n\n\
                .SUFFIXES: .o .c .cpp .cc .cxx .C\n\n\
                .cpp.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                .cc.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                .cxx.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                .C.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                .c.o:\n\t$(CC) -c $(CFLAGS) $(INCDIR) -o \"$@\" \"$<\"\n\n\
                all: $(TARGET)\n\n\
                {target_command}\
                {dep_list}\n\
                clean:\n\
                \t$(DEL) $(OBJ)\n\
                \t$(DEL) {target}\n\
                \t$(DEL) {clean_list}\n\
                ",
                compiler = &self.compiler.as_ref().unwrap_or(&"gcc".to_owned()),
                ar = &self.ar.as_ref().unwrap_or(&"/usr/bin/env ar".to_owned()),
                ar_flags = &self.arflags.as_ref().unwrap_or(&"rcs".to_owned()),
                target = &self.target.as_ref().unwrap_or(&"a".to_owned()),
                target_command = target_string,
                cflags = &self.gen_make_cflags_list(),
                libs = &self.gen_make_lib_list(),
                incdir = &self.gen_make_inc_list(),
                srcs = &self.gen_make_src_list(),
                dep_list = &self.gen_make_src_deps(),
                clean_list = &self.concat_clean()))
    }
}

// Descibe how to install this project
#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone,PartialEq)]
pub struct InstallDesc {
    prefix: Option<String>,
}

#[derive(Debug,Default,RustcDecodable,RustcEncodable,Clone,PartialEq)]
pub struct DocDesc {
    doc: Option<String>,
}

// General trait for any descriptions
pub trait Desc<T> {
    fn new() -> T;
    fn from_file(file: &str, name: &str) -> Result<T, Vec<YabsError>>;
    fn from_toml_table(table: toml::Value) -> Result<T, YabsError>;
    fn print_json(&self);
}

impl<T: Decodable + Encodable + Default> Desc<T> for T {
    fn new() -> T {
        Default::default()
    }

    fn from_file(file: &str, name: &str) -> Result<T, Vec<YabsError>> {
        parse_toml_file(file).and_then(|toml| {
            toml.get(name)
                .ok_or(vec![YabsError::NoDesc(name.to_owned())])
                .and_then(|desc| {
                    Decodable::decode(&mut toml::Decoder::new(desc.clone()))
                        .map_err(|err| vec![YabsError::TomlDecode(err)])
                })
        })
    }

    fn from_toml_table(table: toml::Value) -> Result<T, YabsError> {
        Ok(try!(Decodable::decode(&mut toml::Decoder::new(table.clone()))))
    }

    fn print_json(&self) {
        println!("{}", json::as_pretty_json(&self));
    }
}
<|endoftext|>


<commit_before>const _GRAMMAR: &'static str = include_str!("grammar.pest");

#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct HandlebarsParser;

#[test]
fn test_raw_text() {
    let s = vec![
        "<h1> helloworld </h1>    ",
        "hello\\{{world}}",
        "hello\\{{#if world}}nice\\{{/if}}",
        "hello \\{{{{raw}}}}hello\\{{{{/raw}}}}",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::raw_text, i).is_ok());
    }
}

#[test]
fn test_raw_block_text() {
    let s = "<h1> {{hello}} </h1>";
    assert!(HandlebarsParser::parse_str(Rule::raw_block_text, s).is_ok());
}

#[test]
fn test_reference() {
    let s = vec![
        "a",
        "abc",
        "../a",
        "a.b",
        "@abc",
        "a.[abc]",
        "aBc.[abc]",
        "abc.[0].[nice]",
        "some-name",
        "this.[0].ok",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::reference(), i).is_ok());
    }
}

#[test]
fn test_name() {
    let s = vec!["if", "(abc)"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::name, i).is_ok());
    }
}

#[test]
fn test_param() {
    let s = vec!["hello", "\"json literal\""];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::param, i).is_ok());
    }
}

#[test]
fn test_hash() {
    let s = vec![
        "hello=world",
        "hello=\"world\"",
        "hello=(world)",
        "hello=(world 0)",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::hash, i).is_ok());
    }
}

#[test]
fn test_json_literal() {
    let s = vec![
        "\"json string\"",
        "\"quot: \\\"\"",
        "[]",
        "[\"hello\"]",
        "[1,2,3,4,true]",
        "{\"hello\": \"world\"}",
        "{}",
        "{\"a\":1, \"b\":2 }",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::literal, i).is_ok());
    }
}

#[test]
fn test_comment() {
    let s = vec!["{{! hello }}"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::comment, i).is_ok());
    }
}

#[test]
fn test_subexpression() {
    let s = vec!["(sub)", "(sub 0)", "(sub a=1)"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::subexpression, i).is_ok());
    }
}

#[test]
fn test_expression() {
    let s = vec!["{{exp}}", "{{(exp)}}", "{{this.name}}", "{{this.[0].name}}"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::expression, i).is_ok());
    }
}

#[test]
fn test_helper_expression() {
    let s = vec![
        "{{exp 1}}",
        "{{exp \"literal\"}}",
        "{{exp ref}}",
        "{{exp (sub)}}",
        "{{exp (sub 123)}}",
        "{{exp []}}",
        "{{exp {}}}",
        "{{exp key=1}}",
        "{{exp key=ref}}",
        "{{exp key=(sub)}}",
        "{{exp key=(sub 0)}}",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::helper_expression, i).is_ok());
    }
}

#[test]
fn test_identifier_with_dash() {
    let s = vec!["{{exp-foo}}"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::expression, i).is_ok());
    }
}

#[test]
fn test_html_expression() {
    let s = vec!["{{{html}}}", "{{{(html)}}}", "{{{(html)}}}"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::html_expression, i).is_ok());
    }
}

#[test]
fn test_helper_start() {
    let s = vec![
        "{{#if hello}}",
        "{{#if (hello)}}",
        "{{#if hello=world}}",
        "{{#if hello hello=world}}",
        "{{#if []}}",
        "{{#if {}}}",
        "{{#if}}",
        "{{~#if hello~}}",
        "{{#each people as |person|}}",
        "{{#each-obj obj as |key val|}}",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::helper_block_start, i).is_ok());
    }
}

#[test]
fn test_helper_end() {
    let s = vec!["{{/if}}", "{{~/if}}", "{{~/if ~}}", "{{/if   ~}}"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::helper_block_end, i).is_ok());
    }
}

#[test]
fn test_helper_block() {
    let s = vec![
        "{{#if hello}}hello{{/if}}",
        "{{#if true}}hello{{/if}}",
        "{{#if nice ok=1}}hello{{/if}}",
        "{{#if}}hello{{else}}world{{/if}}",
        "{{#if}}hello{{^}}world{{/if}}",
        "{{#if}}{{#if}}hello{{/if}}{{/if}}",
        "{{#if}}hello{{~else}}world{{/if}}",
        "{{#if}}hello{{else~}}world{{/if}}",
        "{{#if}}hello{{~^~}}world{{/if}}",
        "{{#if}}{{/if}}",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::helper_block, i).is_ok());
    }
}

#[test]
fn test_raw_block() {
    let s = vec![
        "{{{{if hello}}}}good {{hello}}{{{{/if}}}}",
        "{{{{if hello}}}}{{#if nice}}{{/if}}{{{{/if}}}}",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::raw_block, i).is_ok());
    }
}

#[test]
fn test_block_param() {
    let s = vec!["as |person|", "as |key val|"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::block_param, i).is_ok());
    }
}

#[test]
fn test_path() {
    let s = vec![
        "a",
        "a.b.c.d",
        "a.[0].[1].[2]",
        "a.[abc]",
        "a/v/c.d.s",
        "a.[0]/b/c/../d",
        "a.[bb c]/b/c/../d",
        "a.[0].[#hello]",
        "../a/b.[0].[1]",
        "./this.[0]/[1]/this/../a",
        "./this_name",
        "./goo/[/bar]",
        "a.[你好]",
        "a.[10].[#comment]",
        "a.[]", // empty key
        "././[/foo]",
        "[foo]",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::path, i).is_ok());
    }
}

#[test]
fn test_directive_expression() {
    let s = vec!["{{* ssh}}", "{{~* ssh}}"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::directive_expression, i).is_ok());
    }
}

#[test]
fn test_directive_block() {
    let s = vec![
        "{{#* inline}}something{{/inline}}",
        "{{~#* inline}}hello{{/inline}}",
        "{{#* inline \"partialname\"}}something{{/inline}}",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::directive_block, i).is_ok());
    }
}

#[test]
fn test_partial_expression() {
    let s = vec![
        "{{> hello}}",
        "{{> (hello)}}",
        "{{~> hello a}}",
        "{{> hello a=1}}",
    ];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::partial_expression, i).is_ok());
    }
}

#[test]
fn test_partial_block() {
    let s = vec!["{{#> hello}}nice{{/hello}}"];
    for i in s.iter() {
        assert!(HandlebarsParser::parse_str(Rule::partial_block, i).is_ok());
    }
}
<commit_msg>(test) use macro to test<commit_after>const _GRAMMAR: &'static str = include_str!("grammar.pest");

#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct HandlebarsParser;

#[cfg(test)]
use pest::Parser;

#[cfg(test)]
macro_rules! assert_rule {
    ($rule: expr, $in: expr) => {
        assert_eq!(
            HandlebarsParser::parse_str($rule, $in)
                .unwrap()
                .next()
                .unwrap()
                .into_span()
                .end(),
            $in.len()
        );
    }
}

#[test]
fn test_raw_text() {
    let s = vec![
        "<h1> helloworld </h1>    ",
        "hello\\{{world}}",
        "hello\\{{#if world}}nice\\{{/if}}",
        "hello \\{{{{raw}}}}hello\\{{{{/raw}}}}",
    ];
    for i in s.iter() {
        assert_rule!(Rule::raw_text, i);
    }
}

#[test]
fn test_raw_block_text() {
    let s = "<h1> {{hello}} </h1>";
    assert_rule!(Rule::raw_block_text, s);
}

#[test]
fn test_reference() {
    let s = vec![
        "a",
        "abc",
        "../a",
        "a.b",
        "@abc",
        "a.[abc]",
        "aBc.[abc]",
        "abc.[0].[nice]",
        "some-name",
        "this.[0].ok",
    ];
    for i in s.iter() {
        assert_rule!(Rule::reference, i);
    }
}

#[test]
fn test_name() {
    let s = vec!["if", "(abc)"];
    for i in s.iter() {
        assert_rule!(Rule::name, i);
    }
}

#[test]
fn test_param() {
    let s = vec!["hello", "\"json literal\""];
    for i in s.iter() {
        assert_rule!(Rule::param, i);
    }
}

#[test]
fn test_hash() {
    let s = vec![
        "hello=world",
        "hello=\"world\"",
        "hello=(world)",
        "hello=(world 0)",
    ];
    for i in s.iter() {
        assert_rule!(Rule::hash, i);
    }
}

#[test]
fn test_json_literal() {
    let s = vec![
        "\"json string\"",
        "\"quot: \\\"\"",
        "[]",
        "[\"hello\"]",
        "[1,2,3,4,true]",
        "{\"hello\": \"world\"}",
        "{}",
        "{\"a\":1, \"b\":2 }",
    ];
    for i in s.iter() {
        assert_rule!(Rule::literal, i);
    }
}

#[test]
fn test_comment() {
    let s = vec!["{{! hello }}"];
    for i in s.iter() {
        assert_rule!(Rule::hbs_comment, i);
    }
}

#[test]
fn test_subexpression() {
    let s = vec!["(sub)", "(sub 0)", "(sub a=1)"];
    for i in s.iter() {
        assert_rule!(Rule::subexpression, i);
    }
}

#[test]
fn test_expression() {
    let s = vec!["{{exp}}", "{{(exp)}}", "{{this.name}}", "{{this.[0].name}}"];
    for i in s.iter() {
        assert_rule!(Rule::expression, i);
    }
}

#[test]
fn test_helper_expression() {
    let s = vec![
        "{{exp 1}}",
        "{{exp \"literal\"}}",
        "{{exp ref}}",
        "{{exp (sub)}}",
        "{{exp (sub 123)}}",
        "{{exp []}}",
        "{{exp {}}}",
        "{{exp key=1}}",
        "{{exp key=ref}}",
        "{{exp key=(sub)}}",
        "{{exp key=(sub 0)}}",
    ];
    for i in s.iter() {
        assert_rule!(Rule::helper_expression, i);
    }
}

#[test]
fn test_identifier_with_dash() {
    let s = vec!["{{exp-foo}}"];
    for i in s.iter() {
        assert_rule!(Rule::expression, i);
    }
}

#[test]
fn test_html_expression() {
    let s = vec!["{{{html}}}", "{{{(html)}}}", "{{{(html)}}}"];
    for i in s.iter() {
        assert_rule!(Rule::html_expression, i);
    }
}

#[test]
fn test_helper_start() {
    let s = vec![
        "{{#if hello}}",
        "{{#if (hello)}}",
        "{{#if hello=world}}",
        "{{#if hello hello=world}}",
        "{{#if []}}",
        "{{#if {}}}",
        "{{#if}}",
        "{{~#if hello~}}",
        "{{#each people as |person|}}",
        "{{#each-obj obj as |key val|}}",
    ];
    for i in s.iter() {
        assert_rule!(Rule::helper_block_start, i);
    }
}

#[test]
fn test_helper_end() {
    let s = vec!["{{/if}}", "{{~/if}}", "{{~/if ~}}", "{{/if   ~}}"];
    for i in s.iter() {
        assert_rule!(Rule::helper_block_end, i);
    }
}

#[test]
fn test_helper_block() {
    let s = vec![
        "{{#if hello}}hello{{/if}}",
        "{{#if true}}hello{{/if}}",
        "{{#if nice ok=1}}hello{{/if}}",
        "{{#if}}hello{{else}}world{{/if}}",
        "{{#if}}hello{{^}}world{{/if}}",
        "{{#if}}{{#if}}hello{{/if}}{{/if}}",
        "{{#if}}hello{{~else}}world{{/if}}",
        "{{#if}}hello{{else~}}world{{/if}}",
        "{{#if}}hello{{~^~}}world{{/if}}",
        "{{#if}}{{/if}}",
    ];
    for i in s.iter() {
        assert_rule!(Rule::helper_block, i);
    }
}

#[test]
fn test_raw_block() {
    let s = vec![
        "{{{{if hello}}}}good {{hello}}{{{{/if}}}}",
        "{{{{if hello}}}}{{#if nice}}{{/if}}{{{{/if}}}}",
    ];
    for i in s.iter() {
        assert_rule!(Rule::raw_block, i);
    }
}

#[test]
fn test_block_param() {
    let s = vec!["as |person|", "as |key val|"];
    for i in s.iter() {
        assert_rule!(Rule::block_param, i);
    }
}

#[test]
fn test_path() {
    let s = vec![
        "a",
        "a.b.c.d",
        "a.[0].[1].[2]",
        "a.[abc]",
        "a/v/c.d.s",
        "a.[0]/b/c/../d",
        "a.[bb c]/b/c/../d",
        "a.[0].[#hello]",
        "../a/b.[0].[1]",
        "./this.[0]/[1]/this/../a",
        "./this_name",
        "./goo/[/bar]",
        "a.[你好]",
        "a.[10].[#comment]",
        "a.[]", // empty key
        "././[/foo]",
        "[foo]",
    ];
    for i in s.iter() {
        assert_rule!(Rule::path, i);
    }
}

#[test]
fn test_directive_expression() {
    let s = vec!["{{* ssh}}", "{{~* ssh}}"];
    for i in s.iter() {
        assert_rule!(Rule::directive_expression, i);
    }
}

#[test]
fn test_directive_block() {
    let s = vec![
        "{{#* inline}}something{{/inline}}",
        "{{~#* inline}}hello{{/inline}}",
        "{{#* inline \"partialname\"}}something{{/inline}}",
    ];
    for i in s.iter() {
        assert_rule!(Rule::directive_block, i);
    }
}

#[test]
fn test_partial_expression() {
    let s = vec![
        "{{> hello}}",
        "{{> (hello)}}",
        "{{~> hello a}}",
        "{{> hello a=1}}",
    ];
    for i in s.iter() {
        assert_rule!(Rule::partial_expression, i);
    }
}

#[test]
fn test_partial_block() {
    let s = vec!["{{#> hello}}nice{{/hello}}"];
    for i in s.iter() {
        let h = HandlebarsParser::parse_str(Rule::partial_block, i).unwrap();
        for i in h {
            println!("{:?}", i);
        }
        assert_rule!(Rule::partial_block, i);
    }
}
<|endoftext|>


<commit_before>//! Generic definitions related to the MMU

use core::ops::{Add, AddAssign, Deref};

use hal::arch::mmu::PageEntry;
use hal::arch::mmu::{
    PAGE_SHIFT,
    OFFSET_MASK,
    L1_PAGE_SHIFT,
    L2_PAGE_SHIFT,
    L3_PAGE_SHIFT,
    L4_PAGE_SHIFT
};

pub trait Address {
    type Repr;
}

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[cfg(feature = "xen")]
/// A physical address
pub struct Paddr(u64);

#[cfg(feature = "xen")]
impl Address for Paddr { type Repr = u64; }

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[cfg(feature = "xen")]
/// A machine address
pub struct Maddr(u64);

#[cfg(feature = "xen")]
impl Address for Maddr { type Repr = u64; }

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[cfg(not(feature = "xen"))]
/// A physical address
pub struct Paddr(usize);

#[cfg(not(feature = "xen"))]
impl Address for Paddr { type Repr = usize; }

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[cfg(not(feature = "xen"))]
/// A machine address
pub struct Maddr(usize);

#[cfg(not(feature = "xen"))]
impl Address for Maddr { type Repr = usize; }

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
/// A virtual address
pub struct Vaddr(usize);

impl Address for Vaddr { type Repr = usize; }

#[repr(C)]
#[derive(Clone, Copy)]
/// Physical frame number
pub struct Pfn(usize);

#[repr(C)]
#[derive(Clone, Copy)]
/// Machine frame number
pub struct Mfn(usize);

impl Vaddr {
    /// Create a new virtual address
    pub fn new(value: <Vaddr as Address>::Repr) -> Self {
        Vaddr(value)
    }

    /// Create a new virtual address from a constant pointer
    pub fn from_ptr<T>(ptr: *const T) -> Self {
        Vaddr(ptr as usize)
    }

    /// Create a new virtual address from a mutable pointer
    pub fn from_mut_ptr<T>(ptr: *mut T) -> Self {
        Vaddr(ptr as usize)
    }

    /// Get the offset in the 4th layer of MMU configuration tables
    ///
    /// Note: This value will be garbage if the table does not exist
    pub fn l4_offset(&self) -> usize {
        ((self.0 >> L4_PAGE_SHIFT) & OFFSET_MASK) as usize
    }

    /// Get the offset in the 3rd layer of MMU configuration tables
    ///
    /// Note: This value will be garbage if the table does not exist
    pub fn l3_offset(&self) -> usize {
        ((self.0 >> L3_PAGE_SHIFT) & OFFSET_MASK) as usize
    }

    /// Get the offset in the 2nd layer of MMU configuration tables
    pub fn l2_offset(&self) -> usize {
        ((self.0 >> L2_PAGE_SHIFT) & OFFSET_MASK) as usize
    }

    /// Get the offset in the 1st layer of MMU configuration tables
    pub fn l1_offset(&self) -> usize {
        ((self.0 >> L1_PAGE_SHIFT) & OFFSET_MASK) as usize
    }

    /// Get the internal value as a constant rust pointer
    ///
    /// This is unsafe as the internal value might not be a valid pointer
    pub unsafe fn as_ptr<T>(&self) -> *const T {
        self.0 as *const u8 as *const T
    }

    /// Get the internal value as a mutable rust pointer
    ///
    /// This is unsafe as the internal value might not be a valid pointer
    pub unsafe fn as_mut_ptr<T>(&mut self) -> *mut T {
        self.0 as *mut u8 as *mut T
    }

    /// Increment the virtual address
    ///
    /// `count` is the number of bytes you want to increment the pointed value
    pub unsafe fn incr(mut self, count: usize) -> Self {
        self.0 += count as <Self as Address>::Repr;
        self
    }
}

impl Paddr {
    /// Create a new physical address
    pub fn new(value: <Paddr as Address>::Repr) -> Self {
        Paddr(value)
    }

    /// Increment the virtual address
    ///
    /// `count` is the number of bytes you want to increment the pointed value
    pub unsafe fn incr(mut self, count: usize) -> Self {
        self.0 += count as <Self as Address>::Repr;
        self
    }
}

impl Maddr {
    /// Create a new machine address
    pub fn new(value: <Maddr as Address>::Repr) -> Self {
        Maddr(value)
    }

    /// Increment the virtual address
    ///
    /// `count` is the number of bytes you want to increment the pointed value
    pub unsafe fn incr(mut self, count: usize) -> Self {
        self.0 += count as <Self as Address>::Repr;
        self
    }
}

impl Pfn {
    /// Create a new physical frame number
    pub fn new(value: usize) -> Self {
        Pfn(value)
    }
}

impl Mfn {
    /// Create a new machine frame number
    pub fn new(value: usize) -> Self {
        Mfn(value)
    }
}

impl Add<usize> for Pfn {
    type Output = Pfn;

    fn add(mut self, rhs: usize) -> Self::Output {
        self.0 += rhs;
        self
    }
}
impl AddAssign<usize> for Pfn {
    fn add_assign(&mut self, rhs: usize) {
        self.0 += rhs;
    }
}

impl Add<usize> for Mfn {
    type Output = Mfn;

    fn add(mut self, rhs: usize) -> Self::Output {
        self.0 += rhs;
        self
    }
}
impl AddAssign<usize> for Mfn {
    fn add_assign(&mut self, rhs: usize) {
        self.0 += rhs;
    }
}

impl Deref for Vaddr {
    type Target = <Self as Address>::Repr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Deref for Paddr {
    type Target = <Self as Address>::Repr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Deref for Maddr {
    type Target = <Self as Address>::Repr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Deref for Pfn {
    type Target = usize;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<Pfn> for Vaddr {
    fn from(pfn: Pfn) -> Vaddr {
        Vaddr(pfn.0 << PAGE_SHIFT)
    }
}

impl From<Vaddr> for Pfn {
    fn from(vaddr: Vaddr) -> Pfn {
        Pfn(vaddr.0 >> PAGE_SHIFT)
    }
}

impl From<Mfn> for Vaddr {
    fn from(mfn: Mfn) -> Vaddr {
        Vaddr::from(Pfn::from(mfn))
    }
}

impl From<Mfn> for Maddr {
    fn from(mfn: Mfn) -> Maddr {
        Maddr((mfn.0 as <Self as Address>::Repr) << PAGE_SHIFT)
    }
}

impl From<Vaddr> for Mfn {
    fn from(vaddr: Vaddr) -> Mfn {
        Mfn::from(Pfn::from(vaddr))
    }
}

impl From<PageEntry> for Vaddr {
    fn from(entry: PageEntry) -> Vaddr {
        Vaddr::from(Mfn::from(entry))
    }
}

#[cfg(feature = "xen")]
impl From<PageEntry> for Mfn {
    fn from(entry: PageEntry) -> Mfn {
        use hal::arch::mmu::PTE_MASK;

        let page_entry_value = entry.mask(PTE_MASK).value() >> PAGE_SHIFT;

        Mfn(PageEntry::new(page_entry_value).value() as usize)
    }
}

#[cfg(feature = "xen")]
impl From<Maddr> for PageEntry {
    fn from(maddr: Maddr) -> PageEntry {
        use hal::arch::mmu::PTE_FLAGS_MASK;
        use hal::arch::mmu::PageFlags;

        let value = (maddr.0 as u64) & !PTE_FLAGS_MASK;

        PageEntry::new(value).set(PageFlags::Present).set(PageFlags::Writable)
    }
}

#[cfg(feature = "xen")]
impl From<Mfn> for PageEntry {
    fn from(mfn: Mfn) -> PageEntry {
        PageEntry::from(Maddr::from(mfn))
    }
}

#[cfg(feature = "xen")]
impl From<Pfn> for PageEntry {
    fn from(pfn: Pfn) -> PageEntry {
        PageEntry::from(Mfn::from(pfn))
    }
}

#[cfg(feature = "xen")]
impl From<Mfn> for Pfn {
    fn from(mfn: Mfn) -> Pfn {
        use hal::xen::defs::MACH2PHYS_VIRT_START;

        let mtp_mapping: *const Pfn = MACH2PHYS_VIRT_START as *const Pfn;

        unsafe {
            *mtp_mapping.offset(mfn.0 as isize)
        }
    }
}

#[cfg(feature = "xen")]
impl From<Pfn> for Mfn {
    fn from(pfn: Pfn) -> Mfn {
        // XXX: Won't work for ARM
        use hal::xen::boot::start_info;

        unsafe {
            let ptm_mapping: *const Mfn = (*start_info).mfn_list as *const Mfn;

            *ptm_mapping.offset(pfn.0 as isize)
        }
    }
}
<commit_msg>hal: Add impl Deref for Mfn<commit_after>//! Generic definitions related to the MMU

use core::ops::{Add, AddAssign, Deref};

use hal::arch::mmu::PageEntry;
use hal::arch::mmu::{
    PAGE_SHIFT,
    OFFSET_MASK,
    L1_PAGE_SHIFT,
    L2_PAGE_SHIFT,
    L3_PAGE_SHIFT,
    L4_PAGE_SHIFT
};

pub trait Address {
    type Repr;
}

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[cfg(feature = "xen")]
/// A physical address
pub struct Paddr(u64);

#[cfg(feature = "xen")]
impl Address for Paddr { type Repr = u64; }

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[cfg(feature = "xen")]
/// A machine address
pub struct Maddr(u64);

#[cfg(feature = "xen")]
impl Address for Maddr { type Repr = u64; }

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[cfg(not(feature = "xen"))]
/// A physical address
pub struct Paddr(usize);

#[cfg(not(feature = "xen"))]
impl Address for Paddr { type Repr = usize; }

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[cfg(not(feature = "xen"))]
/// A machine address
pub struct Maddr(usize);

#[cfg(not(feature = "xen"))]
impl Address for Maddr { type Repr = usize; }

#[repr(C)]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
/// A virtual address
pub struct Vaddr(usize);

impl Address for Vaddr { type Repr = usize; }

#[repr(C)]
#[derive(Clone, Copy)]
/// Physical frame number
pub struct Pfn(usize);

#[repr(C)]
#[derive(Clone, Copy)]
/// Machine frame number
pub struct Mfn(usize);

impl Vaddr {
    /// Create a new virtual address
    pub fn new(value: <Vaddr as Address>::Repr) -> Self {
        Vaddr(value)
    }

    /// Create a new virtual address from a constant pointer
    pub fn from_ptr<T>(ptr: *const T) -> Self {
        Vaddr(ptr as usize)
    }

    /// Create a new virtual address from a mutable pointer
    pub fn from_mut_ptr<T>(ptr: *mut T) -> Self {
        Vaddr(ptr as usize)
    }

    /// Get the offset in the 4th layer of MMU configuration tables
    ///
    /// Note: This value will be garbage if the table does not exist
    pub fn l4_offset(&self) -> usize {
        ((self.0 >> L4_PAGE_SHIFT) & OFFSET_MASK) as usize
    }

    /// Get the offset in the 3rd layer of MMU configuration tables
    ///
    /// Note: This value will be garbage if the table does not exist
    pub fn l3_offset(&self) -> usize {
        ((self.0 >> L3_PAGE_SHIFT) & OFFSET_MASK) as usize
    }

    /// Get the offset in the 2nd layer of MMU configuration tables
    pub fn l2_offset(&self) -> usize {
        ((self.0 >> L2_PAGE_SHIFT) & OFFSET_MASK) as usize
    }

    /// Get the offset in the 1st layer of MMU configuration tables
    pub fn l1_offset(&self) -> usize {
        ((self.0 >> L1_PAGE_SHIFT) & OFFSET_MASK) as usize
    }

    /// Get the internal value as a constant rust pointer
    ///
    /// This is unsafe as the internal value might not be a valid pointer
    pub unsafe fn as_ptr<T>(&self) -> *const T {
        self.0 as *const u8 as *const T
    }

    /// Get the internal value as a mutable rust pointer
    ///
    /// This is unsafe as the internal value might not be a valid pointer
    pub unsafe fn as_mut_ptr<T>(&mut self) -> *mut T {
        self.0 as *mut u8 as *mut T
    }

    /// Increment the virtual address
    ///
    /// `count` is the number of bytes you want to increment the pointed value
    pub unsafe fn incr(mut self, count: usize) -> Self {
        self.0 += count as <Self as Address>::Repr;
        self
    }
}

impl Paddr {
    /// Create a new physical address
    pub fn new(value: <Paddr as Address>::Repr) -> Self {
        Paddr(value)
    }

    /// Increment the virtual address
    ///
    /// `count` is the number of bytes you want to increment the pointed value
    pub unsafe fn incr(mut self, count: usize) -> Self {
        self.0 += count as <Self as Address>::Repr;
        self
    }
}

impl Maddr {
    /// Create a new machine address
    pub fn new(value: <Maddr as Address>::Repr) -> Self {
        Maddr(value)
    }

    /// Increment the virtual address
    ///
    /// `count` is the number of bytes you want to increment the pointed value
    pub unsafe fn incr(mut self, count: usize) -> Self {
        self.0 += count as <Self as Address>::Repr;
        self
    }
}

impl Pfn {
    /// Create a new physical frame number
    pub fn new(value: usize) -> Self {
        Pfn(value)
    }
}

impl Mfn {
    /// Create a new machine frame number
    pub fn new(value: usize) -> Self {
        Mfn(value)
    }
}

impl Add<usize> for Pfn {
    type Output = Pfn;

    fn add(mut self, rhs: usize) -> Self::Output {
        self.0 += rhs;
        self
    }
}
impl AddAssign<usize> for Pfn {
    fn add_assign(&mut self, rhs: usize) {
        self.0 += rhs;
    }
}

impl Add<usize> for Mfn {
    type Output = Mfn;

    fn add(mut self, rhs: usize) -> Self::Output {
        self.0 += rhs;
        self
    }
}
impl AddAssign<usize> for Mfn {
    fn add_assign(&mut self, rhs: usize) {
        self.0 += rhs;
    }
}

impl Deref for Vaddr {
    type Target = <Self as Address>::Repr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Deref for Paddr {
    type Target = <Self as Address>::Repr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Deref for Maddr {
    type Target = <Self as Address>::Repr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Deref for Pfn {
    type Target = usize;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Deref for Mfn {
    type Target = usize;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<Pfn> for Vaddr {
    fn from(pfn: Pfn) -> Vaddr {
        Vaddr(pfn.0 << PAGE_SHIFT)
    }
}

impl From<Vaddr> for Pfn {
    fn from(vaddr: Vaddr) -> Pfn {
        Pfn(vaddr.0 >> PAGE_SHIFT)
    }
}

impl From<Mfn> for Vaddr {
    fn from(mfn: Mfn) -> Vaddr {
        Vaddr::from(Pfn::from(mfn))
    }
}

impl From<Mfn> for Maddr {
    fn from(mfn: Mfn) -> Maddr {
        Maddr((mfn.0 as <Self as Address>::Repr) << PAGE_SHIFT)
    }
}

impl From<Vaddr> for Mfn {
    fn from(vaddr: Vaddr) -> Mfn {
        Mfn::from(Pfn::from(vaddr))
    }
}

impl From<PageEntry> for Vaddr {
    fn from(entry: PageEntry) -> Vaddr {
        Vaddr::from(Mfn::from(entry))
    }
}

#[cfg(feature = "xen")]
impl From<PageEntry> for Mfn {
    fn from(entry: PageEntry) -> Mfn {
        use hal::arch::mmu::PTE_MASK;

        let page_entry_value = entry.mask(PTE_MASK).value() >> PAGE_SHIFT;

        Mfn(PageEntry::new(page_entry_value).value() as usize)
    }
}

#[cfg(feature = "xen")]
impl From<Maddr> for PageEntry {
    fn from(maddr: Maddr) -> PageEntry {
        use hal::arch::mmu::PTE_FLAGS_MASK;
        use hal::arch::mmu::PageFlags;

        let value = (maddr.0 as u64) & !PTE_FLAGS_MASK;

        PageEntry::new(value).set(PageFlags::Present).set(PageFlags::Writable)
    }
}

#[cfg(feature = "xen")]
impl From<Mfn> for PageEntry {
    fn from(mfn: Mfn) -> PageEntry {
        PageEntry::from(Maddr::from(mfn))
    }
}

#[cfg(feature = "xen")]
impl From<Pfn> for PageEntry {
    fn from(pfn: Pfn) -> PageEntry {
        PageEntry::from(Mfn::from(pfn))
    }
}

#[cfg(feature = "xen")]
impl From<Mfn> for Pfn {
    fn from(mfn: Mfn) -> Pfn {
        use hal::xen::defs::MACH2PHYS_VIRT_START;

        let mtp_mapping: *const Pfn = MACH2PHYS_VIRT_START as *const Pfn;

        unsafe {
            *mtp_mapping.offset(mfn.0 as isize)
        }
    }
}

#[cfg(feature = "xen")]
impl From<Pfn> for Mfn {
    fn from(pfn: Pfn) -> Mfn {
        // XXX: Won't work for ARM
        use hal::xen::boot::start_info;

        unsafe {
            let ptm_mapping: *const Mfn = (*start_info).mfn_list as *const Mfn;

            *ptm_mapping.offset(pfn.0 as isize)
        }
    }
}
<|endoftext|>


<commit_before>use super::errors::*;

use fuse;
use fuse::{Filesystem, Request, ReplyAttr, ReplyDirectory, ReplyEntry, FileAttr, ReplyOpen,
           ReplyData, ReplyEmpty, ReplyCreate, ReplyWrite};

use libc::{ENOSYS, ENOENT};

use time::Timespec;
use std;
use std::ffi::OsStr;
use std::fs;
use std::time::SystemTime;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::collections::HashMap;

pub struct S3HierarchicalFilesystem<'a> {
    _mount_path: &'a str,
    _backing_path: &'a str,
    ino_paths: HashMap<u64, PathBuf>,
    files: HashMap<u64, File>,
}

impl<'a> S3HierarchicalFilesystem<'a> {
    pub fn mount(mp: &str, bp: &str) -> Result<()> {
        let mut fs = S3HierarchicalFilesystem {
            _mount_path: mp,
            _backing_path: bp,
            ino_paths: HashMap::new(),
            files: HashMap::new(),
        };
        fs.ino_paths.insert(1, PathBuf::from(bp));
        fuse::mount(fs, &mp, &[]).chain_err(|| "mounting filesystem")
    }
}

fn filetype_tryfrom(ft: &fs::FileType) -> Result<fuse::FileType> {
    if ft.is_file() {
        return Ok(fuse::FileType::RegularFile);
    }
    if ft.is_dir() {
        return Ok(fuse::FileType::Directory);
    }
    if ft.is_symlink() {
        return Ok(fuse::FileType::Symlink);
    }
    bail!("unknown filetype")
}

fn timespec_from(st: &SystemTime) -> Timespec {
    if let Ok(dur_since_epoch) = st.duration_since(std::time::UNIX_EPOCH) {
        Timespec::new(dur_since_epoch.as_secs() as i64,
                      dur_since_epoch.subsec_nanos() as i32)
    } else {
        Timespec::new(0, 0)
    }
}

fn fileattr_from(m: &std::fs::Metadata) -> FileAttr {
    use std::os::unix::fs::{MetadataExt, PermissionsExt};
    use std::time::SystemTime;

    let kind = filetype_tryfrom(&m.file_type()).unwrap();
    let mode = m.permissions().mode();
    FileAttr {
        ino: m.ino(),
        size: m.len(),
        blocks: m.blocks(),
        atime: timespec_from(&m.accessed().unwrap_or(SystemTime::now())),
        mtime: timespec_from(&m.modified().unwrap_or(SystemTime::now())),
        ctime: Timespec::new(m.ctime(), m.ctime_nsec() as i32),
        crtime: timespec_from(&m.created().unwrap_or(SystemTime::now())),
        kind: kind,
        perm: mode as u16,
        nlink: m.nlink() as u32,
        uid: m.uid(),
        gid: m.gid(),
        rdev: m.rdev() as u32,
        flags: 0,
    }
}

fn dir_from(entry_opt: std::io::Result<fs::DirEntry>) -> Option<(u64, fuse::FileType, String)> {
    use std::os::unix::fs::DirEntryExt;

    let mut result = None;
    if let Ok(entry) = entry_opt {
        if let Ok(fsfiletype) = entry.file_type() {
            if let Ok(filetype) = filetype_tryfrom(&fsfiletype) {
                if let Ok(filename) = entry.file_name().into_string() {
                    result = Some((entry.ino(), filetype, filename));
                }
            }
        }
    }
    result
}

macro_rules! ino_path_or_return {
    ($self:ident, $parent:expr, $reply:ident) => ({
        let path = match $self.ino_paths.get($parent) {
            Some(p) => p,
            None => {
                error!("parent not found in cache");
                $reply.error(ENOSYS);
                return;
            }
        };
        path.clone()
    })
}

macro_rules! full_path_or_return {
    ($self:ident, $parent:expr, $name:expr, $reply:ident) => ({
        let parent_path = match $self.ino_paths.get($parent) {
            Some(p) => p,
            None => {
                error!("parent not found in cache");
                $reply.error(ENOSYS);
                return;
            }
        };
        let new_name = match $name.to_str() {
            Some(p) => p,
            None => {
                error!("unable to read name");
                $reply.error(ENOSYS);
                return;
            }
        };
        Path::new(&parent_path).join(new_name).clone()
    })
}

macro_rules! file_handle_or_return {
    ($self:ident, $fh:expr, $reply:ident) => (
        match $self.files.get($fh) {
            Some(f) => f,
            None => {
                error!("File handle not found: {}", $fh);
                $reply.error(ENOENT);
                return;
            }
        }
    )
}

macro_rules! ok_or_return_error {
    ($v:expr, $err:expr, $reply:ident) => (
        match $v {
            Ok(v) => v,
            Err(e) => {
                error!("{:?}", e);
                $reply.error($err);
                return;
            }
        }
    )
}

impl<'a> Filesystem for S3HierarchicalFilesystem<'a> {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        trace!("getattr(ino={})", ino);

        let path = ino_path_or_return!(self, &ino, reply);
        let metadata = ok_or_return_error!(std::fs::metadata(path), ENOENT, reply);

        debug!("{:?}", metadata);
        let attr = fileattr_from(&metadata);
        let ttl = Timespec::new(1, 0);
        reply.attr(&ttl, &attr);
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) {
        trace!("lookup(parent={}, name={:?})", parent, name);

        let path = full_path_or_return!(self, &parent, name, reply);

        match path.metadata() {
            Ok(metadata) => {
                debug!("{:?}", metadata);
                let attr = fileattr_from(&metadata);
                let ttl = Timespec::new(1, 0);
                debug!("warning: generation assumed 0");
                reply.entry(&ttl, &attr, 0);
                debug!("{:?}", attr);
            }
            Err(e) => {
                info!("std::fs::metadata: {}", e);
                reply.error(ENOENT);
            }
        };
    }

    fn readdir(&mut self,
               _req: &Request,
               ino: u64,
               fh: u64,
               offset: u64,
               mut reply: ReplyDirectory) {

        trace!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);

        let path: PathBuf = ino_path_or_return!(self, &ino, reply);

        if offset < 1 {
            reply.add(ino, 0, fuse::FileType::Directory, ".");
            reply.add(ino, 1, fuse::FileType::Directory, "..");
        };

        let rd = ok_or_return_error!(path.read_dir(), ENOENT, reply);

        for (i, entry_opt) in rd.enumerate() {
            let entry_offset = (i + 2) as u64;
            if offset >= entry_offset {
                continue;
            };
            let (ino, filetype, filename) = match dir_from(entry_opt) {
                Some(x) => x,
                None => {
                    error!("unable to extract directory entry");
                    reply.error(ENOENT);
                    return;
                }
            };
            debug!("Adding: {}, {}, {:?}, \"{}\"",
                   ino,
                   entry_offset,
                   filetype,
                   filename);
            if reply.add(ino, entry_offset, filetype, &filename) {
                break;
            }
            self.ino_paths.insert(ino, path.join(filename));
        }
        reply.ok();
    }

    fn open(&mut self, _req: &Request, ino: u64, _flags: u32, reply: ReplyOpen) {
        trace!("open(ino={}, flags={})", ino, _flags);

        let path = ino_path_or_return!(self, &ino, reply);

        match File::open(path) {
            Ok(f) => {
                // FIXME: race condition
                let fh: u64 = *self.files.keys().max().unwrap_or(&10u64) + 1;
                self.files.insert(fh, f);
                trace!("opened file handle: {}", fh);
                reply.opened(fh, 0);
            }
            Err(e) => {
                error!("File::open: {:?}", e);
                reply.error(e.raw_os_error().unwrap_or(ENOENT));
            }
        };
    }

    fn read(&mut self,
            _req: &Request,
            _ino: u64,
            fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {

        use std::io::Read;

        trace!("read(ino={}, fh={}, offset={}, size={})",
               _ino,
               fh,
               offset,
               size);

        let mut f = file_handle_or_return!(self, &fh, reply);
        debug!("File: {:?}", f);

        let mut buffer = Vec::with_capacity(size as usize);
        buffer.resize(size as usize, 0u8);

        match f.read_exact(&mut buffer[..]) {
            Ok(_) => {
                debug!("Read {} bytes", size);
                reply.data(&buffer);
            }
            Err(e) => reply.error(e.raw_os_error().unwrap_or(ENOENT)),
        }
    }

    fn release(&mut self,
               _req: &Request,
               _ino: u64,
               fh: u64,
               _flags: u32,
               _lock_owner: u64,
               _flush: bool,
               reply: ReplyEmpty) {
        trace!("release(ino={}, fh={}, flags={}, lock_owner={}, flush={})",
               _ino,
               fh,
               _flags,
               _lock_owner,
               _flush);

        match self.files.remove(&fh) {
            Some(_) => {
                debug!("closed file handle: {}", fh);
                reply.ok();
            }
            None => {
                error!("File handle not found: {}", fh);
                reply.error(ENOENT);
            }
        };
    }

    fn create(&mut self,
              _req: &Request,
              parent: u64,
              name: &OsStr,
              _mode: u32,
              _flags: u32,
              reply: ReplyCreate) {
        trace!("create(parent={}, name={:?}, mode={}, flags={})",
               parent,
               name,
               _mode,
               _flags);

        let path = full_path_or_return!(self, &parent, name, reply);

        match File::create(&path) {
            Ok(f) => {
                trace!("File created: {:?}", f);
                let fh: u64 = *self.files.keys().max().unwrap_or(&10u64) + 1;
                debug!("Handle: {}", fh);
                self.files.insert(fh, f);
                match path.metadata() {
                    Ok(metadata) => {
                        let attr = fileattr_from(&metadata);
                        self.ino_paths.insert(attr.ino, path);
                        let ttl = Timespec::new(1, 0);
                        reply.created(&ttl, &attr, 0, fh, 0);
                    }
                    Err(e) => {
                        error!("std::fs::metadata: {}", e);
                        reply.error(e.raw_os_error().unwrap_or(ENOENT));
                    }
                }
            }
            Err(e) => {
                error!("File::create: {:?}", e);
                reply.error(e.raw_os_error().unwrap_or(ENOENT));
            }
        }
    }

    fn write(&mut self,
             _req: &Request,
             _ino: u64,
             fh: u64,
             offset: u64,
             data: &[u8],
             _flags: u32,
             reply: ReplyWrite) {

        use std::io::{Seek, SeekFrom, Write};

        trace!("write(ino={}, fh={}, offset={}, data={:?}, flags={})",
               _ino,
               fh,
               offset,
               data,
               _flags);

        let mut f = file_handle_or_return!(self, &fh, reply);
        debug!("File: {:?}", f);

        if let Err(e) = f.seek(SeekFrom::Start(offset)) {
            error!("Unable to seek to position: {}", offset);
            reply.error(e.raw_os_error().unwrap_or(ENOENT));
            return;
        }

        match f.write(&data) {
            Ok(size) => {
                debug!("Written {} bytes", size);
                reply.written(size as u32);
            }
            Err(e) => reply.error(e.raw_os_error().unwrap_or(ENOENT)),
        }
    }

    fn mkdir(&mut self, _req: &Request, parent: u64, name: &OsStr, _mode: u32, reply: ReplyEntry) {
        trace!("mkdir(parent={}, name={:?}, mode={})", parent, name, _mode);

        let path = full_path_or_return!(self, &parent, name, reply);

        match fs::create_dir(&path) {
            Ok(()) => {
                trace!("Subdir created");
                match path.metadata() {
                    Ok(metadata) => {
                        let attr = fileattr_from(&metadata);
                        self.ino_paths.insert(attr.ino, path);
                        let ttl = Timespec::new(1, 0);
                        reply.entry(&ttl, &attr, 0);
                    }
                    Err(e) => {
                        error!("std::fs::metadata: {}", e);
                        reply.error(e.raw_os_error().unwrap_or(ENOENT));
                    }
                }
            }
            Err(e) => reply.error(e.raw_os_error().unwrap_or(ENOENT)),
        }
    }
}
<commit_msg>setattr for mode<commit_after>use super::errors::*;

use fuse;
use fuse::{Filesystem, Request, ReplyAttr, ReplyDirectory, ReplyEntry, FileAttr, ReplyOpen,
           ReplyData, ReplyEmpty, ReplyCreate, ReplyWrite};

use libc::{ENOSYS, ENOENT};

use time::Timespec;
use std;
use std::ffi::OsStr;
use std::fs;
use std::time::SystemTime;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::collections::HashMap;

pub struct S3HierarchicalFilesystem<'a> {
    _mount_path: &'a str,
    _backing_path: &'a str,
    ino_paths: HashMap<u64, PathBuf>,
    files: HashMap<u64, File>,
}

impl<'a> S3HierarchicalFilesystem<'a> {
    pub fn mount(mp: &str, bp: &str) -> Result<()> {
        let mut fs = S3HierarchicalFilesystem {
            _mount_path: mp,
            _backing_path: bp,
            ino_paths: HashMap::new(),
            files: HashMap::new(),
        };
        fs.ino_paths.insert(1, PathBuf::from(bp));
        fuse::mount(fs, &mp, &[]).chain_err(|| "mounting filesystem")
    }
}

fn filetype_tryfrom(ft: &fs::FileType) -> Result<fuse::FileType> {
    if ft.is_file() {
        return Ok(fuse::FileType::RegularFile);
    }
    if ft.is_dir() {
        return Ok(fuse::FileType::Directory);
    }
    if ft.is_symlink() {
        return Ok(fuse::FileType::Symlink);
    }
    bail!("unknown filetype")
}

fn timespec_from(st: &SystemTime) -> Timespec {
    if let Ok(dur_since_epoch) = st.duration_since(std::time::UNIX_EPOCH) {
        Timespec::new(dur_since_epoch.as_secs() as i64,
                      dur_since_epoch.subsec_nanos() as i32)
    } else {
        Timespec::new(0, 0)
    }
}

fn fileattr_from(m: &std::fs::Metadata) -> FileAttr {
    use std::os::unix::fs::{MetadataExt, PermissionsExt};
    use std::time::SystemTime;

    let kind = filetype_tryfrom(&m.file_type()).unwrap();
    let mode = m.permissions().mode();
    FileAttr {
        ino: m.ino(),
        size: m.len(),
        blocks: m.blocks(),
        atime: timespec_from(&m.accessed().unwrap_or(SystemTime::now())),
        mtime: timespec_from(&m.modified().unwrap_or(SystemTime::now())),
        ctime: Timespec::new(m.ctime(), m.ctime_nsec() as i32),
        crtime: timespec_from(&m.created().unwrap_or(SystemTime::now())),
        kind: kind,
        perm: mode as u16,
        nlink: m.nlink() as u32,
        uid: m.uid(),
        gid: m.gid(),
        rdev: m.rdev() as u32,
        flags: 0,
    }
}

fn dir_from(entry_opt: std::io::Result<fs::DirEntry>) -> Option<(u64, fuse::FileType, String)> {
    use std::os::unix::fs::DirEntryExt;

    let mut result = None;
    if let Ok(entry) = entry_opt {
        if let Ok(fsfiletype) = entry.file_type() {
            if let Ok(filetype) = filetype_tryfrom(&fsfiletype) {
                if let Ok(filename) = entry.file_name().into_string() {
                    result = Some((entry.ino(), filetype, filename));
                }
            }
        }
    }
    result
}

macro_rules! ino_path_or_return {
    ($self:ident, $parent:expr, $reply:ident) => ({
        let path = match $self.ino_paths.get($parent) {
            Some(p) => p,
            None => {
                error!("parent not found in cache");
                $reply.error(ENOSYS);
                return;
            }
        };
        path.clone()
    })
}

macro_rules! full_path_or_return {
    ($self:ident, $parent:expr, $name:expr, $reply:ident) => ({
        let parent_path = match $self.ino_paths.get($parent) {
            Some(p) => p,
            None => {
                error!("parent not found in cache");
                $reply.error(ENOSYS);
                return;
            }
        };
        let new_name = match $name.to_str() {
            Some(p) => p,
            None => {
                error!("unable to read name");
                $reply.error(ENOSYS);
                return;
            }
        };
        Path::new(&parent_path).join(new_name).clone()
    })
}

macro_rules! file_handle_or_return {
    ($self:ident, $fh:expr, $reply:ident) => (
        match $self.files.get($fh) {
            Some(f) => f,
            None => {
                error!("File handle not found: {}", $fh);
                $reply.error(ENOENT);
                return;
            }
        }
    )
}

macro_rules! ok_or_return_error {
    ($v:expr, $err:expr, $reply:ident) => (
        match $v {
            Ok(v) => v,
            Err(e) => {
                error!("{:?}", e);
                $reply.error($err);
                return;
            }
        }
    )
}

macro_rules! none_or_return_error {
    ($v:expr, $reply:ident) => (
        if $v.is_some() {
            error!("setattr change not implemented: {:?}", $v);
            $reply.error(ENOSYS);
            return;
        }
    )
}

impl<'a> Filesystem for S3HierarchicalFilesystem<'a> {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        trace!("getattr(ino={})", ino);

        let path = ino_path_or_return!(self, &ino, reply);
        let metadata = ok_or_return_error!(std::fs::metadata(path), ENOENT, reply);

        debug!("{:?}", metadata);
        let attr = fileattr_from(&metadata);
        let ttl = Timespec::new(1, 0);
        reply.attr(&ttl, &attr);
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) {
        trace!("lookup(parent={}, name={:?})", parent, name);

        let path = full_path_or_return!(self, &parent, name, reply);

        match path.metadata() {
            Ok(metadata) => {
                debug!("{:?}", metadata);
                let attr = fileattr_from(&metadata);
                let ttl = Timespec::new(1, 0);
                debug!("warning: generation assumed 0");
                reply.entry(&ttl, &attr, 0);
                debug!("{:?}", attr);
            }
            Err(e) => {
                info!("std::fs::metadata: {}", e);
                reply.error(ENOENT);
            }
        };
    }

    fn readdir(&mut self,
               _req: &Request,
               ino: u64,
               fh: u64,
               offset: u64,
               mut reply: ReplyDirectory) {

        trace!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);

        let path: PathBuf = ino_path_or_return!(self, &ino, reply);

        if offset < 1 {
            reply.add(ino, 0, fuse::FileType::Directory, ".");
            reply.add(ino, 1, fuse::FileType::Directory, "..");
        };

        let rd = ok_or_return_error!(path.read_dir(), ENOENT, reply);

        for (i, entry_opt) in rd.enumerate() {
            let entry_offset = (i + 2) as u64;
            if offset >= entry_offset {
                continue;
            };
            let (ino, filetype, filename) = match dir_from(entry_opt) {
                Some(x) => x,
                None => {
                    error!("unable to extract directory entry");
                    reply.error(ENOENT);
                    return;
                }
            };
            debug!("Adding: {}, {}, {:?}, \"{}\"",
                   ino,
                   entry_offset,
                   filetype,
                   filename);
            if reply.add(ino, entry_offset, filetype, &filename) {
                break;
            }
            self.ino_paths.insert(ino, path.join(filename));
        }
        reply.ok();
    }

    fn open(&mut self, _req: &Request, ino: u64, _flags: u32, reply: ReplyOpen) {
        trace!("open(ino={}, flags={})", ino, _flags);

        let path = ino_path_or_return!(self, &ino, reply);

        match File::open(path) {
            Ok(f) => {
                // FIXME: race condition
                let fh: u64 = *self.files.keys().max().unwrap_or(&10u64) + 1;
                self.files.insert(fh, f);
                trace!("opened file handle: {}", fh);
                reply.opened(fh, 0);
            }
            Err(e) => {
                error!("File::open: {:?}", e);
                reply.error(e.raw_os_error().unwrap_or(ENOENT));
            }
        };
    }

    fn read(&mut self,
            _req: &Request,
            _ino: u64,
            fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {

        use std::io::Read;

        trace!("read(ino={}, fh={}, offset={}, size={})",
               _ino,
               fh,
               offset,
               size);

        let mut f = file_handle_or_return!(self, &fh, reply);
        debug!("File: {:?}", f);

        let mut buffer = Vec::with_capacity(size as usize);
        buffer.resize(size as usize, 0u8);

        match f.read_exact(&mut buffer[..]) {
            Ok(_) => {
                debug!("Read {} bytes", size);
                reply.data(&buffer);
            }
            Err(e) => reply.error(e.raw_os_error().unwrap_or(ENOENT)),
        }
    }

    fn release(&mut self,
               _req: &Request,
               _ino: u64,
               fh: u64,
               _flags: u32,
               _lock_owner: u64,
               _flush: bool,
               reply: ReplyEmpty) {
        trace!("release(ino={}, fh={}, flags={}, lock_owner={}, flush={})",
               _ino,
               fh,
               _flags,
               _lock_owner,
               _flush);

        match self.files.remove(&fh) {
            Some(_) => {
                debug!("closed file handle: {}", fh);
                reply.ok();
            }
            None => {
                error!("File handle not found: {}", fh);
                reply.error(ENOENT);
            }
        };
    }

    fn create(&mut self,
              _req: &Request,
              parent: u64,
              name: &OsStr,
              _mode: u32,
              _flags: u32,
              reply: ReplyCreate) {
        trace!("create(parent={}, name={:?}, mode={}, flags={})",
               parent,
               name,
               _mode,
               _flags);

        let path = full_path_or_return!(self, &parent, name, reply);

        match File::create(&path) {
            Ok(f) => {
                trace!("File created: {:?}", f);
                let fh: u64 = *self.files.keys().max().unwrap_or(&10u64) + 1;
                debug!("Handle: {}", fh);
                self.files.insert(fh, f);
                match path.metadata() {
                    Ok(metadata) => {
                        let attr = fileattr_from(&metadata);
                        self.ino_paths.insert(attr.ino, path);
                        let ttl = Timespec::new(1, 0);
                        reply.created(&ttl, &attr, 0, fh, 0);
                    }
                    Err(e) => {
                        error!("std::fs::metadata: {}", e);
                        reply.error(e.raw_os_error().unwrap_or(ENOENT));
                    }
                }
            }
            Err(e) => {
                error!("File::create: {:?}", e);
                reply.error(e.raw_os_error().unwrap_or(ENOENT));
            }
        }
    }

    fn write(&mut self,
             _req: &Request,
             _ino: u64,
             fh: u64,
             offset: u64,
             data: &[u8],
             _flags: u32,
             reply: ReplyWrite) {

        use std::io::{Seek, SeekFrom, Write};

        trace!("write(ino={}, fh={}, offset={}, data={:?}, flags={})",
               _ino,
               fh,
               offset,
               data,
               _flags);

        let mut f = file_handle_or_return!(self, &fh, reply);
        debug!("File: {:?}", f);

        if let Err(e) = f.seek(SeekFrom::Start(offset)) {
            error!("Unable to seek to position: {}", offset);
            reply.error(e.raw_os_error().unwrap_or(ENOENT));
            return;
        }

        match f.write(&data) {
            Ok(size) => {
                debug!("Written {} bytes", size);
                reply.written(size as u32);
            }
            Err(e) => reply.error(e.raw_os_error().unwrap_or(ENOENT)),
        }
    }

    fn mkdir(&mut self, _req: &Request, parent: u64, name: &OsStr, _mode: u32, reply: ReplyEntry) {
        trace!("mkdir(parent={}, name={:?}, mode={})", parent, name, _mode);

        let path = full_path_or_return!(self, &parent, name, reply);

        match fs::create_dir(&path) {
            Ok(()) => {
                trace!("Subdir created");
                match path.metadata() {
                    Ok(metadata) => {
                        let attr = fileattr_from(&metadata);
                        self.ino_paths.insert(attr.ino, path);
                        let ttl = Timespec::new(1, 0);
                        reply.entry(&ttl, &attr, 0);
                    }
                    Err(e) => {
                        error!("std::fs::metadata: {}", e);
                        reply.error(e.raw_os_error().unwrap_or(ENOENT));
                    }
                }
            }
            Err(e) => reply.error(e.raw_os_error().unwrap_or(ENOENT)),
        }
    }

    fn setattr(&mut self,
               _req: &Request,
               ino: u64,
               mode: Option<u32>,
               _uid: Option<u32>,
               _gid: Option<u32>,
               _size: Option<u64>,
               _atime: Option<Timespec>,
               _mtime: Option<Timespec>,
               _fh: Option<u64>,
               _crtime: Option<Timespec>,
               _chgtime: Option<Timespec>,
               _bkuptime: Option<Timespec>,
               _flags: Option<u32>,
               reply: ReplyAttr) {
        trace!("setattr(ino={}, mode={:?}, uid={:?}, gid={:?}, size={:?}, atime={:?}, \
                mtime={:?}, fh={:?}, crtime={:?}, chgtime={:?}, bkuptime={:?}, flags={:?})",
               ino,
               mode,
               _uid,
               _gid,
               _size,
               _atime,
               _mtime,
               _fh,
               _crtime,
               _chgtime,
               _bkuptime,
               _flags);

        let path = ino_path_or_return!(self, &ino, reply);
        let old_metadata = ok_or_return_error!(std::fs::metadata(&path), ENOENT, reply);
        debug!("{:?}", old_metadata);

        if let Some(new_mode) = mode {
            use std::os::unix::fs::PermissionsExt;
            debug!("new_mode: {}", new_mode);
            let mut perms = old_metadata.permissions();
            perms.set_mode(new_mode);
            if let Err(e) = fs::set_permissions(&path, perms) {
                reply.error(e.raw_os_error().unwrap_or(ENOENT));
                return;
            }
        }

        none_or_return_error!(_uid, reply);
        none_or_return_error!(_gid, reply);
        none_or_return_error!(_size, reply);
        none_or_return_error!(_atime, reply);
        none_or_return_error!(_mtime, reply);
        none_or_return_error!(_crtime, reply);
        none_or_return_error!(_chgtime, reply);
        none_or_return_error!(_bkuptime, reply);

        let new_metadata = ok_or_return_error!(std::fs::metadata(&path), ENOENT, reply);
        let attr = fileattr_from(&new_metadata);
        debug!("{:?}", new_metadata);
        let ttl = Timespec::new(1, 0);
        reply.attr(&ttl, &attr);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::ops::RangeFrom;
use {Command, Error, Record};

const ORIGIN: usize = 0;

/// A history of commands.
#[derive(Debug)]
pub struct History<R> {
    id: usize,
    parent: usize,
    next: RangeFrom<usize>,
    record: Record<R>,
    branches: HashMap<usize, Branch<R>>,
}

impl<R> History<R> {
    /// Returns a new history.
    #[inline]
    pub fn new(receiver: impl Into<R>) -> History<R> {
        History {
            id: ORIGIN,
            parent: ORIGIN,
            next: 1..,
            record: Record::new(receiver),
            branches: HashMap::new(),
        }
    }

    /// Pushes the command to the top of the history and executes its [`apply`] method.
    /// The command is merged with the previous top command if they have the same [`id`].
    ///
    /// # Errors
    /// If an error occur when executing [`apply`] the error is returned together with the command.
    ///
    /// [`apply`]: trait.Command.html#tymethod.apply
    /// [`id`]: trait.Command.html#method.id
    #[inline]
    pub fn apply(&mut self, cmd: impl Command<R> + 'static) -> Result<Option<usize>, Error<R>>
    where
        R: 'static,
    {
        let cursor = self.record.cursor();
        let commands = self.record.apply(cmd)?;
        let commands: Vec<_> = commands.collect();
        if !commands.is_empty() {
            let id = self.next.next().unwrap();
            self.branches.insert(
                id,
                Branch {
                    parent: self.id,
                    cursor,
                    commands,
                },
            );
            Ok(Some(id))
        } else {
            Ok(None)
        }
    }

    /// Calls the [`undo`] method for the active command and sets the previous one as the new active one.
    ///
    /// # Errors
    /// If an error occur when executing [`undo`] the error is returned together with the command.
    ///
    /// [`undo`]: trait.Command.html#tymethod.undo
    #[inline]
    #[must_use]
    pub fn undo(&mut self) -> Option<Result<(), Error<R>>> {
        self.record.undo()
    }

    /// Calls the [`redo`] method for the active command and sets the next one as the
    /// new active one.
    ///
    /// # Errors
    /// If an error occur when executing [`redo`] the error is returned together with the command.
    ///
    /// [`redo`]: trait.Command.html#method.redo
    #[inline]
    #[must_use]
    pub fn redo(&mut self) -> Option<Result<(), Error<R>>> {
        self.record.redo()
    }

    /// Jumps to the command in `branch` at `cursor`.
    #[inline]
    #[must_use]
    pub fn jump_to(&mut self, mut branch: usize, cursor: usize) -> Option<Result<(), Error<R>>>
    where
        R: 'static,
    {
        if self.id == branch {
            return self.record.jump_to(cursor);
        }

        // Find the path from `dest` to `ORIGIN`.
        let mut dest = self.branches.remove(&branch)?;
        branch = dest.parent;
        let mut path = vec![dest];
        while branch != ORIGIN {
            dest = self.branches.remove(&branch).unwrap();
            branch = dest.parent;
            path.push(dest);
        }

        // Find the path from `start` to `ORIGIN`.
        if self.id != ORIGIN {
            let mut start = self.branches.remove(&self.parent).unwrap();
            branch = start.parent;
            let mut tmp = vec![start];
            while branch != ORIGIN {
                start = self.branches.remove(&branch).unwrap();
                branch = start.parent;
                tmp.push(start);
            }
            tmp.reverse();
            path.append(&mut tmp);
        }

        // Walk the path from `start` to `dest`.
        while let Some(dest) = path.pop() {
            // Move to `dest.cursor` either by undoing or redoing.
            if let Err(err) = self.record.jump_to(dest.cursor).unwrap() {
                return Some(Err(err));
            }
            // Apply the commands in the branch and move older commands into their own branch.
            for cmd in dest.commands {
                let cursor = self.record.cursor();
                let commands = match self.record.apply(cmd) {
                    Ok(commands) => commands,
                    Err(err) => return Some(Err(err)),
                };
                let commands: Vec<_> = commands.collect();
                if !commands.is_empty() {
                    self.branches.insert(
                        self.id,
                        Branch {
                            parent: dest.parent,
                            cursor,
                            commands,
                        },
                    );
                    self.id = dest.parent;
                }
            }
        }

        Some(Ok(()))
    }
}

impl<R: Default> Default for History<R> {
    #[inline]
    fn default() -> History<R> {
        History::new(R::default())
    }
}

impl<R> From<R> for History<R> {
    #[inline]
    fn from(receiver: R) -> Self {
        History::new(receiver)
    }
}

struct Branch<R> {
    parent: usize,
    cursor: usize,
    commands: Vec<Box<dyn Command<R> + 'static>>,
}

impl<R> Debug for Branch<R> {
    #[inline]
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.debug_struct("Branch")
            .field("parent", &self.parent)
            .field("cursor", &self.cursor)
            .field("commands", &self.commands)
            .finish()
    }
}

/// Builder for a history.
#[derive(Debug)]
pub struct HistoryBuilder<R> {
    receiver: PhantomData<R>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::error::Error;

    #[derive(Debug)]
    struct Add(char);

    impl Command<String> for Add {
        fn apply(&mut self, receiver: &mut String) -> Result<(), Box<dyn Error>> {
            receiver.push(self.0);
            Ok(())
        }

        fn undo(&mut self, receiver: &mut String) -> Result<(), Box<dyn Error>> {
            self.0 = receiver.pop().ok_or("`receiver` is empty")?;
            Ok(())
        }
    }

    #[test]
    fn jump_to() {
        let mut history = History::default();
        history.apply(Add('a')).unwrap();
        history.apply(Add('b')).unwrap();
        history.apply(Add('c')).unwrap();
        history.apply(Add('d')).unwrap();
        history.apply(Add('e')).unwrap();

        history.undo().unwrap().unwrap();
        history.undo().unwrap().unwrap();

        let b = history.apply(Add('f')).unwrap().unwrap();
        history.apply(Add('g')).unwrap();

        history.jump_to(b, 5).unwrap().unwrap();
    }
}
<commit_msg>Only walk path until LCA is found<commit_after>use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::ops::RangeFrom;
use {Command, Error, Record};

const ORIGIN: usize = 0;

/// A history of commands.
#[derive(Debug)]
pub struct History<R> {
    id: usize,
    parent: usize,
    next: RangeFrom<usize>,
    record: Record<R>,
    branches: HashMap<usize, Branch<R>>,
}

impl<R> History<R> {
    /// Returns a new history.
    #[inline]
    pub fn new(receiver: impl Into<R>) -> History<R> {
        History {
            id: ORIGIN,
            parent: ORIGIN,
            next: 1..,
            record: Record::new(receiver),
            branches: HashMap::new(),
        }
    }

    /// Pushes the command to the top of the history and executes its [`apply`] method.
    /// The command is merged with the previous top command if they have the same [`id`].
    ///
    /// # Errors
    /// If an error occur when executing [`apply`] the error is returned together with the command.
    ///
    /// [`apply`]: trait.Command.html#tymethod.apply
    /// [`id`]: trait.Command.html#method.id
    #[inline]
    pub fn apply(&mut self, cmd: impl Command<R> + 'static) -> Result<Option<usize>, Error<R>>
    where
        R: 'static,
    {
        let cursor = self.record.cursor();
        let commands = self.record.apply(cmd)?;
        let commands: Vec<_> = commands.collect();
        if !commands.is_empty() {
            let id = self.next.next().unwrap();
            self.branches.insert(
                id,
                Branch {
                    parent: self.id,
                    cursor,
                    commands,
                },
            );
            Ok(Some(id))
        } else {
            Ok(None)
        }
    }

    /// Calls the [`undo`] method for the active command and sets the previous one as the new active one.
    ///
    /// # Errors
    /// If an error occur when executing [`undo`] the error is returned together with the command.
    ///
    /// [`undo`]: trait.Command.html#tymethod.undo
    #[inline]
    #[must_use]
    pub fn undo(&mut self) -> Option<Result<(), Error<R>>> {
        self.record.undo()
    }

    /// Calls the [`redo`] method for the active command and sets the next one as the
    /// new active one.
    ///
    /// # Errors
    /// If an error occur when executing [`redo`] the error is returned together with the command.
    ///
    /// [`redo`]: trait.Command.html#method.redo
    #[inline]
    #[must_use]
    pub fn redo(&mut self) -> Option<Result<(), Error<R>>> {
        self.record.redo()
    }

    /// Jumps to the command in `branch` at `cursor`.
    #[inline]
    #[must_use]
    pub fn jump_to(&mut self, mut branch: usize, cursor: usize) -> Option<Result<(), Error<R>>>
    where
        R: 'static,
    {
        use std::collections::HashSet;

        if self.id == branch {
            return self.record.jump_to(cursor);
        }

        // All visited nodes.
        let visited = {
            let mut visited = HashSet::new();
            // Find the path from `dest` to `ORIGIN`.
            let mut dest = self.branches.get(&branch)?;
            while dest.parent != ORIGIN {
                assert!(visited.insert(dest.parent));
                dest = self.branches.get(&dest.parent).unwrap();
            }
            visited
        };

        let mut path = vec![];
        // Find the path from `start` to the lowest common ancestor of `dest`.
        if self.id != ORIGIN {
            let mut start = self.branches.remove(&self.parent).unwrap();
            branch = start.parent;
            while !visited.contains(&branch) {
                path.push(start);
                start = self.branches.remove(&branch).unwrap();
                branch = start.parent;
            }
        }

        // Find the path from `dest` to the lowest common ancestor of `start`.
        let mut dest = self.branches.remove(&branch)?;
        branch = dest.parent;
        let mut tmp = vec![dest];
        let last = path.last().map_or(ORIGIN, |last| last.parent);
        while branch != ORIGIN || branch != last {
            dest = self.branches.remove(&branch).unwrap();
            branch = dest.parent;
            tmp.push(dest);
        }
        tmp.reverse();
        path.append(&mut tmp);

        // Walk the path from `start` to `dest`.
        for dest in path {
            // Move to `dest.cursor` either by undoing or redoing.
            if let Err(err) = self.record.jump_to(dest.cursor).unwrap() {
                return Some(Err(err));
            }
            // Apply the commands in the branch and move older commands into their own branch.
            for cmd in dest.commands {
                let cursor = self.record.cursor();
                let commands = match self.record.apply(cmd) {
                    Ok(commands) => commands,
                    Err(err) => return Some(Err(err)),
                };
                let commands: Vec<_> = commands.collect();
                if !commands.is_empty() {
                    self.branches.insert(
                        self.id,
                        Branch {
                            parent: dest.parent,
                            cursor,
                            commands,
                        },
                    );
                    self.id = dest.parent;
                }
            }
        }

        Some(Ok(()))
    }
}

impl<R: Default> Default for History<R> {
    #[inline]
    fn default() -> History<R> {
        History::new(R::default())
    }
}

impl<R> From<R> for History<R> {
    #[inline]
    fn from(receiver: R) -> Self {
        History::new(receiver)
    }
}

struct Branch<R> {
    parent: usize,
    cursor: usize,
    commands: Vec<Box<dyn Command<R> + 'static>>,
}

impl<R> Debug for Branch<R> {
    #[inline]
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.debug_struct("Branch")
            .field("parent", &self.parent)
            .field("cursor", &self.cursor)
            .field("commands", &self.commands)
            .finish()
    }
}

/// Builder for a history.
#[derive(Debug)]
pub struct HistoryBuilder<R> {
    receiver: PhantomData<R>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::error::Error;

    #[derive(Debug)]
    struct Add(char);

    impl Command<String> for Add {
        fn apply(&mut self, receiver: &mut String) -> Result<(), Box<dyn Error>> {
            receiver.push(self.0);
            Ok(())
        }

        fn undo(&mut self, receiver: &mut String) -> Result<(), Box<dyn Error>> {
            self.0 = receiver.pop().ok_or("`receiver` is empty")?;
            Ok(())
        }
    }

    #[test]
    fn jump_to() {
        let mut history = History::default();
        history.apply(Add('a')).unwrap();
        history.apply(Add('b')).unwrap();
        history.apply(Add('c')).unwrap();
        history.apply(Add('d')).unwrap();
        history.apply(Add('e')).unwrap();

        history.undo().unwrap().unwrap();
        history.undo().unwrap().unwrap();

        let b = history.apply(Add('f')).unwrap().unwrap();
        history.apply(Add('g')).unwrap();

        history.jump_to(b, 5).unwrap().unwrap();
    }
}
<|endoftext|>