<commit_before>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::slice;
use std::ffi;
use std::str;

use htslib;

pub type SampleSubset = Vec<i32>;

custom_derive! {
    /// A newtype for IDs from BCF headers.
    #[derive(
        NewtypeFrom,
        NewtypeDeref,
        NewtypeAdd(*),
        NewtypeSub(*),
        NewtypeMul(*),
        NewtypeDiv(*),
        PartialEq,
        PartialOrd,
        Copy,
        Clone,
        Debug,
        Default
    )]
    pub struct Id(pub u32);
}

/// A BCF header.
#[derive(Debug)]
pub struct Header {
    pub inner: *mut htslib::bcf_hdr_t,
    pub subset: Option<SampleSubset>,
}

impl Header {
    /// Create a new header.
    pub fn new() -> Self {
        Header {
            inner: unsafe { htslib::bcf_hdr_init(ffi::CString::new(&b"w"[..]).unwrap().as_ptr()) },
            subset: None,
        }
    }

    pub fn with_template(header: &HeaderView) -> Self {
        Header { inner: unsafe { htslib::bcf_hdr_dup(header.inner) }, subset: None }
    }

    pub fn subset_template(header: &HeaderView, samples: &[&[u8]]) -> Result<Self, SubsetError> {
        let mut imap = vec![0; samples.len()];
        let names: Vec<_> = samples.iter().map(|&s| ffi::CString::new(s).unwrap()).collect();
        let name_pointers: Vec<_> = names.iter().map(|s| s.as_ptr() as *mut i8).collect();
        let inner = unsafe {
            htslib::bcf_hdr_subset(
                header.inner,
                samples.len() as i32,
                name_pointers.as_ptr() as *const *const i8,
                imap.as_mut_ptr() as *mut i32
            )
        };
        if inner.is_null() {
            Err(SubsetError::DuplicateSampleName)
        }
        else {
            Ok(Header { inner: inner, subset: Some(imap) })
        }
    }

    pub fn push_sample(&mut self, sample: &[u8]) -> &mut Self {
        unsafe { htslib::bcf_hdr_add_sample(self.inner, ffi::CString::new(sample).unwrap().as_ptr()) };
        self
    }

    /// Add a record to the header.
    pub fn push_record(&mut self, record: &[u8]) -> &mut Self {
        unsafe { htslib::bcf_hdr_append(self.inner, ffi::CString::new(record).unwrap().as_ptr()) };
        self
    }

    pub fn remove_info(&mut self, tag: &[u8]) -> &mut Self {
        unsafe {
            htslib::bcf_hdr_remove(self.inner, htslib::BCF_HL_INFO as i32, tag.as_ptr() as *const i8);
        }
        self
    }

    pub fn remove_format(&mut self, tag: &[u8]) -> &mut Self {
        unsafe {
            htslib::bcf_hdr_remove(
                self.inner,
                htslib::BCF_HL_FMT as i32,
                tag.as_ptr() as *const i8
            );
        }
        self
    }
}

impl Drop for Header {
    fn drop(&mut self) {
        unsafe { htslib::bcf_hdr_destroy(self.inner) };
    }
}

#[derive(Debug)]
pub struct HeaderView {
    pub inner: *mut htslib::bcf_hdr_t,
}

impl HeaderView {
    pub fn new(inner: *mut htslib::bcf_hdr_t) -> Self {
        HeaderView { inner: inner }
    }

    #[inline]
    fn inner(&self) -> htslib::bcf_hdr_t {
        unsafe { (*self.inner) }
    }

    pub fn sample_count(&self) -> u32 {
        self.inner().n[htslib::BCF_DT_SAMPLE as usize] as u32
    }

    pub fn samples(&self) -> Vec<&[u8]> {
        let names = unsafe { slice::from_raw_parts(self.inner().samples, self.sample_count() as usize) };
        names.iter().map(|name| unsafe { ffi::CStr::from_ptr(*name).to_bytes() }).collect()
    }

    pub fn rid2name(&self, rid: u32) -> &[u8] {
        unsafe {
            let dict = self.inner().id[htslib::BCF_DT_CTG as usize];
            let ptr = (*dict.offset(rid as isize)).key;
            ffi::CStr::from_ptr(ptr).to_bytes()
        }
    }

    pub fn name2rid(&self, name: &[u8]) -> Result<u32, RidError> {
        unsafe {
            match htslib::bcf_hdr_id2int(
                self.inner,
                htslib::BCF_DT_CTG as i32,
                ffi::CString::new(name).unwrap().as_ptr() as *mut i8
            ) {
                -1 => Err(RidError::UnknownSequence(str::from_utf8(name).unwrap().to_owned())),
                i  => Ok(i as u32)
            }
        }
    }

    pub fn info_type(&self, tag: &[u8]) -> Result<(TagType, TagLength), TagTypeError> {
        self.tag_type(tag, htslib::BCF_HL_INFO)
    }

    pub fn format_type(&self, tag: &[u8]) -> Result<(TagType, TagLength), TagTypeError> {
        self.tag_type(tag, htslib::BCF_HL_FMT)
    }

    fn tag_type(&self, tag: &[u8], hdr_type: ::libc::c_uint) -> Result<(TagType, TagLength), TagTypeError> {
        let (_type, length) = unsafe {
            let id = htslib::bcf_hdr_id2int(
                self.inner,
                htslib::BCF_DT_ID as i32,
                ffi::CString::new(tag).unwrap().as_ptr() as *mut i8
            );
            if id < 0 {
                return Err(TagTypeError::UndefinedTag(str::from_utf8(tag).unwrap().to_owned()));
            }
            let n = (*self.inner).n[htslib::BCF_DT_ID as usize] as usize;
            let entry = slice::from_raw_parts((*self.inner).id[htslib::BCF_DT_ID as usize], n);
            let d = (*entry[id as usize].val).info[hdr_type as usize];
            (d >> 4 & 0xf, d >> 8 & 0xf)
        };
        let _type = match _type as ::libc::c_uint {
            htslib::BCF_HT_FLAG => TagType::Flag,
            htslib::BCF_HT_INT => TagType::Integer,
            htslib::BCF_HT_REAL => TagType::Float,
            htslib::BCF_HT_STR => TagType::String,
            _ => return Err(TagTypeError::UnexpectedTagType)
        };
        let length = match length as ::libc::c_uint {
            htslib::BCF_VL_FIXED => TagLength::Fixed,
            htslib::BCF_VL_VAR => TagLength::Variable,
            htslib::BCF_VL_A => TagLength::AltAlleles,
            htslib::BCF_VL_R => TagLength::Alleles,
            htslib::BCF_VL_G => TagLength::Genotypes,
            _ => return Err(TagTypeError::UnexpectedTagType)
        };

        Ok((_type, length))
    }

    /// Convert string ID (e.g., for a `FILTER` value) to its numeric identifier.
    pub fn name_to_id(&self, id: &[u8]) -> Result<Id, IdError> {
        unsafe {
            match htslib::bcf_hdr_id2int(
                self.inner,
                htslib::BCF_DT_ID as i32,
                ffi::CString::new(id).unwrap().as_ptr() as *const i8
            ) {
                -1 => Err(IdError::UnknownID(str::from_utf8(id).unwrap().to_owned())),
                i => Ok(Id(i as u32)),
            }
        }
    }

    /// Convert integer representing an identifier (e.g., a `FILTER` value) to its string
    /// name.bam
    pub fn id_to_name(&self, id: Id) -> Vec<u8> {
        let key = unsafe { ffi::CStr::from_ptr(
            (*(*self.inner).id[htslib::BCF_DT_ID as usize].offset(*id as isize)).key) };
        key.to_bytes().to_vec()
    }

    /// Convert string sample name to its numeric identifier.
    pub fn sample_to_id(&self, id: &[u8]) -> Result<Id, SampleError> {
        unsafe {
            match htslib::bcf_hdr_id2int(
                self.inner,
                htslib::BCF_DT_SAMPLE as i32,
                ffi::CString::new(id).unwrap().as_ptr() as *const i8
            ) {
                -1 => Err(SampleError::UnknownSample(str::from_utf8(id).unwrap().to_owned())),
                i => Ok(Id(i as u32)),
            }
        }
    }

    /// Convert integer representing an contig to its name.
    pub fn id_to_sample(&self, id: Id) -> Vec<u8> {
        let key = unsafe { ffi::CStr::from_ptr(
            (*(*self.inner).id[htslib::BCF_DT_SAMPLE as usize].offset(*id as isize)).key) };
        key.to_bytes().to_vec()
    }
}

impl Clone for HeaderView {
    fn clone(&self) -> Self {
        HeaderView {
            inner: unsafe { htslib::bcf_hdr_dup(self.inner) }
        }
    }
}

