<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>>,
    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));
    }
}
<commit_msg>build: fix makefile gen for one source file<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() {
            // Multiple sources
            if source_list.len() > 1 {
                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));
                    }
                }
            // One source file
            } else {
                for src in source_list {
                    horrid_string.push_str(&format!("{}\n", src));
                }
            }
            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 parsed_obj_list.len() > 1 {
                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!("{}", split_last.0));
                    }
                }
            } else {
                for obj in parsed_obj_list {
                    horrid_string.push_str(&format!("\t{}\n", obj));
                }
            }
        }
        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>//! An rope.
//!
//! A rope is an efficient data structure for large mutable strings. It's
//! essentially a binary tree whose leaves are strings.
//!
//! For more information, see the following resources:
//! + http://scienceblogs.com/goodmath/2009/01/26/ropes-twining-together-strings/
//! + https://www.ibm.com/developerworks/library/j-ropes/
//! + http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9450&rep=rep1&type=pdf

#![feature(const_fn)]
#![feature(box_syntax, box_patterns)]

use std::cmp;
use std::ops;
use std::convert;

#[derive(Debug)]
pub struct Rope {
    // can we get away with having these be of &str or will they need
    // to be string?
    root: Node
}

use self::Node::*;

/// A `Node` in the `Rope`'s tree.
///
/// A `Node` is either a `Leaf` holding a `String`, or a
/// a `Branch` concatenating together two `Node`s.
#[derive(Debug)]
enum Node {
    /// A leaf node
    Leaf(String)
  , /// A branch concatenating together `l`eft and `r`ight nodes.
    Branch {
        /// The length of this node
        len: usize
      , /// The weight of a node is the summed weight of its left subtree
        weight: usize
      , /// The left branch node
        left: Option<Box<Node>>
      , /// The right branch node
        right: Option<Box<Node>>
    }
}

impl Node {
    const fn none() -> Self {
        Branch { len: 0
               , weight: 0
               , left: None
               , right: None
               }
    }

    /// Concatenate two `Node`s to return a new `Branch` node.
    fn branch(left: Self, right: Self) -> Self {
        Branch { len: left.len() + right.len()
               , weight: left.weight()
               , left: Some(box left)
               , right: Some(box right)
        }
    }

    #[inline]
    fn leaf(string: String) -> Self {
        Leaf(string)
    }

    /// Returns true if this node is balanced
    #[inline]
    fn is_balanced(&self) -> bool {
        unimplemented!()
    }

    /// Returns the depth in the tree of a node
    #[inline]
    fn depth(&self) -> usize {
        use std::cmp::max;

        match self {
            &Node::Leaf(_) => 0
          , &Node::Branch { ref left, ref right, ..} =>
                max( left.as_ref().map(Box::as_ref).map_or(0, Node::depth)
                   , right.as_ref().map(Box::as_ref).map_or(0, Node::depth)
                   ) + 1
            }
    }

    /// Returns the length of a node
    //  TODO: do we want to cache this?
    fn len(&self) -> usize {
        match self { &Node::Leaf(ref s) => s.len()
                   , &Node::Branch { ref left, ref right, .. } =>
                        left.as_ref().map(Box::as_ref).map_or(0, Node::len) +
                        right.as_ref().map(Box::as_ref).map_or(0, Node::len)
                    }
    }

    /// Returns the weight of a node
    fn weight (&self) -> usize {
        match self { &Node::Leaf(ref s) => s.len()
                   , &Node::Branch { ref left, .. } =>
                        left.as_ref().map(Box::as_ref).map_or(0, Node::weight)
                    }
    }
}

impl ops::Add for Node {
    type Output = Self;
    /// Concatenate two `Node`s, returning a `Branch` node.
    fn add(self, right: Self) -> Self { Node::branch(self, right) }
}

impl Rope {

    /// Returns a new empty Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::<u8>::new();
    /// assert_eq!(an_rope.len(), 0);
    /// ```
    pub const fn new() -> Rope {
        Rope { root: Node::none() }
    }

    /// Returns the length of this Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_empty_rope = Rope::new();
    /// assert_eq!(an_empty_rope.len(), 0);
    /// ```
    /// ```
    /// use an_rope::Rope;
    /// let mut an_empty_rope = Rope::from(String::from(""));
    /// assert_eq!(an_empty_rope.len(), 0);
    /// ```
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("a string"));
    /// assert_eq!(an_rope.len(), "a string".len());
    /// ```
    pub fn len(&self) -> usize {
        // self.root.len()
        unimplemented!()
    }

    /// Appends a `Rope` to the end of this `Rope`, returning a new `Rope`
    ///
    /// Note that this is equivalent to using the `+` operator.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let an_rope = Rope::from(String::from("abcd"));
    /// let another_rope = an_rope.merge(Rope::from(String::from("efgh")))
    /// assert_eq!(another_rope, Rope::from(String::from("abcdefgh")));
    /// ```
    pub fn merge(&self, other: &Rope) -> Rope {
        unimplemented!()
    }

    /// Appends a `Rope` to the end of this `Rope`, updating it in place.
    ///
    /// Note that this is equivalent to using the `+=` operator.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("abcd"));
    /// an_rope.update(Rope::from(String::from("efgh")))
    /// assert_eq!(an_rope, Rope::from(String::from("abcdefgh")));
    /// ```
    pub fn update(&mut self, other: Rope) {
        unimplemented!()
    }

    /// Splits the rope into two ropes at the given index.
    ///
    /// Consumes this rope.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("abcd"));
    /// let (ab, cd) = an_rope.split(2);
    /// assert_eq!(ab, Rope::from(String::from("ab")));
    /// assert_eq!(cd, Rope::from(String::from("cd")));
    /// ```
    pub fn split(self, index: usize) -> (Rope, Rope) {
        unimplemented!()
    }

    /// Rebalances this entire `Rope`, returning a balanced `Rope`.
    fn rebalance(self) -> Self {
        if self.is_balanced() {
            // the rope is already balanced, do nothing
            self
        } else {
            // rebalance the rope
            unimplemented!()
        }
    }

    /// Returns true if this `Rope` is balanced.
    ///
    /// Balancing invariant:
    /// the rope length needs to be less than _F_(rope_length) where F is fibonacci
    #[inline]
    fn is_balanced(&self) -> bool {
        self.root.is_balanced()
    }
}