impl Drop for HeaderView {
    fn drop(&mut self) {
        unsafe {
            htslib::bcf_hdr_destroy(self.inner);
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TagType {
    Flag,
    Integer,
    Float,
    String
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TagLength {
    Fixed,
    AltAlleles,
    Alleles,
    Genotypes,
    Variable
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum RidError {
        UnknownSequence(name: String) {
            description("unknown sequence")
            display("sequence {} not found in header", name)
        }
    }
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum IdError {
        UnknownID(name: String) {
            description("unknown ID")
            display("ID {} not found in header", name)
        }
    }
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum SampleError {
        UnknownSample(name: String) {
            description("unknown sample")
            display("sample {} not found in header", name)
        }
    }
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum SubsetError {
        DuplicateSampleName {
            description("duplicate sample name when subsetting header")
        }
    }
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum TagTypeError {
        UnexpectedTagType {
            description("unexpected tag type in header")
        }
        UndefinedTag(name: String) {
            description("undefined tag")
            display("tag {} is undefined in header", name)
        }
    }
}
<commit_msg>Update header.rs (#75)<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::slice;
use std::ffi;
use std::str;

use htslib;

pub type SampleSubset = Vec<i32>;

custom_derive! {
    /// A newtype for IDs from BCF headers.
    #[derive(
        NewtypeFrom,
        NewtypeDeref,
        PartialEq,
        PartialOrd,
        Eq,
        Ord,
        Copy,
        Clone,
        Debug
    )]
    pub struct Id(pub u32);
}

/// A BCF header.
#[derive(Debug)]
pub struct Header {
    pub inner: *mut htslib::bcf_hdr_t,
    pub subset: Option<SampleSubset>,
}

impl Header {
    /// Create a new header.
    pub fn new() -> Self {
        Header {
            inner: unsafe { htslib::bcf_hdr_init(ffi::CString::new(&b"w"[..]).unwrap().as_ptr()) },
            subset: None,
        }
    }

    pub fn with_template(header: &HeaderView) -> Self {
        Header { inner: unsafe { htslib::bcf_hdr_dup(header.inner) }, subset: None }
    }

    pub fn subset_template(header: &HeaderView, samples: &[&[u8]]) -> Result<Self, SubsetError> {
        let mut imap = vec![0; samples.len()];
        let names: Vec<_> = samples.iter().map(|&s| ffi::CString::new(s).unwrap()).collect();
        let name_pointers: Vec<_> = names.iter().map(|s| s.as_ptr() as *mut i8).collect();
        let inner = unsafe {
            htslib::bcf_hdr_subset(
                header.inner,
                samples.len() as i32,
                name_pointers.as_ptr() as *const *const i8,
                imap.as_mut_ptr() as *mut i32
            )
        };
        if inner.is_null() {
            Err(SubsetError::DuplicateSampleName)
        }
        else {
            Ok(Header { inner: inner, subset: Some(imap) })
        }
    }

    pub fn push_sample(&mut self, sample: &[u8]) -> &mut Self {
        unsafe { htslib::bcf_hdr_add_sample(self.inner, ffi::CString::new(sample).unwrap().as_ptr()) };
        self
    }

    /// Add a record to the header.
    pub fn push_record(&mut self, record: &[u8]) -> &mut Self {
        unsafe { htslib::bcf_hdr_append(self.inner, ffi::CString::new(record).unwrap().as_ptr()) };
        self
    }

    pub fn remove_info(&mut self, tag: &[u8]) -> &mut Self {
        unsafe {
            htslib::bcf_hdr_remove(self.inner, htslib::BCF_HL_INFO as i32, tag.as_ptr() as *const i8);
        }
        self
    }

    pub fn remove_format(&mut self, tag: &[u8]) -> &mut Self {
        unsafe {
            htslib::bcf_hdr_remove(
                self.inner,
                htslib::BCF_HL_FMT as i32,
                tag.as_ptr() as *const i8
            );
        }
        self
    }
}

impl Drop for Header {
    fn drop(&mut self) {
        unsafe { htslib::bcf_hdr_destroy(self.inner) };
    }
}

#[derive(Debug)]
pub struct HeaderView {
    pub inner: *mut htslib::bcf_hdr_t,
}

impl HeaderView {
    pub fn new(inner: *mut htslib::bcf_hdr_t) -> Self {
        HeaderView { inner: inner }
    }

    #[inline]
    fn inner(&self) -> htslib::bcf_hdr_t {
        unsafe { (*self.inner) }
    }

    pub fn sample_count(&self) -> u32 {
        self.inner().n[htslib::BCF_DT_SAMPLE as usize] as u32
    }

    pub fn samples(&self) -> Vec<&[u8]> {
        let names = unsafe { slice::from_raw_parts(self.inner().samples, self.sample_count() as usize) };
        names.iter().map(|name| unsafe { ffi::CStr::from_ptr(*name).to_bytes() }).collect()
    }

    pub fn rid2name(&self, rid: u32) -> &[u8] {
        unsafe {
            let dict = self.inner().id[htslib::BCF_DT_CTG as usize];
            let ptr = (*dict.offset(rid as isize)).key;
            ffi::CStr::from_ptr(ptr).to_bytes()
        }
    }

    pub fn name2rid(&self, name: &[u8]) -> Result<u32, RidError> {
        unsafe {
            match htslib::bcf_hdr_id2int(
                self.inner,
                htslib::BCF_DT_CTG as i32,
                ffi::CString::new(name).unwrap().as_ptr() as *mut i8
            ) {
                -1 => Err(RidError::UnknownSequence(str::from_utf8(name).unwrap().to_owned())),
                i  => Ok(i as u32)
            }
        }
    }

    pub fn info_type(&self, tag: &[u8]) -> Result<(TagType, TagLength), TagTypeError> {
        self.tag_type(tag, htslib::BCF_HL_INFO)
    }

    pub fn format_type(&self, tag: &[u8]) -> Result<(TagType, TagLength), TagTypeError> {
        self.tag_type(tag, htslib::BCF_HL_FMT)
    }

    fn tag_type(&self, tag: &[u8], hdr_type: ::libc::c_uint) -> Result<(TagType, TagLength), TagTypeError> {
        let (_type, length) = unsafe {
            let id = htslib::bcf_hdr_id2int(
                self.inner,
                htslib::BCF_DT_ID as i32,
                ffi::CString::new(tag).unwrap().as_ptr() as *mut i8
            );
            if id < 0 {
                return Err(TagTypeError::UndefinedTag(str::from_utf8(tag).unwrap().to_owned()));
            }
            let n = (*self.inner).n[htslib::BCF_DT_ID as usize] as usize;
            let entry = slice::from_raw_parts((*self.inner).id[htslib::BCF_DT_ID as usize], n);
            let d = (*entry[id as usize].val).info[hdr_type as usize];
            (d >> 4 & 0xf, d >> 8 & 0xf)
        };
        let _type = match _type as ::libc::c_uint {
            htslib::BCF_HT_FLAG => TagType::Flag,
            htslib::BCF_HT_INT => TagType::Integer,
            htslib::BCF_HT_REAL => TagType::Float,
            htslib::BCF_HT_STR => TagType::String,
            _ => return Err(TagTypeError::UnexpectedTagType)
        };
        let length = match length as ::libc::c_uint {
            htslib::BCF_VL_FIXED => TagLength::Fixed,
            htslib::BCF_VL_VAR => TagLength::Variable,
            htslib::BCF_VL_A => TagLength::AltAlleles,
            htslib::BCF_VL_R => TagLength::Alleles,
            htslib::BCF_VL_G => TagLength::Genotypes,
            _ => return Err(TagTypeError::UnexpectedTagType)
        };

        Ok((_type, length))
    }

    /// Convert string ID (e.g., for a `FILTER` value) to its numeric identifier.
    pub fn name_to_id(&self, id: &[u8]) -> Result<Id, IdError> {
        unsafe {
            match htslib::bcf_hdr_id2int(
                self.inner,
                htslib::BCF_DT_ID as i32,
                ffi::CString::new(id).unwrap().as_ptr() as *const i8
            ) {
                -1 => Err(IdError::UnknownID(str::from_utf8(id).unwrap().to_owned())),
                i => Ok(Id(i as u32)),
            }
        }
    }

    /// Convert integer representing an identifier (e.g., a `FILTER` value) to its string
    /// name.bam
    pub fn id_to_name(&self, id: Id) -> Vec<u8> {
        let key = unsafe { ffi::CStr::from_ptr(
            (*(*self.inner).id[htslib::BCF_DT_ID as usize].offset(*id as isize)).key) };
        key.to_bytes().to_vec()
    }

    /// Convert string sample name to its numeric identifier.
    pub fn sample_to_id(&self, id: &[u8]) -> Result<Id, SampleError> {
        unsafe {
            match htslib::bcf_hdr_id2int(
                self.inner,
                htslib::BCF_DT_SAMPLE as i32,
                ffi::CString::new(id).unwrap().as_ptr() as *const i8
            ) {
                -1 => Err(SampleError::UnknownSample(str::from_utf8(id).unwrap().to_owned())),
                i => Ok(Id(i as u32)),
            }
        }
    }

    /// Convert integer representing an contig to its name.
    pub fn id_to_sample(&self, id: Id) -> Vec<u8> {
        let key = unsafe { ffi::CStr::from_ptr(
            (*(*self.inner).id[htslib::BCF_DT_SAMPLE as usize].offset(*id as isize)).key) };
        key.to_bytes().to_vec()
    }
}

impl Clone for HeaderView {
    fn clone(&self) -> Self {
        HeaderView {
            inner: unsafe { htslib::bcf_hdr_dup(self.inner) }
        }
    }
}

impl Drop for HeaderView {
    fn drop(&mut self) {
        unsafe {
            htslib::bcf_hdr_destroy(self.inner);
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TagType {
    Flag,
    Integer,
    Float,
    String
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TagLength {
    Fixed,
    AltAlleles,
    Alleles,
    Genotypes,
    Variable
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum RidError {
        UnknownSequence(name: String) {
            description("unknown sequence")
            display("sequence {} not found in header", name)
        }
    }
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum IdError {
        UnknownID(name: String) {
            description("unknown ID")
            display("ID {} not found in header", name)
        }
    }
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum SampleError {
        UnknownSample(name: String) {
            description("unknown sample")
            display("sample {} not found in header", name)
        }
    }
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum SubsetError {
        DuplicateSampleName {
            description("duplicate sample name when subsetting header")
        }
    }
}

quick_error! {
    #[derive(Debug, Clone)]
    pub enum TagTypeError {
        UnexpectedTagType {
            description("unexpected tag type in header")
        }
        UndefinedTag(name: String) {
            description("undefined tag")
            display("tag {} is undefined in header", name)
        }
    }
}
<|endoftext|>


<commit_before>//! The main user interface from which the user will define systems to create.
//! They can also access and modify the `DataBase` of components to use in their
//! systems.
//!
//! This is implemented as a *very* basic text interface. This could be improved
//! greatly by knowing more about human interface design. In particular the systems
//! for creating `SheetConfEntry` and `SystemDefinition` are in need of improvement.

#[macro_use]
mod utils;
mod edit_database;
mod define_components;

use super::Config;
use database::AvailableComponents;
use error::{GrafenCliError, Result};
use output;
use ui::utils::{CommandList, CommandParser};

use grafen::system::{Component, Coord};
use std::error::Error;

#[derive(Debug)]
/// A `Component` which has been constructed along with a descriptive string.
pub struct ConstructedComponent {
    /// Description of the component.
    description: String,
    /// The component.
    pub component: Component,
}

#[derive(Debug)]
/// All `ConstructedComponent`s and `ComponentDefinition`s are kept in this
/// `System` which keeps track of those and other meta information.
pub struct System {
    /// System box size. Either set by the user or calculated from the components.
    pub box_size: Option<Coord>,
    /// Component definitions.
    pub definitions: Vec<AvailableComponents>,
    /// Components and their descriptions belonging to the system.
    pub constructed: Vec<ConstructedComponent>,
}

impl System {
    /// Calculate the box size from the system components. The largest extension along each
    /// direction is used for the final box size.
    ///
    /// Returns None if no components exist in the system.
    pub fn calc_box_size(&self) -> Option<Coord> {
        if self.constructed.is_empty() {
            return None;
        }

        let box_size = self.constructed.iter().fold(
            Coord::new(0.0, 0.0, 0.0), |acc, conf| {
                let (x1, y1, z1) = acc.to_tuple();
                let (x2, y2, z2) = conf.component.box_size.to_tuple();

                Coord::new(x1.max(x2), y1.max(y2), z1.max(z2))
            }
        );

        Some(box_size)
    }

    /// Calculate the number of atoms in the system.
    pub fn num_atoms(&self) -> usize {
        self.constructed.iter().map(|conf| {
            conf.component.residue_base.atoms.len() * conf.component.residue_coords.len()
        }).sum()
    }
}

#[derive(Clone, Copy, Debug)]
/// User commands for defining the system.
enum Command {
    DefineComponents,
    ConstructComponents,
    EditDatabase,
    SaveSystem,
    Quit,
}

/// Loop over a menu in which the user can define the system which will be created, etc.
///
/// The idea of this interface is relatively simple:
///
/// 1. The user reads or constructs a `DataBase` of residues (`ResidueBase`).
/// 2. Then constructs definitions of substrates or objects to be created (can also be
///    saved to and read from the `DataBase`).
/// 3. Processes these definitions to create the actual components which make up the system.
/// 4. Modifies or transforms these components by copying, translating, rotating etc.
/// 5. Finally saves the full system to disk.
///
/// Modifying the `DataBase`, setting the definitions `SheetConf` and editing the
/// `Component`s each require a separate menu, accessed from this super menu.
/// This menu should also allow the user to save the system to disk, set its name
/// and file path and any other possible options.
pub fn user_menu(mut config: &mut Config) -> Result<()> {
    let mut system = System { box_size: None, constructed: vec![], definitions: vec![] };

    let command_list: CommandList<Command> = vec![
        ("de", Command::DefineComponents, "Define the list of components to construct"),
        ("co", Command::ConstructComponents, "Construct components from all definitions"),
        ("db", Command::EditDatabase, "Edit the database of residue and object definitions"),
        ("save", Command::SaveSystem, "Save the constructed components to disk as a system"),
        ("quit", Command::Quit, "Quit the program"),
    ];
    let commands = CommandParser::from_list(command_list);

    loop {
        define_components::describe_system_definitions(&system.definitions);
        describe_created_components(&system.constructed);

        commands.print_menu();
        let input = utils::get_input_string("Selection")?;
        println!("");

        if let Some((cmd, _)) = commands.get_selection_and_tail(&input) {
            match cmd {
                Command::DefineComponents => {
                    match define_components::user_menu(&config.database, &mut system.definitions) {
                        Ok(_) => println!("Finished editing list of definitions."),
                        Err(err) => println!("Could not create definition: {}", err.description()),
                    }
                },
                Command::ConstructComponents => {
                    match construct_components(&mut system) {
                        Ok(_) => println!("Successfully constructed all components."),
                        Err(err) => println!("Could not construct all components: {}", err.description()),
                    }
                },
                Command::EditDatabase => {
                    match edit_database::user_menu(&mut config.database) {
                        Ok(msg) => println!("{}", msg),
                        Err(err) => println!("Error when editing database: {}", err.description()),
                    }
                },
                Command::SaveSystem => {
                    match output::write_gromos(&system, &config) {
                        Ok(()) => println!("Saved system to disk."),
                        Err(msg) => println!("Error when saving system: {}", msg),
                    }
                },
                Command::Quit => {
                    return Err(GrafenCliError::QuitWithoutSaving);
                },
            }
        } else {
            println!("Not a valid selection.");
        }

        println!("");
    }
}

fn construct_components(system: &mut System) -> Result<()> {
    let ref mut definitions = system.definitions;
    let ref mut components = system.constructed;

    for def in definitions.drain(..) {
        let description = def.describe_long();
        let component = def.into_component()?;
        components.push(ConstructedComponent{ description, component });
    }

    Ok(())
}

fn describe_created_components(constructed: &[ConstructedComponent]) {
    if constructed.is_empty() {
        println!("(No components have been created)");
    } else {
        println!("Constructed system components:");
        for (i, conf) in constructed.iter().enumerate() {
            println!("{}. {}", i, &conf.description);
        }
    }

    println!("");
}

#[cfg(test)]
mod tests {
    use super::*;
    use grafen::system::{Atom, ResidueBase};

    /// Setup a system of three components and in total seven atoms
    fn setup_system() -> System {
        let base_one = ResidueBase {
            code: "R1".to_string(),
            atoms: vec![
                Atom { code: "A1".to_string(), position: Coord::new(0.0, 1.0, 2.0) },
                Atom { code: "A2".to_string(), position: Coord::new(0.0, 2.0, 1.0) }
                ],
            };
        let base_two = ResidueBase {
            code: "R2".to_string(),
            atoms: vec![
                Atom { code: "B".to_string(), position: Coord::new(0.0, 1.0, 2.0) },
                ],
            };

        System {
            box_size: None,
            definitions: vec![],
            constructed: vec![
                ConstructedComponent {
                    description: "None".to_string(),
                    component: Component {
                        // Largest along x
                        box_size: Coord::new(10.0, 1.0, 0.0),
                        origin: Coord::new(0.0, 0.0, 0.0),
                        residue_base: base_one.clone(),
                        // 1 residue * 2 atoms per residue
                        residue_coords: vec![Coord::new(0.0, 0.0, 0.0)],
                    },
                },
                ConstructedComponent {
                    description: "None".to_string(),
                    component: Component {
                        // Largest along z
                        box_size: Coord::new(1.0, 1.0, 7.0),
                        origin: Coord::new(0.0, 0.0, 0.0),
                        residue_base: base_two.clone(),
                        // 1 * 1 atoms
                        residue_coords: vec![Coord::new(0.0, 0.0, 0.0)],
                    },
                },
                ConstructedComponent {
                    description: "None".to_string(),
                    component: Component {
                        // Largest along y
                        box_size: Coord::new(1.0, 5.0, 0.0),
                        origin: Coord::new(0.0, 0.0, 0.0),
                        residue_base: base_one.clone(),
                        // 2 * 2 atoms
                        residue_coords: vec![Coord::new(0.0, 0.0, 0.0), Coord::new(1.0, 0.0, 0.0)],
                    },
                },
            ]
        }
    }

    #[test]
    fn all_atoms_are_counted_in_system() {
        let system = setup_system();

        assert_eq!(7, system.num_atoms());
    }

    #[test]
    fn box_size_is_largest_in_each_direction() {
        let system = setup_system();
        assert_eq!(Some(Coord::new(10.0, 5.0, 7.0)), system.calc_box_size());
    }

    #[test]
    fn box_size_is_none_if_no_components() {
        let system = System {
            box_size: None,
            definitions: vec![],
            constructed: vec![],
        };

        assert!(system.calc_box_size().is_none());
    }
}
<commit_msg>Use only the `command_parser` macro for UI<commit_after>//! The main user interface from which the user will define systems to create.
//! They can also access and modify the `DataBase` of components to use in their
//! systems.
//!
//! This is implemented as a *very* basic text interface. This could be improved
//! greatly by knowing more about human interface design. In particular the systems
//! for creating `SheetConfEntry` and `SystemDefinition` are in need of improvement.

#[macro_use]
mod utils;
mod edit_database;
mod define_components;

use super::Config;
use database::AvailableComponents;
use error::{GrafenCliError, Result};
use output;
use ui::utils::CommandParser;

use grafen::system::{Component, Coord};
use std::error::Error;

#[derive(Debug)]
/// A `Component` which has been constructed along with a descriptive string.
pub struct ConstructedComponent {
    /// Description of the component.
    description: String,
    /// The component.
    pub component: Component,
}

#[derive(Debug)]
/// All `ConstructedComponent`s and `ComponentDefinition`s are kept in this
/// `System` which keeps track of those and other meta information.
pub struct System {
    /// System box size. Either set by the user or calculated from the components.
    pub box_size: Option<Coord>,
    /// Component definitions.
    pub definitions: Vec<AvailableComponents>,
    /// Components and their descriptions belonging to the system.
    pub constructed: Vec<ConstructedComponent>,
}

impl System {
    /// Calculate the box size from the system components. The largest extension along each
    /// direction is used for the final box size.
    ///
    /// Returns None if no components exist in the system.
    pub fn calc_box_size(&self) -> Option<Coord> {
        if self.constructed.is_empty() {
            return None;
        }

        let box_size = self.constructed.iter().fold(
            Coord::new(0.0, 0.0, 0.0), |acc, conf| {
                let (x1, y1, z1) = acc.to_tuple();
                let (x2, y2, z2) = conf.component.box_size.to_tuple();

                Coord::new(x1.max(x2), y1.max(y2), z1.max(z2))
            }
        );

        Some(box_size)
    }

    /// Calculate the number of atoms in the system.
    pub fn num_atoms(&self) -> usize {
        self.constructed.iter().map(|conf| {
            conf.component.residue_base.atoms.len() * conf.component.residue_coords.len()
        }).sum()
    }
}

#[derive(Clone, Copy, Debug)]
/// User commands for defining the system.
enum Command {
    DefineComponents,
    ConstructComponents,
    EditDatabase,
    SaveSystem,
    Quit,
}

/// Loop over a menu in which the user can define the system which will be created, etc.
///
/// The idea of this interface is relatively simple:
///
/// 1. The user reads or constructs a `DataBase` of residues (`ResidueBase`).
/// 2. Then constructs definitions of substrates or objects to be created (can also be
///    saved to and read from the `DataBase`).
/// 3. Processes these definitions to create the actual components which make up the system.
/// 4. Modifies or transforms these components by copying, translating, rotating etc.
/// 5. Finally saves the full system to disk.
///
/// Modifying the `DataBase`, setting the definitions `SheetConf` and editing the
/// `Component`s each require a separate menu, accessed from this super menu.
/// This menu should also allow the user to save the system to disk, set its name
/// and file path and any other possible options.
pub fn user_menu(mut config: &mut Config) -> Result<()> {
    let mut system = System { box_size: None, constructed: vec![], definitions: vec![] };

    let commands = command_parser!(
        ("de", Command::DefineComponents, "Define the list of components to construct"),
        ("co", Command::ConstructComponents, "Construct components from all definitions"),
        ("db", Command::EditDatabase, "Edit the database of residue and object definitions"),
        ("save", Command::SaveSystem, "Save the constructed components to disk as a system"),
        ("quit", Command::Quit, "Quit the program")
    );

    loop {
        define_components::describe_system_definitions(&system.definitions);
        describe_created_components(&system.constructed);

        commands.print_menu();
        let input = utils::get_input_string("Selection")?;
        println!("");

        if let Some((cmd, _)) = commands.get_selection_and_tail(&input) {
            match cmd {
                Command::DefineComponents => {
                    match define_components::user_menu(&config.database, &mut system.definitions) {
                        Ok(_) => println!("Finished editing list of definitions."),
                        Err(err) => println!("Could not create definition: {}", err.description()),
                    }
                },
                Command::ConstructComponents => {
                    match construct_components(&mut system) {
                        Ok(_) => println!("Successfully constructed all components."),
                        Err(err) => println!("Could not construct all components: {}", err.description()),
                    }
                },
                Command::EditDatabase => {
                    match edit_database::user_menu(&mut config.database) {
                        Ok(msg) => println!("{}", msg),
                        Err(err) => println!("Error when editing database: {}", err.description()),
                    }
                },
                Command::SaveSystem => {
                    match output::write_gromos(&system, &config) {
                        Ok(()) => println!("Saved system to disk."),
                        Err(msg) => println!("Error when saving system: {}", msg),
                    }
                },
                Command::Quit => {
                    return Err(GrafenCliError::QuitWithoutSaving);
                },
            }
        } else {
            println!("Not a valid selection.");
        }

        println!("");
    }
}

fn construct_components(system: &mut System) -> Result<()> {
    let ref mut definitions = system.definitions;
    let ref mut components = system.constructed;

    for def in definitions.drain(..) {
        let description = def.describe_long();
        let component = def.into_component()?;
        components.push(ConstructedComponent{ description, component });
    }

    Ok(())
}

fn describe_created_components(constructed: &[ConstructedComponent]) {
    if constructed.is_empty() {
        println!("(No components have been created)");
    } else {
        println!("Constructed system components:");
        for (i, conf) in constructed.iter().enumerate() {
            println!("{}. {}", i, &conf.description);
        }
    }

    println!("");
}

#[cfg(test)]
mod tests {
    use super::*;
    use grafen::system::{Atom, ResidueBase};

    /// Setup a system of three components and in total seven atoms
    fn setup_system() -> System {
        let base_one = ResidueBase {
            code: "R1".to_string(),
            atoms: vec![
                Atom { code: "A1".to_string(), position: Coord::new(0.0, 1.0, 2.0) },
                Atom { code: "A2".to_string(), position: Coord::new(0.0, 2.0, 1.0) }
                ],
            };
        let base_two = ResidueBase {
            code: "R2".to_string(),
            atoms: vec![
                Atom { code: "B".to_string(), position: Coord::new(0.0, 1.0, 2.0) },
                ],
            };

        System {
            box_size: None,
            definitions: vec![],
            constructed: vec![
                ConstructedComponent {
                    description: "None".to_string(),
                    component: Component {
                        // Largest along x
                        box_size: Coord::new(10.0, 1.0, 0.0),
                        origin: Coord::new(0.0, 0.0, 0.0),
                        residue_base: base_one.clone(),
                        // 1 residue * 2 atoms per residue
                        residue_coords: vec![Coord::new(0.0, 0.0, 0.0)],
                    },
                },
                ConstructedComponent {
                    description: "None".to_string(),
                    component: Component {
                        // Largest along z
                        box_size: Coord::new(1.0, 1.0, 7.0),
                        origin: Coord::new(0.0, 0.0, 0.0),
                        residue_base: base_two.clone(),
                        // 1 * 1 atoms
                        residue_coords: vec![Coord::new(0.0, 0.0, 0.0)],
                    },
                },
                ConstructedComponent {
                    description: "None".to_string(),
                    component: Component {
                        // Largest along y
                        box_size: Coord::new(1.0, 5.0, 0.0),
                        origin: Coord::new(0.0, 0.0, 0.0),
                        residue_base: base_one.clone(),
                        // 2 * 2 atoms
                        residue_coords: vec![Coord::new(0.0, 0.0, 0.0), Coord::new(1.0, 0.0, 0.0)],
                    },
                },
            ]
        }
    }

    #[test]
    fn all_atoms_are_counted_in_system() {
        let system = setup_system();

        assert_eq!(7, system.num_atoms());
    }

    #[test]
    fn box_size_is_largest_in_each_direction() {
        let system = setup_system();
        assert_eq!(Some(Coord::new(10.0, 5.0, 7.0)), system.calc_box_size());
    }

    #[test]
    fn box_size_is_none_if_no_components() {
        let system = System {
            box_size: None,
            definitions: vec![],
            constructed: vec![],
        };

        assert!(system.calc_box_size().is_none());
    }
}
<|endoftext|>


<commit_before>use std::any::TypeId;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::descriptor::FileDescriptorProto;
use crate::enums::ProtobufEnum;
use crate::enums::ProtobufEnumOrUnknown;
use crate::reflect::find_message_or_enum::find_message_or_enum;
use crate::reflect::find_message_or_enum::MessageOrEnum;
use crate::reflect::ProtobufValue;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone)]
pub struct EnumValueDescriptor {
    proto: &'static EnumValueDescriptorProto,
    protobuf_value: &'static dyn ProtobufValue,
    get_descriptor: &'static dyn GetEnumDescriptor,
}

impl PartialEq for EnumValueDescriptor {
    fn eq(&self, other: &EnumValueDescriptor) -> bool {
        self.enum_descriptor() == other.enum_descriptor() && self.value() == other.value()
    }
}

impl Hash for EnumValueDescriptor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&self.value(), state)
    }
}

fn _assert_send_sync() {
    fn _assert_send_sync<T: Send + Sync>() {}
    _assert_send_sync::<EnumValueDescriptor>();
}

impl fmt::Debug for EnumValueDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumValueDescriptor")
            .field("proto", self.proto)
            .field("value", &"...")
            .finish()
    }
}

impl Copy for EnumValueDescriptor {}

impl EnumValueDescriptor {
    /// Name of enum variant as specified in proto file
    pub fn name(&self) -> &'static str {
        self.proto.get_name()
    }

    /// `i32` value of the enum variant
    pub fn value(&self) -> i32 {
        self.proto.get_number()
    }

    /// Convert to generic `ProtobufValue`
    pub fn protobuf_value(&self) -> &'static dyn ProtobufValue {
        self.protobuf_value
    }

    /// Get descriptor of enum holding this value.
    pub fn enum_descriptor(&self) -> &EnumDescriptor {
        self.get_descriptor.descriptor()
    }

    /// Convert this value descriptor into proper enum object.
    ///
    /// See [`EnumDescriptor::cast`](crate::reflect::EnumDescriptor::cast) for more details.
    pub fn cast<E: 'static>(&self) -> Option<E> {
        self.enum_descriptor().cast(self.value())
    }
}

trait GetEnumDescriptor: Send + Sync + 'static {
    fn descriptor(&self) -> &EnumDescriptor;
    unsafe fn copy_to(&self, value: i32, dest: *mut ());
}

struct GetDescriptorImpl<E: ProtobufEnum>(marker::PhantomData<E>);

impl<E: ProtobufEnum> GetEnumDescriptor for GetDescriptorImpl<E> {
    fn descriptor(&self) -> &EnumDescriptor {
        E::enum_descriptor_static()
    }