impl ops::Index<usize> for Node {
    type Output = str;

    fn index(&self, i: usize) -> &str {
        let len = self.len();
        match *self { Node::Leaf(ref vec) => { &vec[i..i+1] }
                    , Node::Branch { right: Some(box ref r), .. } if len < i =>
                        &r[i - len]
                    , Node::Branch { left: Some(box ref l), .. } => &l[i]
                    , _ =>
                        panic!("Index out of bounds!")
                    }
    }
}

impl convert::Into<Vec<u8>> for Rope {
    fn into(self) -> Vec<u8> {
        unimplemented!()
    }

}

impl convert::From<String> for Rope {
    fn from(string: String) -> Rope {
        Rope {
            root: if string.len() == 0 { Node::none() }
                  else { Node::Leaf(string) }
        }
    }
}

//-- comparisons ----------------------------------------------------
impl cmp::PartialEq for Rope {
    fn eq(&self, other: &Rope) -> bool {
        unimplemented!()
    }
}

impl cmp::PartialEq<str> for Rope {
    fn eq(&self, other: &str) -> bool {
        unimplemented!()
    }
}

//-- concatenation --------------------------------------------------
impl<'a> ops::Add for &'a Rope {
    type Output = Rope;
    /// Non-destructively concatenate two `Rope`s, returning a new `Rope`.
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( &rope + &Rope::from(String::from("cd"))
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: Self) -> Rope { self.merge(other) }

}

impl ops::Add for Rope {
    type Output = Rope;
    /// Non-destructively concatenate two `Rope`s, returning a new `Rope`.
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( rope + Rope::from(String::from("cd"))
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: Self) -> Rope { self.merge(&other) }
}

impl ops::Add<String> for Rope {
    type Output = Rope;
    /// Non-destructively concatenate a `Rope` and a `String`.
    ///
    /// Returns a new `Rope`
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( rope + String::from("cd"))
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: String) -> Rope {
         self.merge(&Rope::from(other))
    }
}

impl<'a, 'b> ops::Add<&'b str> for &'a Rope {
    type Output = Rope;
    /// Non-destructively concatenate a `Rope` and an `&str`.
    ///
    /// Returns a new `Rope`
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( &rope + "cd")
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: &'b str) -> Rope {
         self.merge(&Rope::from(other.to_owned()))
     }

}

impl<'a> ops::Add<&'a str> for Rope {
    type Output = Rope;
    /// Non-destructively concatenate a `Rope` and an `&str`.
    ///
    /// Returns a new `Rope`
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( rope + "cd")
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: &'a str) -> Rope {
         self.merge(&Rope::from(other.to_owned()))
     }

}

impl ops::AddAssign for Rope {

    /// Concatenate two `Rope`s mutably.
    ///
    /// # Examples
    /// ```
    /// let mut rope = Rope::from(String::from("ab"));
    /// rope += Rope::from(String::from("cd"));
    /// assert_eq!(rope, Rope::from(String::from("abcd")));
    /// ```
    #[inline]
    fn add_assign(&mut self, other: Rope) {
        self.update(other)
    }
}

impl ops::AddAssign<String> for Rope {

    /// Concatenate a `String` onto a `Rope` mutably.
    ///
    /// # Examples
    /// ```
    /// let mut rope = Rope::from(String::from("ab"));
    /// rope += String::from("cd");
    /// assert_eq!(rope, Rope::from(String::from("abcd")));
    /// ```
    #[inline]
    fn add_assign(&mut self, string: String) {
        self.update(Rope::from(string))
    }
}

impl<'a> ops::AddAssign<&'a str> for Rope {

    /// Concatenate an `&str` onto a `Rope` mutably.
    ///
    /// # Examples
    /// ```
    /// let mut rope = Rope::from(String::from("ab"));
    /// rope += String::from("cd");
    /// assert_eq!(rope, Rope::from(String::from("abcd")));
    /// ```
    #[inline]
    fn add_assign(&mut self, string: &'a str) {
        self.update(Rope::from(string.to_owned()))
    }
}

impl ops::Index<usize> for Rope {
    type Output = str;

    /// Recursively index the Rope to return the `i` th character.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let an_rope = Rope::from(String::from("abcd"));
    /// assert_eq!(&an_rope[0], "a");
    /// assert_eq!(&an_rope[1], "b");
    /// assert_eq!(&an_rope[2], "c");
    /// assert_eq!(&an_rope[3], "d");
    /// ```
    ///
    /// # Time complexity
    /// _O_(log _n_)
    ///
    #[inline]
    fn index(&self, i: usize) -> &str {
        &self.root[i]
    }
}

//-- slicing operators ----------------------------------------------
impl ops::Index<ops::Range<usize>> for Rope {
    type Output = str;