    unsafe fn copy_to(&self, value: i32, dest: *mut ()) {
        let e = E::from_i32(value).expect("unknown value");
        (&e as *const E).copy_to(dest as *mut E, 1);
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
pub struct EnumDescriptor {
    full_name: String,
    proto: &'static EnumDescriptorProto,
    values: Vec<EnumValueDescriptor>,
    /// Type id of `<E>`
    type_id: TypeId,
    /// Type id of `<ProtobufEnumOrUnknown<E>>`
    enum_or_unknown_type_id: TypeId,

    #[cfg(not(rustc_nightly))]
    get_descriptor: &'static dyn GetEnumDescriptor,

    index_by_name: HashMap<String, usize>,
    index_by_number: HashMap<i32, usize>,
}

impl fmt::Debug for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumDescriptor")
            .field("full_name", &self.full_name)
            .field("..", &"..")
            .finish()
    }
}

/// Identity comparison: message descriptor are equal if their addresses are equal
impl PartialEq for EnumDescriptor {
    fn eq(&self, other: &EnumDescriptor) -> bool {
        self as *const EnumDescriptor == other as *const EnumDescriptor
    }
}

impl EnumDescriptor {
    /// Enum name as given in `.proto` file
    pub fn name(&self) -> &'static str {
        self.proto.get_name()
    }

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        &self.full_name[..]
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: ProtobufEnum>() -> &'static EnumDescriptor {
        E::enum_descriptor_static()
    }

    fn compute_full_name(
        package: &str,
        path_to_package: &str,
        proto: &EnumDescriptorProto,
    ) -> String {
        let mut full_name = package.to_owned();
        if path_to_package.len() != 0 {
            if full_name.len() != 0 {
                full_name.push('.');
            }
            full_name.push_str(path_to_package);
        }
        if full_name.len() != 0 {
            full_name.push('.');
        }
        full_name.push_str(proto.get_name());
        full_name
    }

    /// Construct `EnumDescriptor` given enum name and `FileDescriptorProto`.
    ///
    /// This function is called from generated code, and should rarely be called directly.
    ///
    /// This function is not a part of public API.
    #[doc(hidden)]
    pub fn new<E>(name_in_file: &'static str, file: &'static FileDescriptorProto) -> EnumDescriptor
    where
        E: ProtobufEnum,
    {
        let (path_to_package, proto) = match find_message_or_enum(file, name_in_file) {
            (path_to_package, MessageOrEnum::Enum(e)) => (path_to_package, e),
            (_, MessageOrEnum::Message(_)) => panic!("not an enum"),
        };

        let mut index_by_name = HashMap::new();
        let mut index_by_number = HashMap::new();
        for (i, v) in proto.value.iter().enumerate() {
            index_by_number.insert(v.get_number(), i);
            index_by_name.insert(v.get_name().to_string(), i);
        }

        let proto_values = &proto.value;
        let code_values = E::values();
        assert_eq!(proto_values.len(), code_values.len());

        let get_descriptor = &GetDescriptorImpl(marker::PhantomData::<E>);

        let values = proto_values
            .iter()
            .zip(code_values)
            .map(|(p, c)| EnumValueDescriptor {
                proto: p,
                protobuf_value: c,
                get_descriptor,
            })
            .collect();

        EnumDescriptor {
            full_name: EnumDescriptor::compute_full_name(
                file.get_package(),
                &path_to_package,
                &proto,
            ),
            proto,
            values,
            type_id: TypeId::of::<E>(),
            enum_or_unknown_type_id: TypeId::of::<ProtobufEnumOrUnknown<E>>(),
            #[cfg(not(rustc_nightly))]
            get_descriptor,
            index_by_name,
            index_by_number,
        }
    }

    /// This enum values
    pub fn values(&self) -> &[EnumValueDescriptor] {
        &self.values
    }

    /// Find enum variant by name
    pub fn value_by_name<'a>(&'a self, name: &str) -> Option<&'a EnumValueDescriptor> {
        let &index = self.index_by_name.get(name)?;
        Some(&self.values[index])
    }

    /// Find enum variant by number
    pub fn value_by_number(&self, number: i32) -> Option<&EnumValueDescriptor> {
        let &index = self.index_by_number.get(&number)?;
        Some(&self.values[index])
    }

    pub(crate) fn value_by_number_or_default(&self, number: i32) -> &EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => &self.values()[0],
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    pub fn is<E: 'static>(&self) -> bool {
        TypeId::of::<E>() == self.type_id
    }

    /// Create enum object from given value.
    ///
    /// Type parameter `E` can be either [`ProtobufEnum`](crate::ProtobufEnum)
    /// or [`ProtobufEnumOrUnknown`](crate::ProtobufEnumOrUnknown).
    ///
    /// # Panics
    ///
    /// This operation panics of `E` is `ProtobufEnum` and `value` is unknown.
    pub fn cast<E: 'static>(&self, value: i32) -> Option<E> {
        if TypeId::of::<E>() == self.type_id {
            Some(self.cast_to_protobuf_enum(value))
        } else if TypeId::of::<E>() == self.enum_or_unknown_type_id {
            Some(self.cast_to_protobuf_enum_or_unknown(value))
        } else {
            None
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnum>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        unsafe {
            let mut r = mem::uninitialized();
            self.get_descriptor
                .copy_to(value, &mut r as *mut E as *mut ());
            r
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnumOrUnknown>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        use std::ptr;
        debug_assert_eq!(mem::size_of::<E>(), mem::size_of::<i32>());
        unsafe {
            // This works because `ProtobufEnumOrUnknown<E>` is `#[repr(transparent)]`
            let mut r = mem::uninitialized();
            ptr::copy(&value, &mut r as *mut E as *mut i32, 1);
            r
        }
    }
}

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

    pub(crate) trait CastValueToProtobufEnumOrUnknown: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnumOrUnknown for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnumOrUnknown for ProtobufEnumOrUnknown<E> {
        fn cast(value: i32) -> Option<ProtobufEnumOrUnknown<E>> {
            Some(ProtobufEnumOrUnknown::from_i32(value))
        }
    }

    pub(crate) trait CastValueToProtobufEnum: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnum for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnum for E {
        fn cast(value: i32) -> Option<E> {
            Some(E::from_i32(value).expect(&format!("unknown enum value: {}", value)))
        }
    }
}
<commit_msg>Extract a function from EnumDescriptor::new<commit_after>use std::any::TypeId;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::descriptor::FileDescriptorProto;
use crate::enums::ProtobufEnum;
use crate::enums::ProtobufEnumOrUnknown;
use crate::reflect::find_message_or_enum::find_message_or_enum;
use crate::reflect::find_message_or_enum::MessageOrEnum;
use crate::reflect::ProtobufValue;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone)]
pub struct EnumValueDescriptor {
    proto: &'static EnumValueDescriptorProto,
    protobuf_value: &'static dyn ProtobufValue,
    get_descriptor: &'static dyn GetEnumDescriptor,
}

impl PartialEq for EnumValueDescriptor {
    fn eq(&self, other: &EnumValueDescriptor) -> bool {
        self.enum_descriptor() == other.enum_descriptor() && self.value() == other.value()
    }
}

impl Hash for EnumValueDescriptor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&self.value(), state)
    }
}

fn _assert_send_sync() {
    fn _assert_send_sync<T: Send + Sync>() {}
    _assert_send_sync::<EnumValueDescriptor>();
}

impl fmt::Debug for EnumValueDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumValueDescriptor")
            .field("proto", self.proto)
            .field("value", &"...")
            .finish()
    }
}

impl Copy for EnumValueDescriptor {}

impl EnumValueDescriptor {
    /// Name of enum variant as specified in proto file
    pub fn name(&self) -> &'static str {
        self.proto.get_name()
    }

    /// `i32` value of the enum variant
    pub fn value(&self) -> i32 {
        self.proto.get_number()
    }

    /// Convert to generic `ProtobufValue`
    pub fn protobuf_value(&self) -> &'static dyn ProtobufValue {
        self.protobuf_value
    }

    /// Get descriptor of enum holding this value.
    pub fn enum_descriptor(&self) -> &EnumDescriptor {
        self.get_descriptor.descriptor()
    }

    /// Convert this value descriptor into proper enum object.
    ///
    /// See [`EnumDescriptor::cast`](crate::reflect::EnumDescriptor::cast) for more details.
    pub fn cast<E: 'static>(&self) -> Option<E> {
        self.enum_descriptor().cast(self.value())
    }
}