    // Index a substring
    fn index(&self, i: ops::Range<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::Index<ops::RangeTo<usize>> for Rope {
    type Output = str;

    fn index(&self, i: ops::RangeTo<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::Index<ops::RangeFrom<usize>> for Rope {
    type Output = str;

    fn index(&self, i: ops::RangeFrom<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::Range<usize>> for Rope {
    fn index_mut(&mut self, i: ops::Range<usize>) -> &mut str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::RangeTo<usize>> for Rope {
    fn index_mut(&mut self, i: ops::RangeTo<usize>) -> &mut str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::RangeFrom<usize>> for Rope {
    fn index_mut(&mut self, i: ops::RangeFrom<usize>) -> &mut str {
        unimplemented!()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>rope: implemented Node.is_balanced()<commit_after>//! An rope.
//!
//! A rope is an efficient data structure for large mutable strings. It's
//! essentially a binary tree whose leaves are strings.
//!
//! For more information, see the following resources:
//! + http://scienceblogs.com/goodmath/2009/01/26/ropes-twining-together-strings/
//! + https://www.ibm.com/developerworks/library/j-ropes/
//! + http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9450&rep=rep1&type=pdf

#![feature(const_fn)]
#![feature(box_syntax, box_patterns)]

use std::cmp;
use std::ops;
use std::convert;

#[derive(Debug)]
pub struct Rope {
    // can we get away with having these be of &str or will they need
    // to be string?
    root: Node
}

use self::Node::*;

/// A `Node` in the `Rope`'s tree.
///
/// A `Node` is either a `Leaf` holding a `String`, or a
/// a `Branch` concatenating together two `Node`s.
#[derive(Debug)]
enum Node {
    /// A leaf node
    Leaf(String)
  , /// A branch concatenating together `l`eft and `r`ight nodes.
    Branch {
        /// The length of this node
        len: usize
      , /// The weight of a node is the summed weight of its left subtree
        weight: usize
      , /// The left branch node
        left: Option<Box<Node>>
      , /// The right branch node
        right: Option<Box<Node>>
    }
}

/// Returns the _n_th fibonacci number.
// TODO: replace with an iterative implementation and/or lookup table?
fn fibonacci(n: usize) -> usize {
    match n { 1 => 1
            , 2 => 1
            , _ => fibonacci(n - 1) + fibonacci(n - 2)
            }
}

impl Node {
    const fn none() -> Self {
        Branch { len: 0
               , weight: 0
               , left: None
               , right: None
               }
    }

    /// Concatenate two `Node`s to return a new `Branch` node.
    fn branch(left: Self, right: Self) -> Self {
        Branch { len: left.len() + right.len()
               , weight: left.weight()
               , left: Some(box left)
               , right: Some(box right)
        }
    }

    #[inline]
    fn leaf(string: String) -> Self {
        Leaf(string)
    }

    /// Returns true if this node is balanced
    ///
    /// "We define the depth of a leaf to be 0, and the depth of a
    /// concatenation to be one plus the maximum depth of its children. Let
    /// _Fn_ be the _n_th Fibonacci number. A rope of depth _n_ is balanced if
    /// its length is at least _Fn_+2, e.g. a balanced rope of depth 1 must
    /// have length at least 2. Note that balanced ropes may contain unbalanced
    /// subropes."
    /// – from "Ropes: An Alternative to Strings"
    #[inline]
    fn is_balanced(&self) -> bool {
        self.len() >= (fibonacci(self.depth()) + 2)
    }

    /// Returns the depth in the tree of a node
    #[inline]
    fn depth(&self) -> usize {
        use std::cmp::max;

        match self {
            &Node::Leaf(_) => 0
          , &Node::Branch { ref left, ref right, ..} =>
                max( left.as_ref().map(Box::as_ref).map_or(0, Node::depth)
                   , right.as_ref().map(Box::as_ref).map_or(0, Node::depth)
                   ) + 1
            }
    }

    /// Returns the length of a node
    //  TODO: do we want to cache this?
    fn len(&self) -> usize {
        match self { &Node::Leaf(ref s) => s.len()
                   , &Node::Branch { ref left, ref right, .. } =>
                        left.as_ref().map(Box::as_ref).map_or(0, Node::len) +
                        right.as_ref().map(Box::as_ref).map_or(0, Node::len)
                    }
    }

    /// Returns the weight of a node
    fn weight (&self) -> usize {
        match self { &Node::Leaf(ref s) => s.len()
                   , &Node::Branch { ref left, .. } =>
                        left.as_ref().map(Box::as_ref).map_or(0, Node::weight)
                    }
    }
}

impl ops::Add for Node {
    type Output = Self;
    /// Concatenate two `Node`s, returning a `Branch` node.
    fn add(self, right: Self) -> Self { Node::branch(self, right) }
}

impl Rope {

    /// Returns a new empty Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::<u8>::new();
    /// assert_eq!(an_rope.len(), 0);
    /// ```
    pub const fn new() -> Rope {
        Rope { root: Node::none() }
    }

    /// Returns the length of this Rope
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_empty_rope = Rope::new();
    /// assert_eq!(an_empty_rope.len(), 0);
    /// ```
    /// ```
    /// use an_rope::Rope;
    /// let mut an_empty_rope = Rope::from(String::from(""));
    /// assert_eq!(an_empty_rope.len(), 0);
    /// ```
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("a string"));
    /// assert_eq!(an_rope.len(), "a string".len());
    /// ```
    pub fn len(&self) -> usize {
        // self.root.len()
        unimplemented!()
    }

    /// Appends a `Rope` to the end of this `Rope`, returning a new `Rope`
    ///
    /// Note that this is equivalent to using the `+` operator.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let an_rope = Rope::from(String::from("abcd"));
    /// let another_rope = an_rope.merge(Rope::from(String::from("efgh")))
    /// assert_eq!(another_rope, Rope::from(String::from("abcdefgh")));
    /// ```
    pub fn merge(&self, other: &Rope) -> Rope {
        unimplemented!()
    }

    /// Appends a `Rope` to the end of this `Rope`, updating it in place.
    ///
    /// Note that this is equivalent to using the `+=` operator.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("abcd"));
    /// an_rope.update(Rope::from(String::from("efgh")))
    /// assert_eq!(an_rope, Rope::from(String::from("abcdefgh")));
    /// ```
    pub fn update(&mut self, other: Rope) {
        unimplemented!()
    }

    /// Splits the rope into two ropes at the given index.
    ///
    /// Consumes this rope.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let mut an_rope = Rope::from(String::from("abcd"));
    /// let (ab, cd) = an_rope.split(2);
    /// assert_eq!(ab, Rope::from(String::from("ab")));
    /// assert_eq!(cd, Rope::from(String::from("cd")));
    /// ```
    pub fn split(self, index: usize) -> (Rope, Rope) {
        unimplemented!()
    }

    /// Rebalances this entire `Rope`, returning a balanced `Rope`.
    fn rebalance(self) -> Self {
        if self.is_balanced() {
            // the rope is already balanced, do nothing
            self
        } else {
            // rebalance the rope
            unimplemented!()
        }
    }

    /// Returns true if this `Rope` is balanced.
    ///
    /// Balancing invariant:
    /// the rope length needs to be less than _F_(rope_length) where F is fibonacci
    #[inline]
    fn is_balanced(&self) -> bool {
        self.root.is_balanced()
    }
}

impl ops::Index<usize> for Node {
    type Output = str;

    fn index(&self, i: usize) -> &str {
        let len = self.len();
        match *self { Node::Leaf(ref vec) => { &vec[i..i+1] }
                    , Node::Branch { right: Some(box ref r), .. } if len < i =>
                        &r[i - len]
                    , Node::Branch { left: Some(box ref l), .. } => &l[i]
                    , _ =>
                        panic!("Index out of bounds!")
                    }
    }
}

impl convert::Into<Vec<u8>> for Rope {
    fn into(self) -> Vec<u8> {
        unimplemented!()
    }

}

impl convert::From<String> for Rope {
    fn from(string: String) -> Rope {
        Rope {
            root: if string.len() == 0 { Node::none() }
                  else { Node::Leaf(string) }
        }
    }
}

//-- comparisons ----------------------------------------------------
impl cmp::PartialEq for Rope {
    fn eq(&self, other: &Rope) -> bool {
        unimplemented!()
    }
}

impl cmp::PartialEq<str> for Rope {
    fn eq(&self, other: &str) -> bool {
        unimplemented!()
    }
}

//-- concatenation --------------------------------------------------
impl<'a> ops::Add for &'a Rope {
    type Output = Rope;
    /// Non-destructively concatenate two `Rope`s, returning a new `Rope`.
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( &rope + &Rope::from(String::from("cd"))
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: Self) -> Rope { self.merge(other) }

}

impl ops::Add for Rope {
    type Output = Rope;
    /// Non-destructively concatenate two `Rope`s, returning a new `Rope`.
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( rope + Rope::from(String::from("cd"))
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: Self) -> Rope { self.merge(&other) }
}

impl ops::Add<String> for Rope {
    type Output = Rope;
    /// Non-destructively concatenate a `Rope` and a `String`.
    ///
    /// Returns a new `Rope`
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( rope + String::from("cd"))
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: String) -> Rope {
         self.merge(&Rope::from(other))
    }
}

impl<'a, 'b> ops::Add<&'b str> for &'a Rope {
    type Output = Rope;
    /// Non-destructively concatenate a `Rope` and an `&str`.
    ///
    /// Returns a new `Rope`
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( &rope + "cd")
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: &'b str) -> Rope {
         self.merge(&Rope::from(other.to_owned()))
     }

}

impl<'a> ops::Add<&'a str> for Rope {
    type Output = Rope;
    /// Non-destructively concatenate a `Rope` and an `&str`.
    ///
    /// Returns a new `Rope`
    ///
    /// # Examples
    /// ```
    /// let rope = Rope::from(String::from("ab"));
    /// assert_eq!( rope + "cd")
    ///           , Rope::from(String::from("abcd")));
    /// ```
    #[inline] fn add(self, other: &'a str) -> Rope {
         self.merge(&Rope::from(other.to_owned()))
     }

}

impl ops::AddAssign for Rope {

    /// Concatenate two `Rope`s mutably.
    ///
    /// # Examples
    /// ```
    /// let mut rope = Rope::from(String::from("ab"));
    /// rope += Rope::from(String::from("cd"));
    /// assert_eq!(rope, Rope::from(String::from("abcd")));
    /// ```
    #[inline]
    fn add_assign(&mut self, other: Rope) {
        self.update(other)
    }
}

impl ops::AddAssign<String> for Rope {

    /// Concatenate a `String` onto a `Rope` mutably.
    ///
    /// # Examples
    /// ```
    /// let mut rope = Rope::from(String::from("ab"));
    /// rope += String::from("cd");
    /// assert_eq!(rope, Rope::from(String::from("abcd")));
    /// ```
    #[inline]
    fn add_assign(&mut self, string: String) {
        self.update(Rope::from(string))
    }
}

impl<'a> ops::AddAssign<&'a str> for Rope {

    /// Concatenate an `&str` onto a `Rope` mutably.
    ///
    /// # Examples
    /// ```
    /// let mut rope = Rope::from(String::from("ab"));
    /// rope += String::from("cd");
    /// assert_eq!(rope, Rope::from(String::from("abcd")));
    /// ```
    #[inline]
    fn add_assign(&mut self, string: &'a str) {
        self.update(Rope::from(string.to_owned()))
    }
}

impl ops::Index<usize> for Rope {
    type Output = str;

    /// Recursively index the Rope to return the `i` th character.
    ///
    /// # Examples
    /// ```
    /// use an_rope::Rope;
    /// let an_rope = Rope::from(String::from("abcd"));
    /// assert_eq!(&an_rope[0], "a");
    /// assert_eq!(&an_rope[1], "b");
    /// assert_eq!(&an_rope[2], "c");
    /// assert_eq!(&an_rope[3], "d");
    /// ```
    ///
    /// # Time complexity
    /// _O_(log _n_)
    ///
    #[inline]
    fn index(&self, i: usize) -> &str {
        &self.root[i]
    }
}

//-- slicing operators ----------------------------------------------
impl ops::Index<ops::Range<usize>> for Rope {
    type Output = str;

    // Index a substring
    fn index(&self, i: ops::Range<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::Index<ops::RangeTo<usize>> for Rope {
    type Output = str;

    fn index(&self, i: ops::RangeTo<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::Index<ops::RangeFrom<usize>> for Rope {
    type Output = str;

    fn index(&self, i: ops::RangeFrom<usize>) -> &str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::Range<usize>> for Rope {
    fn index_mut(&mut self, i: ops::Range<usize>) -> &mut str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::RangeTo<usize>> for Rope {
    fn index_mut(&mut self, i: ops::RangeTo<usize>) -> &mut str {
        unimplemented!()
    }
}

impl ops::IndexMut<ops::RangeFrom<usize>> for Rope {
    fn index_mut(&mut self, i: ops::RangeFrom<usize>) -> &mut str {
        unimplemented!()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>use ffi;
use parser::YamlErrorType;
use event::YamlVersionDirective;

use std::ptr;
use std::cast;
use std::c_vec::CVec;
use std::c_str::CString;
use libc;

pub struct YamlBaseEmitter {
    emitter_mem: ffi::yaml_emitter_t
}

impl YamlBaseEmitter {
    fn new() -> YamlBaseEmitter {
        YamlBaseEmitter {
            emitter_mem: ffi::yaml_emitter_t::new()
        }
    }
}

impl Drop for YamlBaseEmitter {
    fn drop(&mut self) {
        unsafe {
            ffi::yaml_emitter_delete(&mut self.emitter_mem);
        }
    }
}

pub struct YamlEmitter<'r> {
    base_emitter: YamlBaseEmitter,
    writer: &'r mut Writer
}

impl<'r> YamlEmitter<'r> {
    pub fn init<'r>(writer: &'r mut Writer) -> ~YamlEmitter<'r> {
        let mut emitter = ~YamlEmitter {
            base_emitter: YamlBaseEmitter::new(),
            writer: writer
        };

        unsafe {
            if ffi::yaml_emitter_initialize(&mut emitter.base_emitter.emitter_mem) == 0 {
                fail!("failed to initialize yaml_emitter_t");
            }

            ffi::yaml_emitter_set_output(&mut emitter.base_emitter.emitter_mem, handle_writer_cb, cast::transmute(&mut *emitter));
        }

        emitter
    }

    pub fn get_error(&self) -> (YamlErrorType, ~str) {
        let emitter_mem = &self.base_emitter.emitter_mem;
        unsafe {
            (YamlErrorType::conv(emitter_mem.error), CString::new(emitter_mem.problem, false).as_str().unwrap().to_owned())
        }
    }

    pub fn emit_stream_start_event(&mut self, encoding: ffi::YamlEncoding) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_stream_start_event_initialize(&mut event, encoding) == 0 {
                fail!("yaml_stream_start_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_stream_end_event(&mut self) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_stream_end_event_initialize(&mut event) == 0 {
                fail!("yaml_stream_end_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_document_start_event(&mut self,
            version_directive: Option<YamlVersionDirective>,
            tag_directives: &[(~str, ~str)],
            implicit: bool)
        -> Result<(), (YamlErrorType, ~str)>
    {
        let mut event = ffi::yaml_event_t::new();
        let mut vsn_dir = ffi::yaml_version_directive_t { major: 0, minor: 0 };
        let c_vsn_dir = match version_directive {
            Some(directive) => {
                vsn_dir.major = directive.major as libc::c_int;
                vsn_dir.minor = directive.minor as libc::c_int;
                &vsn_dir as *ffi::yaml_version_directive_t
            },
            None => ptr::null()
        };

        let c_strs: ~[(CString, CString)] = tag_directives.iter().map(|tuple| {
            (tuple.ref0().to_c_str(), tuple.ref1().to_c_str())
        }).collect();
        let c_tag_dirs: ~[ffi::yaml_tag_directive_t] = c_strs.iter().map(|tuple| {
            ffi::yaml_tag_directive_t {
                handle: tuple.ref0().with_ref(|ptr| {ptr}),
                prefix: tuple.ref1().with_ref(|ptr| {ptr}),
            }
        }).collect();
        let tag_dir_start = c_tag_dirs.as_ptr();
        unsafe {
            let tag_dir_end = tag_dir_start.offset(c_tag_dirs.len() as int);
            let c_implicit = if implicit { 1 } else { 0 };

            if ffi::yaml_document_start_event_initialize(&mut event, c_vsn_dir, tag_dir_start, tag_dir_end, c_implicit) == 0 {
                fail!("yaml_document_start_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_document_end_event(&mut self, implicit: bool) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        let c_implicit = if implicit { 1 } else { 0 };
        unsafe {
            if ffi::yaml_document_end_event_initialize(&mut event, c_implicit) == 0 {
                fail!("yaml_stream_end_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_alias_event(&mut self, anchor: &str) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        let c_anchor = anchor.to_c_str();

        unsafe {
            c_anchor.with_ref(|ptr| {
                if ffi::yaml_alias_event_initialize(&mut event, ptr as *ffi::yaml_char_t) != 0 {
                    fail!("yaml_stream_end_event_initialize failed!")
                }
            });

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_scalar_event(&mut self, anchor: Option<&str>, tag: Option<&str>,
        value: &str, plain_implicit: bool, quoted_implicit: bool,
        style: ffi::YamlScalarStyle) -> Result<(), (YamlErrorType, ~str)>
    {
        let c_anchor = anchor.map(|s| { s.to_c_str() });
        let anchor_ptr = match c_anchor {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_tag = tag.map(|s| { s.to_c_str() });
        let tag_ptr = match c_tag {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_plain_implicit = if plain_implicit { 1 } else { 0 };
        let c_quoted_implicit = if quoted_implicit { 1 } else { 0 };

        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_scalar_event_initialize(&mut event,
                    anchor_ptr as *ffi::yaml_char_t, tag_ptr as *ffi::yaml_char_t,
                    value.as_ptr(), value.len() as libc::c_int,
                    c_plain_implicit, c_quoted_implicit,
                    style) == 0
            {
                fail!("yaml_scalar_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_sequence_start_event(&mut self, anchor: Option<&str>, tag: Option<&str>, implicit: bool,
        style: ffi::YamlSequenceStyle) -> Result<(), (YamlErrorType, ~str)>
    {
        let c_anchor = anchor.map(|s| { s.to_c_str() });
        let anchor_ptr = match c_anchor {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_tag = tag.map(|s| { s.to_c_str() });
        let tag_ptr = match c_tag {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_implicit = if implicit { 1 } else { 0 };

        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_sequence_start_event_initialize(&mut event,
                    anchor_ptr as *ffi::yaml_char_t, tag_ptr as *ffi::yaml_char_t,
                    c_implicit, style) == 0
            {
                fail!("yaml_sequence_start_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_sequence_end_event(&mut self) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_sequence_end_event_initialize(&mut event) == 0 {
                fail!("yaml_sequence_end_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_mapping_start_event(&mut self, anchor: Option<&str>, tag: Option<&str>, implicit: bool,
        style: ffi::YamlSequenceStyle) -> Result<(), (YamlErrorType, ~str)>
    {
        let c_anchor = anchor.map(|s| { s.to_c_str() });
        let anchor_ptr = match c_anchor {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_tag = tag.map(|s| { s.to_c_str() });
        let tag_ptr = match c_tag {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_implicit = if implicit { 1 } else { 0 };

        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_mapping_start_event_initialize(&mut event,
                    anchor_ptr as *ffi::yaml_char_t, tag_ptr as *ffi::yaml_char_t,
                    c_implicit, style) == 0
            {
                fail!("yaml_mapping_start_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_mapping_end_event(&mut self) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_mapping_end_event_initialize(&mut event) == 0 {
                fail!("yaml_mapping_end_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn flush(&mut self) -> Result<(), (YamlErrorType, ~str)> {
        unsafe {
            if ffi::yaml_emitter_flush(&mut self.base_emitter.emitter_mem) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }
}

extern fn handle_writer_cb(data: *mut YamlEmitter, buffer: *u8, size: libc::size_t) -> libc::c_int {
    unsafe {
        let buf = CVec::new(buffer as *mut u8, size as uint);
        let emitter = &mut *data;
        match emitter.writer.write(buf.as_slice()) {
            Ok(()) => 1,
            Err(_) => 0
        }
    }
}

#[cfg(test)]
mod test {
    use std::io::MemWriter;
    use emitter::YamlEmitter;
    use ffi::{YamlUtf8Encoding, YamlPlainScalarStyle, YamlFlowSequenceStyle};

    #[test]
    #[allow(unused_must_use)]
    fn event_emitter_sequence_test() {
        let mut writer = MemWriter::new();
        {
            let mut emitter = YamlEmitter::init(&mut writer);
            emitter.emit_stream_start_event(YamlUtf8Encoding);
            emitter.emit_document_start_event(None, [], true);
            emitter.emit_sequence_start_event(None, None, true, YamlFlowSequenceStyle);
            emitter.emit_scalar_event(None, None, "1", true, false, YamlPlainScalarStyle);
            emitter.emit_scalar_event(None, None, "2", true, false, YamlPlainScalarStyle);
            emitter.emit_sequence_end_event();
            emitter.emit_document_end_event(false);
            emitter.emit_stream_end_event();
            emitter.flush();
        }
        assert_eq!(writer.get_ref(), "[1, 2]\n...\n".as_bytes());
    }

    #[test]
    #[allow(unused_must_use)]
    fn event_emitter_mapping_test() {
        let mut writer = MemWriter::new();
        {
            let mut emitter = YamlEmitter::init(&mut writer);
            emitter.emit_stream_start_event(YamlUtf8Encoding);
            emitter.emit_document_start_event(None, [], true);
            emitter.emit_mapping_start_event(None, None, true, YamlFlowSequenceStyle);
            emitter.emit_scalar_event(None, None, "a", true, false, YamlPlainScalarStyle);
            emitter.emit_scalar_event(None, None, "1", true, false, YamlPlainScalarStyle);
            emitter.emit_scalar_event(None, None, "b", true, false, YamlPlainScalarStyle);
            emitter.emit_scalar_event(None, None, "2", true, false, YamlPlainScalarStyle);
            emitter.emit_mapping_end_event();
            emitter.emit_document_end_event(false);
            emitter.emit_stream_end_event();
            emitter.flush();
        }
        assert_eq!(writer.get_ref(), "{a: 1, b: 2}\n...\n".as_bytes());
    }
}
<commit_msg>Use YamlTagDirective instead of (~str, ~str)<commit_after>use ffi;
use parser::YamlErrorType;
use event::{YamlVersionDirective, YamlTagDirective};

use std::ptr;
use std::cast;
use std::c_vec::CVec;
use std::c_str::CString;
use libc;

pub struct YamlBaseEmitter {
    emitter_mem: ffi::yaml_emitter_t
}

impl YamlBaseEmitter {
    fn new() -> YamlBaseEmitter {
        YamlBaseEmitter {
            emitter_mem: ffi::yaml_emitter_t::new()
        }
    }
}

impl Drop for YamlBaseEmitter {
    fn drop(&mut self) {
        unsafe {
            ffi::yaml_emitter_delete(&mut self.emitter_mem);
        }
    }
}

pub struct YamlEmitter<'r> {
    base_emitter: YamlBaseEmitter,
    writer: &'r mut Writer
}

impl<'r> YamlEmitter<'r> {
    pub fn init<'r>(writer: &'r mut Writer) -> ~YamlEmitter<'r> {
        let mut emitter = ~YamlEmitter {
            base_emitter: YamlBaseEmitter::new(),
            writer: writer
        };

        unsafe {
            if ffi::yaml_emitter_initialize(&mut emitter.base_emitter.emitter_mem) == 0 {
                fail!("failed to initialize yaml_emitter_t");
            }

            ffi::yaml_emitter_set_output(&mut emitter.base_emitter.emitter_mem, handle_writer_cb, cast::transmute(&mut *emitter));
        }

        emitter
    }

    pub fn get_error(&self) -> (YamlErrorType, ~str) {
        let emitter_mem = &self.base_emitter.emitter_mem;
        unsafe {
            (YamlErrorType::conv(emitter_mem.error), CString::new(emitter_mem.problem, false).as_str().unwrap().to_owned())
        }
    }

    pub fn emit_stream_start_event(&mut self, encoding: ffi::YamlEncoding) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_stream_start_event_initialize(&mut event, encoding) == 0 {
                fail!("yaml_stream_start_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_stream_end_event(&mut self) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_stream_end_event_initialize(&mut event) == 0 {
                fail!("yaml_stream_end_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_document_start_event(&mut self,
            version_directive: Option<YamlVersionDirective>,
            tag_directives: &[YamlTagDirective],
            implicit: bool)
        -> Result<(), (YamlErrorType, ~str)>
    {
        let mut event = ffi::yaml_event_t::new();
        let mut vsn_dir = ffi::yaml_version_directive_t { major: 0, minor: 0 };
        let c_vsn_dir = match version_directive {
            Some(directive) => {
                vsn_dir.major = directive.major as libc::c_int;
                vsn_dir.minor = directive.minor as libc::c_int;
                &vsn_dir as *ffi::yaml_version_directive_t
            },
            None => ptr::null()
        };

        let c_strs: ~[(CString, CString)] = tag_directives.iter().map(|tag| {
            (tag.handle.to_c_str(), tag.prefix.to_c_str())
        }).collect();
        let c_tag_dirs: ~[ffi::yaml_tag_directive_t] = c_strs.iter().map(|tuple| {
            ffi::yaml_tag_directive_t {
                handle: tuple.ref0().with_ref(|ptr| {ptr}),
                prefix: tuple.ref1().with_ref(|ptr| {ptr}),
            }
        }).collect();
        let tag_dir_start = c_tag_dirs.as_ptr();
        unsafe {
            let tag_dir_end = tag_dir_start.offset(c_tag_dirs.len() as int);
            let c_implicit = if implicit { 1 } else { 0 };

            if ffi::yaml_document_start_event_initialize(&mut event, c_vsn_dir, tag_dir_start, tag_dir_end, c_implicit) == 0 {
                fail!("yaml_document_start_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_document_end_event(&mut self, implicit: bool) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        let c_implicit = if implicit { 1 } else { 0 };
        unsafe {
            if ffi::yaml_document_end_event_initialize(&mut event, c_implicit) == 0 {
                fail!("yaml_stream_end_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_alias_event(&mut self, anchor: &str) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        let c_anchor = anchor.to_c_str();

        unsafe {
            c_anchor.with_ref(|ptr| {
                if ffi::yaml_alias_event_initialize(&mut event, ptr as *ffi::yaml_char_t) != 0 {
                    fail!("yaml_stream_end_event_initialize failed!")
                }
            });

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_scalar_event(&mut self, anchor: Option<&str>, tag: Option<&str>,
        value: &str, plain_implicit: bool, quoted_implicit: bool,
        style: ffi::YamlScalarStyle) -> Result<(), (YamlErrorType, ~str)>
    {
        let c_anchor = anchor.map(|s| { s.to_c_str() });
        let anchor_ptr = match c_anchor {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_tag = tag.map(|s| { s.to_c_str() });
        let tag_ptr = match c_tag {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_plain_implicit = if plain_implicit { 1 } else { 0 };
        let c_quoted_implicit = if quoted_implicit { 1 } else { 0 };

        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_scalar_event_initialize(&mut event,
                    anchor_ptr as *ffi::yaml_char_t, tag_ptr as *ffi::yaml_char_t,
                    value.as_ptr(), value.len() as libc::c_int,
                    c_plain_implicit, c_quoted_implicit,
                    style) == 0
            {
                fail!("yaml_scalar_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_sequence_start_event(&mut self, anchor: Option<&str>, tag: Option<&str>, implicit: bool,
        style: ffi::YamlSequenceStyle) -> Result<(), (YamlErrorType, ~str)>
    {
        let c_anchor = anchor.map(|s| { s.to_c_str() });
        let anchor_ptr = match c_anchor {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_tag = tag.map(|s| { s.to_c_str() });
        let tag_ptr = match c_tag {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_implicit = if implicit { 1 } else { 0 };

        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_sequence_start_event_initialize(&mut event,
                    anchor_ptr as *ffi::yaml_char_t, tag_ptr as *ffi::yaml_char_t,
                    c_implicit, style) == 0
            {
                fail!("yaml_sequence_start_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_sequence_end_event(&mut self) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_sequence_end_event_initialize(&mut event) == 0 {
                fail!("yaml_sequence_end_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_mapping_start_event(&mut self, anchor: Option<&str>, tag: Option<&str>, implicit: bool,
        style: ffi::YamlSequenceStyle) -> Result<(), (YamlErrorType, ~str)>
    {
        let c_anchor = anchor.map(|s| { s.to_c_str() });
        let anchor_ptr = match c_anchor {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_tag = tag.map(|s| { s.to_c_str() });
        let tag_ptr = match c_tag {
            Some(s) => s.with_ref(|ptr| { ptr }),
            None => ptr::null()
        };
        let c_implicit = if implicit { 1 } else { 0 };

        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_mapping_start_event_initialize(&mut event,
                    anchor_ptr as *ffi::yaml_char_t, tag_ptr as *ffi::yaml_char_t,
                    c_implicit, style) == 0
            {
                fail!("yaml_mapping_start_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn emit_mapping_end_event(&mut self) -> Result<(), (YamlErrorType, ~str)> {
        let mut event = ffi::yaml_event_t::new();
        unsafe {
            if ffi::yaml_mapping_end_event_initialize(&mut event) == 0 {
                fail!("yaml_mapping_end_event_initialize failed!");
            }

            if ffi::yaml_emitter_emit(&mut self.base_emitter.emitter_mem, &mut event) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }

    pub fn flush(&mut self) -> Result<(), (YamlErrorType, ~str)> {
        unsafe {
            if ffi::yaml_emitter_flush(&mut self.base_emitter.emitter_mem) != 0 {
                Ok(())
            } else {
                Err(self.get_error())
            }
        }
    }
}

extern fn handle_writer_cb(data: *mut YamlEmitter, buffer: *u8, size: libc::size_t) -> libc::c_int {
    unsafe {
        let buf = CVec::new(buffer as *mut u8, size as uint);
        let emitter = &mut *data;
        match emitter.writer.write(buf.as_slice()) {
            Ok(()) => 1,
            Err(_) => 0
        }
    }
}

#[cfg(test)]
mod test {
    use std::io::MemWriter;
    use emitter::YamlEmitter;
    use ffi::{YamlUtf8Encoding, YamlPlainScalarStyle, YamlFlowSequenceStyle};

    #[test]
    #[allow(unused_must_use)]
    fn event_emitter_sequence_test() {
        let mut writer = MemWriter::new();
        {
            let mut emitter = YamlEmitter::init(&mut writer);
            emitter.emit_stream_start_event(YamlUtf8Encoding);
            emitter.emit_document_start_event(None, [], true);
            emitter.emit_sequence_start_event(None, None, true, YamlFlowSequenceStyle);
            emitter.emit_scalar_event(None, None, "1", true, false, YamlPlainScalarStyle);
            emitter.emit_scalar_event(None, None, "2", true, false, YamlPlainScalarStyle);
            emitter.emit_sequence_end_event();
            emitter.emit_document_end_event(false);
            emitter.emit_stream_end_event();
            emitter.flush();
        }
        assert_eq!(writer.get_ref(), "[1, 2]\n...\n".as_bytes());
    }

    #[test]
    #[allow(unused_must_use)]
    fn event_emitter_mapping_test() {
        let mut writer = MemWriter::new();
        {
            let mut emitter = YamlEmitter::init(&mut writer);
            emitter.emit_stream_start_event(YamlUtf8Encoding);
            emitter.emit_document_start_event(None, [], true);
            emitter.emit_mapping_start_event(None, None, true, YamlFlowSequenceStyle);
            emitter.emit_scalar_event(None, None, "a", true, false, YamlPlainScalarStyle);
            emitter.emit_scalar_event(None, None, "1", true, false, YamlPlainScalarStyle);
            emitter.emit_scalar_event(None, None, "b", true, false, YamlPlainScalarStyle);
            emitter.emit_scalar_event(None, None, "2", true, false, YamlPlainScalarStyle);
            emitter.emit_mapping_end_event();
            emitter.emit_document_end_event(false);
            emitter.emit_stream_end_event();
            emitter.flush();
        }
        assert_eq!(writer.get_ref(), "{a: 1, b: 2}\n...\n".as_bytes());
    }
}
<|endoftext|>