trait GetEnumDescriptor: Send + Sync + 'static {
    fn descriptor(&self) -> &EnumDescriptor;
    unsafe fn copy_to(&self, value: i32, dest: *mut ());
}

struct GetDescriptorImpl<E: ProtobufEnum>(marker::PhantomData<E>);

impl<E: ProtobufEnum> GetEnumDescriptor for GetDescriptorImpl<E> {
    fn descriptor(&self) -> &EnumDescriptor {
        E::enum_descriptor_static()
    }

    unsafe fn copy_to(&self, value: i32, dest: *mut ()) {
        let e = E::from_i32(value).expect("unknown value");
        (&e as *const E).copy_to(dest as *mut E, 1);
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
pub struct EnumDescriptor {
    full_name: String,
    proto: &'static EnumDescriptorProto,
    values: Vec<EnumValueDescriptor>,
    /// Type id of `<E>`
    type_id: TypeId,
    /// Type id of `<ProtobufEnumOrUnknown<E>>`
    enum_or_unknown_type_id: TypeId,

    #[cfg(not(rustc_nightly))]
    get_descriptor: &'static dyn GetEnumDescriptor,

    index_by_name: HashMap<String, usize>,
    index_by_number: HashMap<i32, usize>,
}

impl fmt::Debug for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumDescriptor")
            .field("full_name", &self.full_name)
            .field("..", &"..")
            .finish()
    }
}

/// Identity comparison: message descriptor are equal if their addresses are equal
impl PartialEq for EnumDescriptor {
    fn eq(&self, other: &EnumDescriptor) -> bool {
        self as *const EnumDescriptor == other as *const EnumDescriptor
    }
}

impl EnumDescriptor {
    /// Enum name as given in `.proto` file
    pub fn name(&self) -> &'static str {
        self.proto.get_name()
    }

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        &self.full_name[..]
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: ProtobufEnum>() -> &'static EnumDescriptor {
        E::enum_descriptor_static()
    }

    fn compute_full_name(
        package: &str,
        path_to_package: &str,
        proto: &EnumDescriptorProto,
    ) -> String {
        let mut full_name = package.to_owned();
        if path_to_package.len() != 0 {
            if full_name.len() != 0 {
                full_name.push('.');
            }
            full_name.push_str(path_to_package);
        }
        if full_name.len() != 0 {
            full_name.push('.');
        }
        full_name.push_str(proto.get_name());
        full_name
    }

    /// Separate function to reduce generated code size bloat.
    fn make_indices(proto: &EnumDescriptorProto) -> (HashMap<String, usize>, HashMap<i32, usize>) {
        let mut index_by_name = HashMap::new();
        let mut index_by_number = HashMap::new();
        for (i, v) in proto.value.iter().enumerate() {
            index_by_number.insert(v.get_number(), i);
            index_by_name.insert(v.get_name().to_string(), i);
        }

        (index_by_name, index_by_number)
    }

    /// Construct `EnumDescriptor` given enum name and `FileDescriptorProto`.
    ///
    /// This function is called from generated code, and should rarely be called directly.
    ///
    /// This function is not a part of public API.
    #[doc(hidden)]
    pub fn new<E>(name_in_file: &'static str, file: &'static FileDescriptorProto) -> EnumDescriptor
    where
        E: ProtobufEnum,
    {
        let (path_to_package, proto) = match find_message_or_enum(file, name_in_file) {
            (path_to_package, MessageOrEnum::Enum(e)) => (path_to_package, e),
            (_, MessageOrEnum::Message(_)) => panic!("not an enum"),
        };

        let (index_by_name, index_by_number) = EnumDescriptor::make_indices(proto);

        let proto_values = &proto.value;
        let code_values = E::values();
        assert_eq!(proto_values.len(), code_values.len());

        let get_descriptor = &GetDescriptorImpl(marker::PhantomData::<E>);

        let values = proto_values
            .iter()
            .zip(code_values)
            .map(|(p, c)| EnumValueDescriptor {
                proto: p,
                protobuf_value: c,
                get_descriptor,
            })
            .collect();

        EnumDescriptor {
            full_name: EnumDescriptor::compute_full_name(
                file.get_package(),
                &path_to_package,
                &proto,
            ),
            proto,
            values,
            type_id: TypeId::of::<E>(),
            enum_or_unknown_type_id: TypeId::of::<ProtobufEnumOrUnknown<E>>(),
            #[cfg(not(rustc_nightly))]
            get_descriptor,
            index_by_name,
            index_by_number,
        }
    }

    /// This enum values
    pub fn values(&self) -> &[EnumValueDescriptor] {
        &self.values
    }

    /// Find enum variant by name
    pub fn value_by_name<'a>(&'a self, name: &str) -> Option<&'a EnumValueDescriptor> {
        let &index = self.index_by_name.get(name)?;
        Some(&self.values[index])
    }

    /// Find enum variant by number
    pub fn value_by_number(&self, number: i32) -> Option<&EnumValueDescriptor> {
        let &index = self.index_by_number.get(&number)?;
        Some(&self.values[index])
    }

    pub(crate) fn value_by_number_or_default(&self, number: i32) -> &EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => &self.values()[0],
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    pub fn is<E: 'static>(&self) -> bool {
        TypeId::of::<E>() == self.type_id
    }

    /// Create enum object from given value.
    ///
    /// Type parameter `E` can be either [`ProtobufEnum`](crate::ProtobufEnum)
    /// or [`ProtobufEnumOrUnknown`](crate::ProtobufEnumOrUnknown).
    ///
    /// # Panics
    ///
    /// This operation panics of `E` is `ProtobufEnum` and `value` is unknown.
    pub fn cast<E: 'static>(&self, value: i32) -> Option<E> {
        if TypeId::of::<E>() == self.type_id {
            Some(self.cast_to_protobuf_enum(value))
        } else if TypeId::of::<E>() == self.enum_or_unknown_type_id {
            Some(self.cast_to_protobuf_enum_or_unknown(value))
        } else {
            None
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnum>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        unsafe {
            let mut r = mem::uninitialized();
            self.get_descriptor
                .copy_to(value, &mut r as *mut E as *mut ());
            r
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnumOrUnknown>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        use std::ptr;
        debug_assert_eq!(mem::size_of::<E>(), mem::size_of::<i32>());
        unsafe {
            // This works because `ProtobufEnumOrUnknown<E>` is `#[repr(transparent)]`
            let mut r = mem::uninitialized();
            ptr::copy(&value, &mut r as *mut E as *mut i32, 1);
            r
        }
    }
}

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

    pub(crate) trait CastValueToProtobufEnumOrUnknown: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnumOrUnknown for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnumOrUnknown for ProtobufEnumOrUnknown<E> {
        fn cast(value: i32) -> Option<ProtobufEnumOrUnknown<E>> {
            Some(ProtobufEnumOrUnknown::from_i32(value))
        }
    }

    pub(crate) trait CastValueToProtobufEnum: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnum for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnum for E {
        fn cast(value: i32) -> Option<E> {
            Some(E::from_i32(value).expect(&format!("unknown enum value: {}", value)))
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Keegan McAllister.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// See `LICENSE` in this repository.

#![feature(plugin_registrar, rustc_private)]

extern crate rustc_registry;

use rustc_registry::registry::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_llvm_pass("afl-coverage");
}
<commit_msg>Fix plugin compilation<commit_after>// Copyright 2015 Keegan McAllister.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// See `LICENSE` in this repository.

#![feature(plugin_registrar, rustc_private)]

extern crate rustc_plugin;

use rustc_plugin::registry::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_llvm_pass("afl-coverage");
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Élisabeth HENRY.
//
// This file is part of Caribon.
//
// Caribon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Caribon.  If not, see <http://www.gnu.org/licenses/>.

use std::env;
use std::process::exit;
use std::fs::File;
use std::error::Error;
use std::io;
use std::io::Read;
use std::io::Write;
use caribon::Parser;

static ARG_LANG:&'static str = "--language=";
static ARG_THRESHOLD:&'static str = "--threshold=";
static ARG_MAX_DISTANCE:&'static str = "--max-distance=";
static ARG_GLOBAL_THRESHOLD:&'static str = "--global-threshold=";
static ARG_INPUT_FORMAT:&'static str = "--input-format=";
static ARG_OUTPUT_FORMAT:&'static str = "--output-format=";
static ARG_IGNORE_PROPER:&'static str = "--ignore-proper=";
static ARG_USAGE:&'static str = "--help";
static ARG_INPUT:&'static str = "--input=";
static ARG_OUTPUT:&'static str = "--output=";
static ARG_VERSION:&'static str = "--version";
static ARG_LIST_LANGUAGES:&'static str = "--list-languages";
static ARG_IGNORE:&'static str = "--ignore=";
static ARG_ADD_IGNORED:&'static str = "--add-ignored=";
static ARG_FUZZY:&'static str = "--fuzzy=";

pub fn list_languages() {
    println!("Supported languages:");
    for l in Parser::list_languages() {
        println!("- '{}'", l);
    }
}

fn version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}

fn usage() {
    println!("
Caribon, version {} by Élisabeth Henry <liz.henry@ouvaton.org>

Detects the repetitions in a text and highlights them

Options:
  {}: displays this message
  {}: displays program version
  {}: lists the implemented languages
  {}[language]: sets the language of the text (default: french)
  {}[filename]: sets input file (default: stdin)
  {}[filename]: sets output file (default: stdout)
  {}[string]: sets ignored word to those contained in the string separated by spaces or comma
  \t(default: the builtin list that depends on the language)
  {}[string]: adds words contained in the string to the list of ignored words (default: none)
  {}[value]: sets max distance to be considered a repetition (in words) (default: 50)
  {}[value]: sets threshold value for underlining local repetitions (default: 1.9)
  {}[value|none]: activate global repetition detector and sets threshold value for underlining global repetitions
  \t(this threshold corresponds to the minimal ratio of words in the text, e.g. a threshold of 0.01 means
  \tthat a word must represent at least 1% of the total words in the text to be underlined) (default: not activated)
  {}[text|html]: sets input format (default: text, depends on input file extension)
  {}[terminal|html]|markdown]: sets output format (default: terminal, depends on output file extension)
  {}[true|false]: if true, try to detect proper nouns and don't count them (default: false)
  {}[value|none]: activate fuzzy string matching; value must be between 0.0 and 1.0 and corresponds to the maximal
  \t'difference' between two words until they are no more considered identical (e.g. 0.25 means that two words
  \t must have no more than 25% of difference) (default: not activated)",
             env!("CARGO_PKG_VERSION"),
             ARG_USAGE,
             ARG_VERSION,
             ARG_LIST_LANGUAGES,
             ARG_LANG,
             ARG_INPUT,
             ARG_OUTPUT,
             ARG_IGNORE,
             ARG_ADD_IGNORED,
             ARG_MAX_DISTANCE,
             ARG_THRESHOLD,
             ARG_GLOBAL_THRESHOLD,
             ARG_INPUT_FORMAT,
             ARG_OUTPUT_FORMAT,
             ARG_IGNORE_PROPER,
             ARG_FUZZY);
}

pub struct Config {
    pub lang: String,
    pub threshold: f32,
    pub global_threshold: Option<f32>,
    pub max_distance: u32,
    pub input_format: String,
    pub output_format: String,
    pub ignore_proper: bool,
    pub input: Box<Read>,
    pub input_filename: String,
    pub output: Box<Write>,
    pub output_filename: String,
    pub ignored: String,
    pub add_ignored: String,
    pub fuzzy: Option<f32>,
}

impl Config {
    /// New default config
    pub fn new() -> Config {
        Config {
            lang: "french".to_string(),
            threshold:1.9,
            global_threshold: None,
            max_distance:50,
            input_format: String::new(),
            output_format: String::new(),
            ignore_proper:false,
            input: Box::new(io::stdin()),
            input_filename: String::new(),
            output: Box::new(io::stdout()),
            output_filename: String::new(),
            ignored: String::new(),
            add_ignored: String::new(),
            fuzzy: None
        }
    }

    /// New config from program args
    pub fn new_from_args() -> Config {
        let mut config = Config::new();
        let mut iter = env::args();
        iter.next();
        // Sets fields from args
        for argument in iter {
            config.parse_arg(&argument);
        }
        // Sets fields to default values if they have not been set
        if config.input_format.is_empty() {
            if config.input_filename.ends_with(".html") {
                config.input_format = "html".to_string();
            } else {
                config.input_format = "text".to_string();
            }
        }
        if config.output_format.is_empty() {
            if config.output_filename.ends_with(".html") {
                config.output_format = "html".to_string();
            } else if config.output_filename.ends_with(".md") {
                config.output_format = "markdown".to_string();
            } else {
                config.output_format = "terminal".to_string();
            }
        }
        config
    }

    /// Parse a single argument
    pub fn parse_arg(&mut self, arg:&str) {
        if arg.starts_with(ARG_OUTPUT) {
            let option = &arg[ARG_OUTPUT.len()..];
            let result = File::create(option);
            match result {
                Ok(f) => {
                    self.output = Box::new(f);
                    self.output_filename = option.to_string();
                },
                Err(e) => {
                    println!("Error opening file {}: {}", option, e.description());
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_INPUT) {
            let option = &arg[ARG_INPUT.len()..];
            let result = File::open(option);
            match result {
                Ok(f) => {
                    self.input = Box::new(f);
                    self.input_filename = option.to_string();
                },
                Err(e) => {
                    println!("Error opening file {}: {}", option, e.description());
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_LANG) {
            let option = &arg[ARG_LANG.len()..];
            self.lang = option.to_string();
        } else if arg.starts_with(ARG_THRESHOLD) {
            let option = &arg[ARG_THRESHOLD.len()..];
            self.threshold = match option.parse() {
                Ok(x) => x,
                Err(_) => {
                    println!("Error passing argument to threshold: {}", option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_FUZZY) {
            let option = &arg[ARG_FUZZY.len()..];
            self.fuzzy = if option == "none" {
                None
            } else {
                match option.parse() {
                    Ok(x) => Some(x),
                    Err(_) => {
                        println!("Error passing argument to fuzzy: {}", option);
                        exit(0);
                    }
                }
            }
        } else if arg.starts_with(ARG_GLOBAL_THRESHOLD) {
            let option = &arg[ARG_GLOBAL_THRESHOLD.len()..];
            self.global_threshold = if option == "none" {
                None
            } else {
                match option.parse() {
                    Ok(x) => Some(x),
                    Err(_) => {
                        println!("Error passing argument to global threshold: {}", option);
                        exit(0);
                    }
                }
            }
        } else if arg.starts_with(ARG_MAX_DISTANCE) {
            let option = &arg[ARG_MAX_DISTANCE.len()..];
            self.max_distance = match option.parse() {
                Ok(x) => x,
                Err(_) => {
                    println!("Error passing argument to max_distance: {}", option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_INPUT_FORMAT) {
            let option = &arg[ARG_INPUT_FORMAT.len()..];
            match option {
                "html" => self.input_format = option.to_string(),
                "text" => self.input_format = option.to_string(),
                _ => {
                    println!("Wrong argument to {}: expected 'html' or 'text', received: {}", ARG_INPUT_FORMAT, option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_OUTPUT_FORMAT) {
            let option = &arg[ARG_OUTPUT_FORMAT.len()..];
            match option {
                "html" => self.output_format = option.to_string(),
                "terminal" => self.output_format = option.to_string(),
                "markdown" => self.output_format = option.to_string(),
                _ => {
                    println!("Wrong argument to {}: expected 'html', 'terminal' or 'markdown', received: {}", ARG_OUTPUT_FORMAT, option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_IGNORE_PROPER) {
            let option = &arg[ARG_IGNORE_PROPER.len()..];
            match option {
                "true" => self.ignore_proper = true,
                "false" => self.ignore_proper = false,
                _ => {
                    println!("Wrong argument to ignore_proper: expected 'true' or 'false', received: {}", option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_IGNORE) {
            let option = &arg[ARG_IGNORE.len()..];
            self.ignored = option.to_string();
        } else if arg.starts_with(ARG_ADD_IGNORED) {
            let option = &arg[ARG_ADD_IGNORED.len()..];
            self.add_ignored = option.to_string();
        } else if arg == ARG_USAGE {
            usage();
            exit(0);
        } else if arg == ARG_VERSION {
            version();
            exit(0);
        } else if arg == ARG_LIST_LANGUAGES {
            list_languages();
            exit(0);
        } else {
            println!("Unrecognized argument: {}. See {} for help", arg, ARG_USAGE);
            exit(0);
        }
    }
}
<commit_msg>config.rs: replaced static by const<commit_after>// Copyright (C) 2015 Élisabeth HENRY.
//
// This file is part of Caribon.
//
// Caribon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Caribon.  If not, see <http://www.gnu.org/licenses/>.

use std::env;
use std::process::exit;
use std::fs::File;
use std::error::Error;
use std::io;
use std::io::Read;
use std::io::Write;
use caribon::Parser;

const ARG_LANG:&'static str = "--language=";
const ARG_THRESHOLD:&'static str = "--threshold=";
const ARG_MAX_DISTANCE:&'static str = "--max-distance=";
const ARG_GLOBAL_THRESHOLD:&'static str = "--global-threshold=";
const ARG_INPUT_FORMAT:&'static str = "--input-format=";
const ARG_OUTPUT_FORMAT:&'static str = "--output-format=";
const ARG_IGNORE_PROPER:&'static str = "--ignore-proper=";
const ARG_USAGE:&'static str = "--help";
const ARG_INPUT:&'static str = "--input=";
const ARG_OUTPUT:&'static str = "--output=";
const ARG_VERSION:&'static str = "--version";
const ARG_LIST_LANGUAGES:&'static str = "--list-languages";
const ARG_IGNORE:&'static str = "--ignore=";
const ARG_ADD_IGNORED:&'static str = "--add-ignored=";
const ARG_FUZZY:&'static str = "--fuzzy=";

pub fn list_languages() {
    println!("Supported languages:");
    for l in Parser::list_languages() {
        println!("- '{}'", l);
    }
}

fn version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}

fn usage() {
    println!("
Caribon, version {} by Élisabeth Henry <liz.henry@ouvaton.org>

Detects the repetitions in a text and highlights them

Options:
  {}: displays this message
  {}: displays program version
  {}: lists the implemented languages
  {}[language]: sets the language of the text (default: french)
  {}[filename]: sets input file (default: stdin)
  {}[filename]: sets output file (default: stdout)
  {}[string]: sets ignored word to those contained in the string separated by spaces or comma
  \t(default: the builtin list that depends on the language)
  {}[string]: adds words contained in the string to the list of ignored words (default: none)
  {}[value]: sets max distance to be considered a repetition (in words) (default: 50)
  {}[value]: sets threshold value for underlining local repetitions (default: 1.9)
  {}[value|none]: activate global repetition detector and sets threshold value for underlining global repetitions
  \t(this threshold corresponds to the minimal ratio of words in the text, e.g. a threshold of 0.01 means
  \tthat a word must represent at least 1% of the total words in the text to be underlined) (default: not activated)
  {}[text|html]: sets input format (default: text, depends on input file extension)
  {}[terminal|html]|markdown]: sets output format (default: terminal, depends on output file extension)
  {}[true|false]: if true, try to detect proper nouns and don't count them (default: false)
  {}[value|none]: activate fuzzy string matching; value must be between 0.0 and 1.0 and corresponds to the maximal
  \t'difference' between two words until they are no more considered identical (e.g. 0.25 means that two words
  \t must have no more than 25% of difference) (default: not activated)",
             env!("CARGO_PKG_VERSION"),
             ARG_USAGE,
             ARG_VERSION,
             ARG_LIST_LANGUAGES,
             ARG_LANG,
             ARG_INPUT,
             ARG_OUTPUT,
             ARG_IGNORE,
             ARG_ADD_IGNORED,
             ARG_MAX_DISTANCE,
             ARG_THRESHOLD,
             ARG_GLOBAL_THRESHOLD,
             ARG_INPUT_FORMAT,
             ARG_OUTPUT_FORMAT,
             ARG_IGNORE_PROPER,
             ARG_FUZZY);
}

pub struct Config {
    pub lang: String,
    pub threshold: f32,
    pub global_threshold: Option<f32>,
    pub max_distance: u32,
    pub input_format: String,
    pub output_format: String,
    pub ignore_proper: bool,
    pub input: Box<Read>,
    pub input_filename: String,
    pub output: Box<Write>,
    pub output_filename: String,
    pub ignored: String,
    pub add_ignored: String,
    pub fuzzy: Option<f32>,
}

impl Config {
    /// New default config
    pub fn new() -> Config {
        Config {
            lang: "french".to_string(),
            threshold:1.9,
            global_threshold: None,
            max_distance:50,
            input_format: String::new(),
            output_format: String::new(),
            ignore_proper:false,
            input: Box::new(io::stdin()),
            input_filename: String::new(),
            output: Box::new(io::stdout()),
            output_filename: String::new(),
            ignored: String::new(),
            add_ignored: String::new(),
            fuzzy: None
        }
    }

    /// New config from program args
    pub fn new_from_args() -> Config {
        let mut config = Config::new();
        let mut iter = env::args();
        iter.next();
        // Sets fields from args
        for argument in iter {
            config.parse_arg(&argument);
        }
        // Sets fields to default values if they have not been set
        if config.input_format.is_empty() {
            if config.input_filename.ends_with(".html") {
                config.input_format = "html".to_string();
            } else {
                config.input_format = "text".to_string();
            }
        }
        if config.output_format.is_empty() {
            if config.output_filename.ends_with(".html") {
                config.output_format = "html".to_string();
            } else if config.output_filename.ends_with(".md") {
                config.output_format = "markdown".to_string();
            } else {
                config.output_format = "terminal".to_string();
            }
        }
        config
    }

    /// Parse a single argument
    pub fn parse_arg(&mut self, arg:&str) {
        if arg.starts_with(ARG_OUTPUT) {
            let option = &arg[ARG_OUTPUT.len()..];
            let result = File::create(option);
            match result {
                Ok(f) => {
                    self.output = Box::new(f);
                    self.output_filename = option.to_string();
                },
                Err(e) => {
                    println!("Error opening file {}: {}", option, e.description());
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_INPUT) {
            let option = &arg[ARG_INPUT.len()..];
            let result = File::open(option);
            match result {
                Ok(f) => {
                    self.input = Box::new(f);
                    self.input_filename = option.to_string();
                },
                Err(e) => {
                    println!("Error opening file {}: {}", option, e.description());
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_LANG) {
            let option = &arg[ARG_LANG.len()..];
            self.lang = option.to_string();
        } else if arg.starts_with(ARG_THRESHOLD) {
            let option = &arg[ARG_THRESHOLD.len()..];
            self.threshold = match option.parse() {
                Ok(x) => x,
                Err(_) => {
                    println!("Error passing argument to threshold: {}", option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_FUZZY) {
            let option = &arg[ARG_FUZZY.len()..];
            self.fuzzy = if option == "none" {
                None
            } else {
                match option.parse() {
                    Ok(x) => Some(x),
                    Err(_) => {
                        println!("Error passing argument to fuzzy: {}", option);
                        exit(0);
                    }
                }
            }
        } else if arg.starts_with(ARG_GLOBAL_THRESHOLD) {
            let option = &arg[ARG_GLOBAL_THRESHOLD.len()..];
            self.global_threshold = if option == "none" {
                None
            } else {
                match option.parse() {
                    Ok(x) => Some(x),
                    Err(_) => {
                        println!("Error passing argument to global threshold: {}", option);
                        exit(0);
                    }
                }
            }
        } else if arg.starts_with(ARG_MAX_DISTANCE) {
            let option = &arg[ARG_MAX_DISTANCE.len()..];
            self.max_distance = match option.parse() {
                Ok(x) => x,
                Err(_) => {
                    println!("Error passing argument to max_distance: {}", option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_INPUT_FORMAT) {
            let option = &arg[ARG_INPUT_FORMAT.len()..];
            match option {
                "html" => self.input_format = option.to_string(),
                "text" => self.input_format = option.to_string(),
                _ => {
                    println!("Wrong argument to {}: expected 'html' or 'text', received: {}", ARG_INPUT_FORMAT, option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_OUTPUT_FORMAT) {
            let option = &arg[ARG_OUTPUT_FORMAT.len()..];
            match option {
                "html" => self.output_format = option.to_string(),
                "terminal" => self.output_format = option.to_string(),
                "markdown" => self.output_format = option.to_string(),
                _ => {
                    println!("Wrong argument to {}: expected 'html', 'terminal' or 'markdown', received: {}", ARG_OUTPUT_FORMAT, option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_IGNORE_PROPER) {
            let option = &arg[ARG_IGNORE_PROPER.len()..];
            match option {
                "true" => self.ignore_proper = true,
                "false" => self.ignore_proper = false,
                _ => {
                    println!("Wrong argument to ignore_proper: expected 'true' or 'false', received: {}", option);
                    exit(0);
                }
            }
        } else if arg.starts_with(ARG_IGNORE) {
            let option = &arg[ARG_IGNORE.len()..];
            self.ignored = option.to_string();
        } else if arg.starts_with(ARG_ADD_IGNORED) {
            let option = &arg[ARG_ADD_IGNORED.len()..];
            self.add_ignored = option.to_string();
        } else if arg == ARG_USAGE {
            usage();
            exit(0);
        } else if arg == ARG_VERSION {
            version();
            exit(0);
        } else if arg == ARG_LIST_LANGUAGES {
            list_languages();
            exit(0);
        } else {
            println!("Unrecognized argument: {}. See {} for help", arg, ARG_USAGE);
            exit(0);
        }
    }
}
<|endoftext|>