<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#[macro_use] #[no_link]
extern crate cfg_if;
extern crate gecko_bindings;
extern crate heapsize;
extern crate selectors;
extern crate serde;

use gecko_bindings::bindings::Gecko_AddRefAtom;
use gecko_bindings::bindings::Gecko_AtomEqualsUTF8IgnoreCase;
use gecko_bindings::bindings::Gecko_Atomize;
use gecko_bindings::bindings::Gecko_GetAtomAsUTF16;
use gecko_bindings::bindings::Gecko_ReleaseAtom;
use gecko_bindings::structs::nsIAtom;
use heapsize::HeapSizeOf;
use selectors::bloom::BloomHash;
use selectors::parser::FromCowStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::borrow::{Cow, Borrow};
use std::char::{self, DecodeUtf16};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::iter::Cloned;
use std::mem;
use std::ops::Deref;
use std::slice;

#[macro_use]
pub mod atom_macro;
pub mod namespace;

pub use namespace::{Namespace, WeakNamespace};

/// A strong reference to a Gecko atom.
#[derive(PartialEq, Eq)]
pub struct Atom(*mut WeakAtom);

/// An atom *without* a strong reference.
///
/// Only usable as `&'a WeakAtom`,
/// where `'a` is the lifetime of something that holds a strong reference to that atom.
pub struct WeakAtom(nsIAtom);

pub type BorrowedAtom<'a> = &'a WeakAtom;

impl Deref for Atom {
    type Target = WeakAtom;

    #[inline]
    fn deref(&self) -> &WeakAtom {
        unsafe {
            &*self.0
        }
    }
}

impl Borrow<WeakAtom> for Atom {
    #[inline]
    fn borrow(&self) -> &WeakAtom {
        self
    }
}

impl Eq for WeakAtom {}
impl PartialEq for WeakAtom {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        let weak: *const WeakAtom = self;
        let other: *const WeakAtom = other;
        weak == other
    }
}

unsafe impl Send for Atom {}
unsafe impl Sync for Atom {}
unsafe impl Sync for WeakAtom {}

impl WeakAtom {
    #[inline]
    pub unsafe fn new<'a>(atom: *mut nsIAtom) -> &'a mut Self {
        &mut *(atom as *mut WeakAtom)
    }

    #[inline]
    pub fn clone(&self) -> Atom {
        Atom::from(self.as_ptr())
    }

    #[inline]
    pub fn get_hash(&self) -> u32 {
        self.0.mHash
    }

    pub fn as_slice(&self) -> &[u16] {
        unsafe {
            let mut len = 0;
            let ptr = Gecko_GetAtomAsUTF16(self.as_ptr(), &mut len);
            slice::from_raw_parts(ptr, len as usize)
        }
    }

    // NOTE: don't expose this, since it's slow, and easy to be misused.
    fn chars(&self) -> DecodeUtf16<Cloned<slice::Iter<u16>>> {
        char::decode_utf16(self.as_slice().iter().cloned())
    }

    pub fn with_str<F, Output>(&self, cb: F) -> Output
        where F: FnOnce(&str) -> Output
    {
        // FIXME(bholley): We should measure whether it makes more sense to
        // cache the UTF-8 version in the Gecko atom table somehow.
        let owned = String::from_utf16(self.as_slice()).unwrap();
        cb(&owned)
    }

    #[inline]
    pub fn eq_str_ignore_ascii_case(&self, s: &str) -> bool {
        unsafe {
            Gecko_AtomEqualsUTF8IgnoreCase(self.as_ptr(), s.as_ptr() as *const _, s.len() as u32)
        }
    }

    #[inline]
    pub fn to_string(&self) -> String {
        String::from_utf16(self.as_slice()).unwrap()
    }

    #[inline]
    pub fn is_static(&self) -> bool {
        unsafe {
            (*self.as_ptr()).mIsStatic() != 0
        }
    }

    #[inline]
    pub fn len(&self) -> u32 {
        unsafe {
            (*self.as_ptr()).mLength()
        }
    }

    #[inline]
    pub fn as_ptr(&self) -> *mut nsIAtom {
        let const_ptr: *const nsIAtom = &self.0;
        const_ptr as *mut nsIAtom
    }
}

impl fmt::Debug for WeakAtom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "Gecko WeakAtom({:p}, {})", self, self)
    }
}

impl fmt::Display for WeakAtom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        for c in self.chars() {
            try!(write!(w, "{}", c.unwrap_or(char::REPLACEMENT_CHARACTER)))
        }
        Ok(())
    }
}

impl Atom {
    pub unsafe fn with<F>(ptr: *mut nsIAtom, callback: &mut F) where F: FnMut(&Atom) {
        let atom = Atom(WeakAtom::new(ptr));
        callback(&atom);
        mem::forget(atom);
    }

    /// Creates an atom from an static atom pointer without checking in release
    /// builds.
    ///
    /// Right now it's only used by the atom macro, and ideally it should keep
    /// that way, now we have sugar for is_static, creating atoms using
    /// Atom::from should involve almost no overhead.
    #[inline]
    unsafe fn from_static(ptr: *mut nsIAtom) -> Self {
        let atom = Atom(ptr as *mut WeakAtom);
        debug_assert!(atom.is_static(),
                      "Called from_static for a non-static atom!");
        atom
    }
}

impl BloomHash for Atom {
    #[inline]
    fn bloom_hash(&self) -> u32 {
        self.get_hash()
    }
}

impl BloomHash for WeakAtom {
    #[inline]
    fn bloom_hash(&self) -> u32 {
        self.get_hash()
    }
}

impl Hash for Atom {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_u32(self.get_hash());
    }
}

impl Hash for WeakAtom {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_u32(self.get_hash());
    }
}

impl Clone for Atom {
    #[inline(always)]
    fn clone(&self) -> Atom {
        Atom::from(self.as_ptr())
    }
}

impl Drop for Atom {
    #[inline]
    fn drop(&mut self) {
        if !self.is_static() {
            unsafe {
                Gecko_ReleaseAtom(self.as_ptr());
            }
        }
    }
}

impl Default for Atom {
    #[inline]
    fn default() -> Self {
        atom!("")
    }
}

impl HeapSizeOf for Atom {
    fn heap_size_of_children(&self) -> usize {
        0
    }
}

impl Serialize for Atom {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        self.with_str(|s| s.serialize(serializer))
    }
}

impl Deserialize for Atom {
    fn deserialize<D>(deserializer: &mut D) -> Result<Atom, D::Error> where D: Deserializer {
        let string: String = try!(Deserialize::deserialize(deserializer));
        Ok(Atom::from(&*string))
    }
}

impl fmt::Debug for Atom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "Gecko Atom({:p}, {})", self.0, self)
    }
}

impl fmt::Display for Atom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            (&*self.0).fmt(w)
        }
    }
}

impl<'a> From<&'a str> for Atom {
    #[inline]
    fn from(string: &str) -> Atom {
        debug_assert!(string.len() <= u32::max_value() as usize);
        unsafe {
            Atom(WeakAtom::new(
                Gecko_Atomize(string.as_ptr() as *const _, string.len() as u32)
            ))
        }
    }
}

impl<'a> From<Cow<'a, str>> for Atom {
    #[inline]
    fn from(string: Cow<'a, str>) -> Atom {
        Atom::from(&*string)
    }
}

impl FromCowStr for Atom {
    #[inline]
    fn from_cow_str(string: Cow<str>) -> Atom {
        Atom::from(&*string)
    }
}

impl From<String> for Atom {
    #[inline]
    fn from(string: String) -> Atom {
        Atom::from(&*string)
    }
}

impl From<*mut nsIAtom> for Atom {
    #[inline]
    fn from(ptr: *mut nsIAtom) -> Atom {
        debug_assert!(!ptr.is_null());
        unsafe {
            if (*ptr).mIsStatic() == 0 {
                Gecko_AddRefAtom(ptr);
            }
            Atom(WeakAtom::new(ptr))
        }
    }
}
<commit_msg>Auto merge of #12906 - servo:ffiless, r=emilio<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#[macro_use] #[no_link]
extern crate cfg_if;
extern crate gecko_bindings;
extern crate heapsize;
extern crate selectors;
extern crate serde;

use gecko_bindings::bindings::Gecko_AddRefAtom;
use gecko_bindings::bindings::Gecko_Atomize;
use gecko_bindings::bindings::Gecko_ReleaseAtom;
use gecko_bindings::structs::nsIAtom;
use heapsize::HeapSizeOf;
use selectors::bloom::BloomHash;
use selectors::parser::FromCowStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::ascii::AsciiExt;
use std::borrow::{Cow, Borrow};
use std::char::{self, DecodeUtf16};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::iter::Cloned;
use std::mem;
use std::ops::Deref;
use std::slice;

#[macro_use]
pub mod atom_macro;
pub mod namespace;

pub use namespace::{Namespace, WeakNamespace};

/// A strong reference to a Gecko atom.
#[derive(PartialEq, Eq)]
pub struct Atom(*mut WeakAtom);

/// An atom *without* a strong reference.
///
/// Only usable as `&'a WeakAtom`,
/// where `'a` is the lifetime of something that holds a strong reference to that atom.
pub struct WeakAtom(nsIAtom);

pub type BorrowedAtom<'a> = &'a WeakAtom;

impl Deref for Atom {
    type Target = WeakAtom;

    #[inline]
    fn deref(&self) -> &WeakAtom {
        unsafe {
            &*self.0
        }
    }
}

impl Borrow<WeakAtom> for Atom {
    #[inline]
    fn borrow(&self) -> &WeakAtom {
        self
    }
}

impl Eq for WeakAtom {}
impl PartialEq for WeakAtom {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        let weak: *const WeakAtom = self;
        let other: *const WeakAtom = other;
        weak == other
    }
}

unsafe impl Send for Atom {}
unsafe impl Sync for Atom {}
unsafe impl Sync for WeakAtom {}

impl WeakAtom {
    #[inline]
    pub unsafe fn new<'a>(atom: *mut nsIAtom) -> &'a mut Self {
        &mut *(atom as *mut WeakAtom)
    }

    #[inline]
    pub fn clone(&self) -> Atom {
        Atom::from(self.as_ptr())
    }

    #[inline]
    pub fn get_hash(&self) -> u32 {
        self.0.mHash
    }

    pub fn as_slice(&self) -> &[u16] {
        unsafe {
            slice::from_raw_parts((*self.as_ptr()).mString, self.len() as usize)
        }
    }

    // NOTE: don't expose this, since it's slow, and easy to be misused.
    fn chars(&self) -> DecodeUtf16<Cloned<slice::Iter<u16>>> {
        char::decode_utf16(self.as_slice().iter().cloned())
    }

    pub fn with_str<F, Output>(&self, cb: F) -> Output
        where F: FnOnce(&str) -> Output
    {
        // FIXME(bholley): We should measure whether it makes more sense to
        // cache the UTF-8 version in the Gecko atom table somehow.
        let owned = String::from_utf16(self.as_slice()).unwrap();
        cb(&owned)
    }

    #[inline]
    pub fn eq_str_ignore_ascii_case(&self, s: &str) -> bool {
        self.chars().map(|r| match r {
            Ok(c) => c.to_ascii_lowercase() as u32,
            Err(e) => e.unpaired_surrogate() as u32,
        }).eq(s.chars().map(|c| c.to_ascii_lowercase() as u32))
    }

    #[inline]
    pub fn to_string(&self) -> String {
        String::from_utf16(self.as_slice()).unwrap()
    }

    #[inline]
    pub fn is_static(&self) -> bool {
        unsafe {
            (*self.as_ptr()).mIsStatic() != 0
        }
    }

    #[inline]
    pub fn len(&self) -> u32 {
        unsafe {
            (*self.as_ptr()).mLength()
        }
    }

    #[inline]
    pub fn as_ptr(&self) -> *mut nsIAtom {
        let const_ptr: *const nsIAtom = &self.0;
        const_ptr as *mut nsIAtom
    }
}

impl fmt::Debug for WeakAtom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "Gecko WeakAtom({:p}, {})", self, self)
    }
}

impl fmt::Display for WeakAtom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        for c in self.chars() {
            try!(write!(w, "{}", c.unwrap_or(char::REPLACEMENT_CHARACTER)))
        }
        Ok(())
    }
}

impl Atom {
    pub unsafe fn with<F>(ptr: *mut nsIAtom, callback: &mut F) where F: FnMut(&Atom) {
        let atom = Atom(WeakAtom::new(ptr));
        callback(&atom);
        mem::forget(atom);
    }

    /// Creates an atom from an static atom pointer without checking in release
    /// builds.
    ///
    /// Right now it's only used by the atom macro, and ideally it should keep
    /// that way, now we have sugar for is_static, creating atoms using
    /// Atom::from should involve almost no overhead.
    #[inline]
    unsafe fn from_static(ptr: *mut nsIAtom) -> Self {
        let atom = Atom(ptr as *mut WeakAtom);
        debug_assert!(atom.is_static(),
                      "Called from_static for a non-static atom!");
        atom
    }
}

impl BloomHash for Atom {
    #[inline]
    fn bloom_hash(&self) -> u32 {
        self.get_hash()
    }
}

impl BloomHash for WeakAtom {
    #[inline]
    fn bloom_hash(&self) -> u32 {
        self.get_hash()
    }
}

impl Hash for Atom {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_u32(self.get_hash());
    }
}

impl Hash for WeakAtom {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write_u32(self.get_hash());
    }
}

impl Clone for Atom {
    #[inline(always)]
    fn clone(&self) -> Atom {
        Atom::from(self.as_ptr())
    }
}

impl Drop for Atom {
    #[inline]
    fn drop(&mut self) {
        if !self.is_static() {
            unsafe {
                Gecko_ReleaseAtom(self.as_ptr());
            }
        }
    }
}

impl Default for Atom {
    #[inline]
    fn default() -> Self {
        atom!("")
    }
}

impl HeapSizeOf for Atom {
    fn heap_size_of_children(&self) -> usize {
        0
    }
}

impl Serialize for Atom {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        self.with_str(|s| s.serialize(serializer))
    }
}

impl Deserialize for Atom {
    fn deserialize<D>(deserializer: &mut D) -> Result<Atom, D::Error> where D: Deserializer {
        let string: String = try!(Deserialize::deserialize(deserializer));
        Ok(Atom::from(&*string))
    }
}

impl fmt::Debug for Atom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "Gecko Atom({:p}, {})", self.0, self)
    }
}

impl fmt::Display for Atom {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            (&*self.0).fmt(w)
        }
    }
}

impl<'a> From<&'a str> for Atom {
    #[inline]
    fn from(string: &str) -> Atom {
        debug_assert!(string.len() <= u32::max_value() as usize);
        unsafe {
            Atom(WeakAtom::new(
                Gecko_Atomize(string.as_ptr() as *const _, string.len() as u32)
            ))
        }
    }
}

impl<'a> From<Cow<'a, str>> for Atom {
    #[inline]
    fn from(string: Cow<'a, str>) -> Atom {
        Atom::from(&*string)
    }
}

impl FromCowStr for Atom {
    #[inline]
    fn from_cow_str(string: Cow<str>) -> Atom {
        Atom::from(&*string)
    }
}

impl From<String> for Atom {
    #[inline]
    fn from(string: String) -> Atom {
        Atom::from(&*string)
    }
}

impl From<*mut nsIAtom> for Atom {
    #[inline]
    fn from(ptr: *mut nsIAtom) -> Atom {
        debug_assert!(!ptr.is_null());
        unsafe {
            if (*ptr).mIsStatic() == 0 {
                Gecko_AddRefAtom(ptr);
            }
            Atom(WeakAtom::new(ptr))
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use crate::descriptor::DescriptorProto;
use crate::descriptor::FileDescriptorProto;
use crate::reflect::file::building::FileDescriptorBuilding;
use crate::reflect::message::path::MessagePath;
use crate::reflect::name::concat_paths;
use crate::reflect::service::index::ServiceIndex;
use crate::reflect::FileDescriptor;

#[derive(Debug)]
pub(crate) struct FileIndexMessageEntry {
    pub path: MessagePath,
    pub name_to_package: String,
    pub full_name: String,
    pub enclosing_message: Option<usize>,
    pub nested_messages: Vec<usize>,
    pub nested_enums: Vec<usize>,
    pub map_entry: bool,
    pub first_enum_index: usize,
}

#[derive(Debug)]
pub(crate) struct FileIndexEnumEntry {
    pub _message_path: MessagePath,
    pub _enum_index: usize,
    pub name_to_package: String,
    pub enclosing_message: Option<usize>,
}

#[derive(Debug)]
pub(crate) struct FileIndexOneofEntry {}

#[derive(Debug)]
pub(crate) struct FileIndex {
    pub(crate) messages: Vec<FileIndexMessageEntry>,
    pub(crate) message_by_name_to_package: HashMap<String, usize>,
    pub(crate) top_level_messages: Vec<usize>,
    pub(crate) enums: Vec<FileIndexEnumEntry>,
    pub(crate) enums_by_name_to_package: HashMap<String, usize>,
    pub(crate) oneofs: Vec<FileIndexOneofEntry>,
    pub(crate) services: Vec<ServiceIndex>,
}

impl FileIndex {
    pub(crate) fn index(
        file: &FileDescriptorProto,
        deps: &[FileDescriptor],
    ) -> crate::Result<FileIndex> {
        let mut index = FileIndex {
            messages: Vec::new(),
            message_by_name_to_package: HashMap::new(),
            enums: Vec::new(),
            top_level_messages: Vec::with_capacity(file.message_type.len()),
            enums_by_name_to_package: HashMap::new(),
            oneofs: Vec::new(),
            services: Vec::new(),
        };

        // Top-level enums start with zero
        for (_, e) in file.enum_type.iter().enumerate() {
            index.enums.push(FileIndexEnumEntry {
                _message_path: MessagePath(Vec::new()),
                _enum_index: index.enums.len(),
                name_to_package: e.name().to_owned(),
                enclosing_message: None,
            });
        }

        for (i, message) in file.message_type.iter().enumerate() {
            let path = MessagePath(vec![i]);
            let message_index = index.index_message_and_inners(file, message, &path, None, "");
            index.top_level_messages.push(message_index);
        }

        index.build_message_by_name_to_package();
        index.build_enum_by_name_to_package();

        for service in &file.service {
            let service_index = ServiceIndex::index(
                service,
                &FileDescriptorBuilding {
                    current_file_descriptor: file,
                    current_file_index: &index,
                    deps_with_public: deps,
                },
            )?;
            index.services.push(service_index);
        }

        Ok(index)
    }

    fn index_message_and_inners(
        &mut self,
        file: &FileDescriptorProto,
        message: &DescriptorProto,
        path: &MessagePath,
        parent: Option<usize>,
        parent_name_to_package: &str,
    ) -> usize {
        let name_to_package = concat_paths(parent_name_to_package, message.name());

        let message_index = self.messages.len();
        self.messages.push(FileIndexMessageEntry {
            path: path.clone(),
            name_to_package: String::new(),
            full_name: String::new(),
            enclosing_message: parent,
            nested_messages: Vec::with_capacity(message.nested_type.len()),
            nested_enums: Vec::with_capacity(message.enum_type.len()),
            map_entry: message.options.get_or_default().map_entry(),
            first_enum_index: self.enums.len(),
        });

        for (_, e) in message.enum_type.iter().enumerate() {
            self.enums.push(FileIndexEnumEntry {
                _message_path: path.clone(),
                _enum_index: self.enums.len(),
                name_to_package: concat_paths(&name_to_package, e.name()),
                enclosing_message: Some(message_index),
            });
            self.messages[message_index]
                .nested_enums
                .push(self.enums.len() - 1);
        }

        for (i, nested) in message.nested_type.iter().enumerate() {
            let mut nested_path = path.clone();
            nested_path.push(i);
            let nested_index = self.index_message_and_inners(
                file,
                nested,
                &nested_path,
                Some(message_index),
                &name_to_package,
            );
            self.messages[message_index]
                .nested_messages
                .push(nested_index);
        }

        self.messages[message_index].full_name = concat_paths(file.package(), &name_to_package);
        self.messages[message_index].name_to_package = name_to_package;

        message_index
    }

    fn build_message_by_name_to_package(&mut self) {
        self.message_by_name_to_package = self
            .messages
            .iter()
            .enumerate()
            .map(|(i, m)| (m.name_to_package.to_owned(), i))
            .collect();
    }

    fn build_enum_by_name_to_package(&mut self) {
        self.enums_by_name_to_package = self
            .enums
            .iter()
            .enumerate()
            .map(|(i, e)| (e.name_to_package.to_owned(), i))
            .collect();
    }
}
<commit_msg>Code cleanup<commit_after>use std::collections::HashMap;

use crate::descriptor::DescriptorProto;
use crate::descriptor::FileDescriptorProto;
use crate::reflect::file::building::FileDescriptorBuilding;
use crate::reflect::message::path::MessagePath;
use crate::reflect::name::concat_paths;
use crate::reflect::service::index::ServiceIndex;
use crate::reflect::FileDescriptor;

#[derive(Debug)]
pub(crate) struct FileIndexMessageEntry {
    pub(crate) path: MessagePath,
    pub(crate) name_to_package: String,
    pub(crate) full_name: String,
    pub(crate) enclosing_message: Option<usize>,
    pub(crate) nested_messages: Vec<usize>,
    pub(crate) nested_enums: Vec<usize>,
    pub(crate) map_entry: bool,
    pub(crate) first_enum_index: usize,
}

#[derive(Debug)]
pub(crate) struct FileIndexEnumEntry {
    pub(crate) _message_path: MessagePath,
    pub(crate) _enum_index: usize,
    pub(crate) name_to_package: String,
    pub(crate) enclosing_message: Option<usize>,
}

#[derive(Debug)]
pub(crate) struct FileIndexOneofEntry {}

#[derive(Debug)]
pub(crate) struct FileIndex {
    pub(crate) messages: Vec<FileIndexMessageEntry>,
    pub(crate) message_by_name_to_package: HashMap<String, usize>,
    pub(crate) top_level_messages: Vec<usize>,
    pub(crate) enums: Vec<FileIndexEnumEntry>,
    pub(crate) enums_by_name_to_package: HashMap<String, usize>,
    pub(crate) oneofs: Vec<FileIndexOneofEntry>,
    pub(crate) services: Vec<ServiceIndex>,
}

impl FileIndex {
    pub(crate) fn index(
        file: &FileDescriptorProto,
        deps: &[FileDescriptor],
    ) -> crate::Result<FileIndex> {
        let mut index = FileIndex {
            messages: Vec::new(),
            message_by_name_to_package: HashMap::new(),
            enums: Vec::new(),
            top_level_messages: Vec::with_capacity(file.message_type.len()),
            enums_by_name_to_package: HashMap::new(),
            oneofs: Vec::new(),
            services: Vec::new(),
        };

        // Top-level enums start with zero
        for (_, e) in file.enum_type.iter().enumerate() {
            index.enums.push(FileIndexEnumEntry {
                _message_path: MessagePath(Vec::new()),
                _enum_index: index.enums.len(),
                name_to_package: e.name().to_owned(),
                enclosing_message: None,
            });
        }

        for (i, message) in file.message_type.iter().enumerate() {
            let path = MessagePath(vec![i]);
            let message_index = index.index_message_and_inners(file, message, &path, None, "");
            index.top_level_messages.push(message_index);
        }

        index.build_message_by_name_to_package();
        index.build_enum_by_name_to_package();

        for service in &file.service {
            let service_index = ServiceIndex::index(
                service,
                &FileDescriptorBuilding {
                    current_file_descriptor: file,
                    current_file_index: &index,
                    deps_with_public: deps,
                },
            )?;
            index.services.push(service_index);
        }

        Ok(index)
    }

    fn index_message_and_inners(
        &mut self,
        file: &FileDescriptorProto,
        message: &DescriptorProto,
        path: &MessagePath,
        parent: Option<usize>,
        parent_name_to_package: &str,
    ) -> usize {
        let name_to_package = concat_paths(parent_name_to_package, message.name());

        let message_index = self.messages.len();
        self.messages.push(FileIndexMessageEntry {
            path: path.clone(),
            name_to_package: String::new(),
            full_name: String::new(),
            enclosing_message: parent,
            nested_messages: Vec::with_capacity(message.nested_type.len()),
            nested_enums: Vec::with_capacity(message.enum_type.len()),
            map_entry: message.options.get_or_default().map_entry(),
            first_enum_index: self.enums.len(),
        });

        for (_, e) in message.enum_type.iter().enumerate() {
            self.enums.push(FileIndexEnumEntry {
                _message_path: path.clone(),
                _enum_index: self.enums.len(),
                name_to_package: concat_paths(&name_to_package, e.name()),
                enclosing_message: Some(message_index),
            });
            self.messages[message_index]
                .nested_enums
                .push(self.enums.len() - 1);
        }

        for (i, nested) in message.nested_type.iter().enumerate() {
            let mut nested_path = path.clone();
            nested_path.push(i);
            let nested_index = self.index_message_and_inners(
                file,
                nested,
                &nested_path,
                Some(message_index),
                &name_to_package,
            );
            self.messages[message_index]
                .nested_messages
                .push(nested_index);
        }

        self.messages[message_index].full_name = concat_paths(file.package(), &name_to_package);
        self.messages[message_index].name_to_package = name_to_package;

        message_index
    }

    fn build_message_by_name_to_package(&mut self) {
        self.message_by_name_to_package = self
            .messages
            .iter()
            .enumerate()
            .map(|(i, m)| (m.name_to_package.to_owned(), i))
            .collect();
    }

    fn build_enum_by_name_to_package(&mut self) {
        self.enums_by_name_to_package = self
            .enums
            .iter()
            .enumerate()
            .map(|(i, e)| (e.name_to_package.to_owned(), i))
            .collect();
    }
}
<|endoftext|>


<commit_before>//! Look-up table (LUT) implementation and transformation functions.
//!
//! This module contains the [`Lut`] data type,
//! designed to turn pixel data native sample values
//! (as encoded in the _Pixel Data_ attribute)
//! into displayable or otherwise more meaningful values.
//!
//! The type also provides easy-to-use constructor functions
//! for common DICOM sample value transformations.

use num_traits::{NumCast, ToPrimitive};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use snafu::{OptionExt, Snafu};

use crate::{Rescale, WindowLevelTransform};

/// The LUT could not be created:
/// entry #{index} was mapped to {y_value},
/// which could not be cast to the target type.
#[derive(Debug, PartialEq, Snafu)]
pub struct CreateLutError {
    index: usize,
    y_value: f64,
}

impl CreateLutError {
    /// Get the original index in the LUT.
    pub fn index(&self) -> usize {
        self.index
    }
    /// Get the value which could not be converted to the target type.
    pub fn y_value(&self) -> f64 {
        self.y_value
    }
}

/// A look up table for pixel data sample value transformations.
///
/// # Example
///
/// The LUT can be populated with common transformations
/// via the functions [`new_rescale`](Lut::new_rescale),
/// [`new_rescale_and_normalize`](Lut::new_rescale_and_normalize),
/// and [`new_rescale_and_window`](Lut::new_rescale_and_window).
///
/// ```
/// # use dicom_pixeldata::{
/// #     CreateLutError, Lut, Rescale, VoiLutFunction,
/// #     WindowLevel, WindowLevelTransform,
/// # };
/// let bits_stored = 8;
/// let lut = Lut::new_rescale_and_window(
///     bits_stored,
///     false,
///     Rescale::new(1., -1024.),
///     WindowLevelTransform::new(
///         VoiLutFunction::Linear,
///         WindowLevel {
///             width: 300.,
///             center: 50.
///         }
///     ),
/// )?;
///
/// let val: u8 = lut.get(100_u8);
/// # Result::<(), CreateLutError>::Ok(())
/// ```
#[derive(Debug)]
pub struct Lut<T> {
    /// the table which maps an index to a transformed value,
    /// of size 2 to the power of `bits_stored`
    table: Vec<T>,
    /// whether the input sample values are signed (Pixel Representation = 1)
    signed: bool,
}

impl<T: 'static> Lut<T>
where
    T: NumCast,
    T: Copy,
    T: Send + Sync,
{
    /// Create a new LUT with the given characteristics
    /// and populates it with the outputs of the provided function.
    /// The function may be called concurrently.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `f`: the mapping function
    pub fn new_with_fn(
        bits_stored: u16,
        signed: bool,
        f: impl Fn(f64) -> f64 + Sync,
    ) -> Result<Self, CreateLutError> {
        let size = (1 << bits_stored as u32) as usize;
        debug_assert!(size.is_power_of_two());

        let table: Result<Vec<_>, _> = (0..size)
            .into_par_iter()
            .map(|i| {
                // account for signedness to determine input pixel value
                let x = if signed && i >= size / 2 {
                    i as f64 - size as f64
                } else {
                    i as f64
                };
                let value = f(x);
                T::from(value).context(CreateLutSnafu {
                    index: i,
                    y_value: value,
                })
            })
            .collect();
        Ok(Self {
            table: table?,
            signed,
        })
    }

    /// Create a new LUT containing only the modality rescale transformation.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `rescale`: the rescale parameters
    pub fn new_rescale(
        bits_stored: u16,
        signed: bool,
        rescale: Rescale,
    ) -> Result<Self, CreateLutError> {
        Self::new_with_fn(bits_stored, signed, |v| rescale.apply(v))
    }

    /// Create a new LUT containing the given modality rescale transformation
    /// and a min-max normalization
    /// which satisfies the raw samples given.
    /// The sample type `I` is expected to be either `u8` or `u16`,
    /// even if the sample is meant to be interpreted as signed.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `rescale`: the rescale parameters
    /// - `samples`: the raw pixel data samples expected to be fed to the LUT
    pub(crate) fn new_rescale_and_normalize<I>(
        bits_stored: u16,
        signed: bool,
        rescale: Rescale,
        samples: I,
    ) -> Result<Self, CreateLutError>
    where
        I: IntoIterator,
        I: Clone,
        I::IntoIter: Clone,
        I::Item: Clone,
        I::Item: NumCast,
        I::Item: ToPrimitive,
    {
        let samples_f64 = samples.into_iter().filter_map(|v| v.to_f64());
        let min: f64 = samples_f64.clone().fold(f64::MAX, |a, b| a.min(b));
        let max: f64 = samples_f64.fold(f64::MIN, |a, b| a.max(b));

        // convert to f64 and swap if negative slope
        let (min, max) = if rescale.slope < 0. {
            (max, min)
        } else {
            (min, max)
        };

        // create a linear window level transform
        let voi = WindowLevelTransform::linear(crate::WindowLevel {
            width: max - min,
            center: min + max / 2.,
        });

        Self::new_rescale_and_window(bits_stored, signed, rescale, voi)
    }

    /// Create a new LUT containing the modality rescale transformation
    /// and the VOI transformation defined by a window level.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `rescale`: the rescale parameters
    /// - `voi`: the value of interest (VOI) function and parameters
    pub fn new_rescale_and_window(
        bits_stored: u16,
        signed: bool,
        rescale: Rescale,
        voi: WindowLevelTransform,
    ) -> Result<Self, CreateLutError> {
        let y_max = ((1 << (bits_stored as usize)) - 1) as f64;
        Self::new_with_fn(bits_stored, signed, |v| {
            let x = v as f64;
            let v = rescale.apply(x);
            voi.apply(v, y_max)
        })
    }

    /// Create a new LUT containing
    /// a VOI transformation defined by a window level.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `voi`: the value of interest (VOI) function and parameters
    pub fn new_window(
        bits_stored: u16,
        signed: bool,
        voi: WindowLevelTransform,
    ) -> Result<Self, CreateLutError> {
        let y_max = ((1 << (bits_stored as usize)) - 1) as f64;
        Self::new_with_fn(bits_stored, signed, |v| voi.apply(v as f64, y_max))
    }

    /// Apply the transformation to a single pixel sample value.
    ///
    /// Although the input is expected to be one of `u8`, `u16`, or `u32`,
    /// this method works for signed sample values as well,
    /// with the bits reinterpreted as their unsigned counterpart.
    ///
    /// # Panics
    ///
    /// Panics if `sample_value` is larger or equal to `2^bits_stored`.
    pub fn get<I: 'static>(&self, sample_value: I) -> T
    where
        I: Copy,
        I: Into<u32>,
    {
        let val = sample_value.into();
        let index = if self.signed {
            // adjust for signedness by masking out the extra sign bits
            let mask = self.table.len() - 1;
            val as usize & mask
        } else {
            val as usize
        };
        assert!((index as usize) < self.table.len());

        self.table[index as usize]
    }

    /// Adapts an iterator of pixel data sample values
    /// to an iterator of transformed values.
    pub fn map_iter<'a, I: 'static>(
        &'a self,
        iter: impl IntoIterator<Item = I> + 'a,
    ) -> impl Iterator<Item = T> + 'a
    where
        I: Copy,
        I: Into<u32>,
    {
        iter.into_iter().map(move |i| self.get(i))
    }

    /// Adapts a parallel iterator of pixel data sample values
    /// to a parallel iterator of transformed values.
    pub fn map_par_iter<'a, I: 'static>(
        &'a self,
        iter: impl ParallelIterator<Item = I> + 'a,
    ) -> impl ParallelIterator<Item = T> + 'a
    where
        I: Copy,
        I: Into<u32>,
    {
        iter.map(move |i| self.get(i))
    }
}

#[cfg(test)]
mod tests {
    use crate::{VoiLutFunction, WindowLevel};

    use super::*;

    /// Applying a common rescale function to a value
    /// gives the expected output.
    #[test]
    fn modality_lut_baseline_2() {
        let rescale = Rescale::new(2., -1024.);

        assert_eq!(rescale.apply(0.), -1024.);
        assert_eq!(rescale.apply(1.), -1022.);
        assert_eq!(rescale.apply(2.), -1020.);
        assert_eq!(rescale.apply(1024.), 1024.);
    }

    #[test]
    fn lut_signed_numbers() {
        // 10-bit precision input, signed output
        let lut: Lut<i16> = Lut::new_rescale(10, true, Rescale::new(2., -1024.)).unwrap();

        assert_eq!(lut.get(0 as u16), -1024);
        assert_eq!(lut.get(1 as u16), -1022);
        assert_eq!(lut.get(-1_i16 as u16), -1026);
        assert_eq!(lut.get(-2_i16 as u16), -1028);
        assert_eq!(lut.get(500 as u16), -24);
    }

    #[test]
    fn lut_rescale_and_window_16bit() {
        let bits_stored = 16;
        let lut = Lut::new_rescale_and_window(
            bits_stored,
            false,
            Rescale::new(1., -1024.),
            WindowLevelTransform::new(
                VoiLutFunction::Linear,
                WindowLevel {
                    width: 300.,
                    center: 50.,
                },
            ),
        )
        .unwrap();

        // < 0
        let val: u16 = lut.get(824_u16);
        assert_eq!(val, 0);

        // > 200
        let val: u16 = lut.get(1224_u16);
        assert_eq!(val, 65535);

        // around the middle

        let val: u16 = lut.get(1024_u16 + 50);
        let expected_range = 32_500..=33_000;
        assert!(
            expected_range.contains(&val),
            "outcome was {}, expected to be in {:?}",
            val,
            expected_range,
        );
    }
}
<commit_msg>[pixeldata] tweak LUT ouput amplitude<commit_after>//! Look-up table (LUT) implementation and transformation functions.
//!
//! This module contains the [`Lut`] data type,
//! designed to turn pixel data native sample values
//! (as encoded in the _Pixel Data_ attribute)
//! into displayable or otherwise more meaningful values.
//!
//! The type also provides easy-to-use constructor functions
//! for common DICOM sample value transformations.

use num_traits::{NumCast, ToPrimitive};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use snafu::{OptionExt, Snafu};

use crate::{Rescale, WindowLevelTransform};

/// The LUT could not be created:
/// entry #{index} was mapped to {y_value},
/// which could not be cast to the target type.
#[derive(Debug, PartialEq, Snafu)]
pub struct CreateLutError {
    index: usize,
    y_value: f64,
}

impl CreateLutError {
    /// Get the original index in the LUT.
    pub fn index(&self) -> usize {
        self.index
    }
    /// Get the value which could not be converted to the target type.
    pub fn y_value(&self) -> f64 {
        self.y_value
    }
}

/// A look up table for pixel data sample value transformations.
///
/// # Example
///
/// The LUT can be populated with common transformations
/// via the functions [`new_rescale`](Lut::new_rescale),
/// [`new_rescale_and_normalize`](Lut::new_rescale_and_normalize),
/// and [`new_rescale_and_window`](Lut::new_rescale_and_window).
///
/// ```
/// # use dicom_pixeldata::{
/// #     CreateLutError, Lut, Rescale, VoiLutFunction,
/// #     WindowLevel, WindowLevelTransform,
/// # };
/// let bits_stored = 8;
/// let lut = Lut::new_rescale_and_window(
///     bits_stored,
///     false,
///     Rescale::new(1., -1024.),
///     WindowLevelTransform::new(
///         VoiLutFunction::Linear,
///         WindowLevel {
///             width: 300.,
///             center: 50.
///         }
///     ),
/// )?;
///
/// let val: u8 = lut.get(100_u8);
/// # Result::<(), CreateLutError>::Ok(())
/// ```
#[derive(Debug)]
pub struct Lut<T> {
    /// the table which maps an index to a transformed value,
    /// of size 2 to the power of `bits_stored`
    table: Vec<T>,
    /// whether the input sample values are signed (Pixel Representation = 1)
    signed: bool,
}

impl<T: 'static> Lut<T>
where
    T: NumCast,
    T: Copy,
    T: Send + Sync,
{
    /// Create a new LUT with the given characteristics
    /// and populates it with the outputs of the provided function.
    /// The function may be called concurrently.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `f`: the mapping function
    ///
    /// # Panics
    ///
    /// Panics if `bits_stored` is 0 or too large.
    pub fn new_with_fn(
        bits_stored: u16,
        signed: bool,
        f: impl Fn(f64) -> f64 + Sync,
    ) -> Result<Self, CreateLutError> {
        assert!(bits_stored != 0 && bits_stored <= 32);
        let size = (1 << bits_stored as u32) as usize;
        debug_assert!(size.is_power_of_two());

        let table: Result<Vec<_>, _> = (0..size)
            .into_par_iter()
            .map(|i| {
                // account for signedness to determine input pixel value
                let x = if signed && i >= size / 2 {
                    i as f64 - size as f64
                } else {
                    i as f64
                };
                let value = f(x);
                T::from(value).context(CreateLutSnafu {
                    index: i,
                    y_value: value,
                })
            })
            .collect();
        Ok(Self {
            table: table?,
            signed,
        })
    }

    /// Create a new LUT containing only the modality rescale transformation.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `rescale`: the rescale parameters
    ///
    /// # Panics
    ///
    /// Panics if `bits_stored` is 0 or too large.
    pub fn new_rescale(
        bits_stored: u16,
        signed: bool,
        rescale: Rescale,
    ) -> Result<Self, CreateLutError> {
        Self::new_with_fn(bits_stored, signed, |v| rescale.apply(v))
    }

    /// Create a new LUT containing the given modality rescale transformation
    /// and a min-max normalization
    /// which satisfies the raw samples given.
    /// The sample type `I` is expected to be either `u8` or `u16`,
    /// even if the sample is meant to be interpreted as signed.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `rescale`: the rescale parameters
    /// - `samples`: the raw pixel data samples expected to be fed to the LUT
    ///
    /// # Panics
    ///
    /// Panics if `bits_stored` is 0 or too large.
    pub(crate) fn new_rescale_and_normalize<I>(
        bits_stored: u16,
        signed: bool,
        rescale: Rescale,
        samples: I,
    ) -> Result<Self, CreateLutError>
    where
        I: IntoIterator,
        I: Clone,
        I::IntoIter: Clone,
        I::Item: Clone,
        I::Item: NumCast,
        I::Item: ToPrimitive,
    {
        let samples_f64 = samples.into_iter().filter_map(|v| v.to_f64());
        let min: f64 = samples_f64.clone().fold(f64::MAX, |a, b| a.min(b));
        let max: f64 = samples_f64.fold(f64::MIN, |a, b| a.max(b));

        // convert to f64 and swap if negative slope
        let (min, max) = if rescale.slope < 0. {
            (max, min)
        } else {
            (min, max)
        };

        // create a linear window level transform
        let voi = WindowLevelTransform::linear(crate::WindowLevel {
            width: max - min,
            center: min + max / 2.,
        });

        Self::new_rescale_and_window(bits_stored, signed, rescale, voi)
    }

    /// Create a new LUT containing the modality rescale transformation
    /// and the VOI transformation defined by a window level.
    ///
    /// The amplitude of the output values
    /// goes from 0 to `2^n - 1`, where `n` is the power of two
    /// which follows `bits_stored` (or itself if it is a power of two).
    /// For instance, if `bits_stored` is 12, the output values
    /// will go from 0 to 65535.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `rescale`: the rescale parameters
    /// - `voi`: the value of interest (VOI) function and parameters
    ///
    /// # Panics
    ///
    /// Panics if `bits_stored` is 0 or too large.
    pub fn new_rescale_and_window(
        bits_stored: u16,
        signed: bool,
        rescale: Rescale,
        voi: WindowLevelTransform,
    ) -> Result<Self, CreateLutError> {
        let bits_allocated = (bits_stored as usize).next_power_of_two();
        let y_max = ((1 << bits_allocated) - 1) as f64;
        Self::new_with_fn(bits_stored, signed, |v| {
            let x = v as f64;
            let v = rescale.apply(x);
            voi.apply(v, y_max)
        })
    }

    /// Create a new LUT containing
    /// a VOI transformation defined by a window level.
    ///
    /// The amplitude of the output values
    /// goes from 0 to `2^n - 1`, where `n` is the power of two
    /// which follows `bits_stored` (or itself if it is a power of two).
    /// For instance, if `bits_stored` is 12, the output values
    /// will go from 0 to 65535.
    ///
    /// - `bits_stored`:
    ///   the number of bits effectively used to represent the sample values
    ///   (the _Bits Stored_ DICOM attribute)
    /// - `signed`:
    ///   whether the input sample values are expected to be signed
    ///   (_Pixel Representation_ = 1)
    /// - `voi`: the value of interest (VOI) function and parameters
    ///
    /// # Panics
    ///
    /// Panics if `bits_stored` is 0 or too large.
    pub fn new_window(
        bits_stored: u16,
        signed: bool,
        voi: WindowLevelTransform,
    ) -> Result<Self, CreateLutError> {
        let bits_allocated = (bits_stored as usize).next_power_of_two();
        let y_max = ((1 << bits_allocated) - 1) as f64;
        Self::new_with_fn(bits_stored, signed, |v| voi.apply(v as f64, y_max))
    }

    /// Apply the transformation to a single pixel sample value.
    ///
    /// Although the input is expected to be one of `u8`, `u16`, or `u32`,
    /// this method works for signed sample values as well,
    /// with the bits reinterpreted as their unsigned counterpart.
    ///
    /// # Panics
    ///
    /// Panics if `sample_value` is larger or equal to `2^bits_stored`.
    pub fn get<I: 'static>(&self, sample_value: I) -> T
    where
        I: Copy,
        I: Into<u32>,
    {
        let val = sample_value.into();
        let index = if self.signed {
            // adjust for signedness by masking out the extra sign bits
            let mask = self.table.len() - 1;
            val as usize & mask
        } else {
            val as usize
        };
        assert!((index as usize) < self.table.len());

        self.table[index as usize]
    }

    /// Adapts an iterator of pixel data sample values
    /// to an iterator of transformed values.
    pub fn map_iter<'a, I: 'static>(
        &'a self,
        iter: impl IntoIterator<Item = I> + 'a,
    ) -> impl Iterator<Item = T> + 'a
    where
        I: Copy,
        I: Into<u32>,
    {
        iter.into_iter().map(move |i| self.get(i))
    }

    /// Adapts a parallel iterator of pixel data sample values
    /// to a parallel iterator of transformed values.
    pub fn map_par_iter<'a, I: 'static>(
        &'a self,
        iter: impl ParallelIterator<Item = I> + 'a,
    ) -> impl ParallelIterator<Item = T> + 'a
    where
        I: Copy,
        I: Into<u32>,
    {
        iter.map(move |i| self.get(i))
    }
}

#[cfg(test)]
mod tests {
    use crate::{VoiLutFunction, WindowLevel};

    use super::*;

    /// Applying a common rescale function to a value
    /// gives the expected output.
    #[test]
    fn modality_lut_baseline_2() {
        let rescale = Rescale::new(2., -1024.);

        assert_eq!(rescale.apply(0.), -1024.);
        assert_eq!(rescale.apply(1.), -1022.);
        assert_eq!(rescale.apply(2.), -1020.);
        assert_eq!(rescale.apply(1024.), 1024.);
    }

    #[test]
    fn lut_signed_numbers() {
        // 10-bit precision input, signed output
        let lut: Lut<i16> = Lut::new_rescale(10, true, Rescale::new(2., -1024.)).unwrap();

        assert_eq!(lut.get(0 as u16), -1024);
        assert_eq!(lut.get(1 as u16), -1022);
        assert_eq!(lut.get(-1_i16 as u16), -1026);
        assert_eq!(lut.get(-2_i16 as u16), -1028);
        assert_eq!(lut.get(500 as u16), -24);
    }

    #[test]
    fn lut_rescale_and_window_16bit() {
        let bits_stored = 16;
        let lut = Lut::new_rescale_and_window(
            bits_stored,
            false,
            Rescale::new(1., -1024.),
            WindowLevelTransform::new(
                VoiLutFunction::Linear,
                WindowLevel {
                    width: 300.,
                    center: 50.,
                },
            ),
        )
        .unwrap();

        // < 0
        let val: u16 = lut.get(824_u16);
        assert_eq!(val, 0);

        // > 200
        let val: u16 = lut.get(1224_u16);
        assert_eq!(val, 65535);

        // around the middle

        let val: u16 = lut.get(1024_u16 + 50);
        let expected_range = 32_500..=33_000;
        assert!(
            expected_range.contains(&val),
            "outcome was {}, expected to be in {:?}",
            val,
            expected_range,
        );
    }
}
<|endoftext|>


<commit_before>use sfml::audio;

use error::Error;
use types::{PreparedPlaylistItem, SequenceData};
use utils;

#[derive(Clone, Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItem {
    pub path: Option<String>,
    pub music: Option<String>,
    pub duration: Option<u32>
}

impl PlaylistItem {
    pub fn new(
        path: Option<String>,
        music: Option<String>,
        duration: Option<u32>
    ) -> Result<PlaylistItem, Error> {

        if path.is_none() && music.is_none() && duration.is_none() {
            Err(Error::InvalidPlaylistItem("All values cannot be None".to_string()))
        } else {
            Ok(PlaylistItem {
                path: path,
                music: music,
                duration: duration
            })
        }
    }

    pub fn prepare(&self) -> Result<PreparedPlaylistItem, Error> {
        match self.path {
            Some(ref p) => match self.music {
                Some(ref m) => {
                    let data = try!(utils::load_sequence_data(p));
                    let music = match audio::Music::new_from_file(m) {
                        Some(mm) => mm,
                        None => return Err(Error::MusicError("Creating rsfml music object failed".to_string()))
                    };
                    Ok(PreparedPlaylistItem {
                        data: Some(data),
                        music: Some(music),
                        duration: self.duration
                    })
                },
                None => {
                    let data = try!(utils::load_sequence_data(p));
                    Ok(PreparedPlaylistItem {
                        data: Some(data),
                        music: None::<audio::Music>,
                        duration: self.duration
                    })
                }
            },
            None => match self.music {
                Some(ref m) => {
                    let music = match audio::Music::new_from_file(m) {
                        Some(mm) => mm,
                        None => return Err(Error::MusicError("Creating rsfml music object failed".to_string()))
                    };
                    Ok(PreparedPlaylistItem {
                        data: None::<SequenceData>,
                        music: Some(music),
                        duration: self.duration
                    })
                },
                None => {
                    Ok(PreparedPlaylistItem {
                        data: None::<SequenceData>,
                        music: None::<audio::Music>,
                        duration: self.duration
                    })
                }
            }
        }
    }
}
<commit_msg>Updated interface for sfml's read music function<commit_after>use sfml::audio;

use error::Error;
use types::{PreparedPlaylistItem, SequenceData};
use utils;

#[derive(Clone, Debug, RustcDecodable, RustcEncodable)]
pub struct PlaylistItem {
    pub path: Option<String>,
    pub music: Option<String>,
    pub duration: Option<u32>
}

impl PlaylistItem {
    pub fn new(
        path: Option<String>,
        music: Option<String>,
        duration: Option<u32>
    ) -> Result<PlaylistItem, Error> {

        if path.is_none() && music.is_none() && duration.is_none() {
            Err(Error::InvalidPlaylistItem("All values cannot be None".to_string()))
        } else {
            Ok(PlaylistItem {
                path: path,
                music: music,
                duration: duration
            })
        }
    }

    pub fn prepare(&self) -> Result<PreparedPlaylistItem, Error> {
        match self.path {
            Some(ref p) => match self.music {
                Some(ref m) => {
                    let data = try!(utils::load_sequence_data(p));
                    let music = match audio::Music::from_file(m) {
                        Some(mm) => mm,
                        None => return Err(Error::MusicError("Creating rsfml music object failed".to_string()))
                    };
                    Ok(PreparedPlaylistItem {
                        data: Some(data),
                        music: Some(music),
                        duration: self.duration
                    })
                },
                None => {
                    let data = try!(utils::load_sequence_data(p));
                    Ok(PreparedPlaylistItem {
                        data: Some(data),
                        music: None::<audio::Music>,
                        duration: self.duration
                    })
                }
            },
            None => match self.music {
                Some(ref m) => {
                    let music = match audio::Music::from_file(m) {
                        Some(mm) => mm,
                        None => return Err(Error::MusicError("Creating rsfml music object failed".to_string()))
                    };
                    Ok(PreparedPlaylistItem {
                        data: None::<SequenceData>,
                        music: Some(music),
                        duration: self.duration
                    })
                },
                None => {
                    Ok(PreparedPlaylistItem {
                        data: None::<SequenceData>,
                        music: None::<audio::Music>,
                        duration: self.duration
                    })
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>

//NEW
#[test]
#[ignore]
/// ending with whitespace
fn test_ending_with_whitespace() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Okay if like my  spacebar  quite a bit?   "); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// shouting numbers
fn test_shouting_numbers() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "1, 2, 3 GO!"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// other whitespace
fn test_other_whitespace() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "

 	"); hm}, "Fine. Be that way!");
}

//NEW
#[test]
#[ignore]
/// shouting with special characters
fn test_shouting_with_special_characters() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// talking forcefully
fn test_talking_forcefully() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Hi there!"); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// prattling on
fn test_prattling_on() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Wait! Hang on. Are you going to be OK?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// forceful question
fn test_forceful_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "WHAT'S GOING ON?"); hm}, "Calm down, I know what I'm doing!");
}

//NEW
#[test]
#[ignore]
/// shouting with no exclamation mark
fn test_shouting_with_no_exclamation_mark() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "I HATE THE DENTIST"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// asking gibberish
fn test_asking_gibberish() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "fffbbcbeab?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// question with no letters
fn test_question_with_no_letters() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "4?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// no letters
fn test_no_letters() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "1, 2, 3"); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// statement containing question mark
fn test_statement_containing_question_mark() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Ending with ? means a question."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// multiple line question
fn test_multiple_line_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "
Does this cryogenic chamber make me look fat?
No."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// non-question ending with whitespace
fn test_nonquestion_ending_with_whitespace() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "This is a statement ending with whitespace      "); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// shouting
fn test_shouting() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "WATCH OUT!"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// non-letters with question
fn test_nonletters_with_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", ":) ?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// stating something
fn test_stating_something() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Tom-ay-to, tom-aaaah-to."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// shouting gibberish
fn test_shouting_gibberish() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "FCECDFCAAB"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// asking a question
fn test_asking_a_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Does this cryogenic chamber make me look fat?"); hm}, "Sure.");
}

//NEW
/// Process a single test case for the property `response`
///
/// All cases for the `response` property are implemented
/// in terms of this function.
///
/// Note that you'll need to both name the expected transform which
/// the student needs to write, and name the types of the inputs and outputs.
/// While rustc _may_ be able to handle things properly given a working example,
/// students will face confusing errors if the `I` and `O` types are not concrete.
///
fn process_response_case<I, O>(input: I, expected: O) {
//  typical implementation:
//  assert_eq!(
//      student_response_func(input),
//      expected
//  )
    unimplemented!()
}

//NEW
#[test]
#[ignore]
/// asking a numeric question
fn test_asking_a_numeric_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "You are, what, like 15?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// silence
fn test_silence() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", ""); hm}, "Fine. Be that way!");
}

//NEW
#[test]
#[ignore]
/// starting with whitespace
fn test_starting_with_whitespace() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "         hmmmmmmm..."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// using acronyms in regular speech
fn test_using_acronyms_in_regular_speech() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "It's OK if you don't want to go work for NASA."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// alternate silence
fn test_alternate_silence() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "										"); hm}, "Fine. Be that way!");
}

//NEW
#[test]
#[ignore]
/// prolonged silence
fn test_prolonged_silence() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "          "); hm}, "Fine. Be that way!");
}

<commit_msg>replace bare CR with \r<commit_after>

//NEW
#[test]
#[ignore]
/// ending with whitespace
fn test_ending_with_whitespace() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Okay if like my  spacebar  quite a bit?   "); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// shouting numbers
fn test_shouting_numbers() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "1, 2, 3 GO!"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// other whitespace
fn test_other_whitespace() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "\r\r 	"); hm}, "Fine. Be that way!");
}

//NEW
#[test]
#[ignore]
/// shouting with special characters
fn test_shouting_with_special_characters() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// talking forcefully
fn test_talking_forcefully() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Hi there!"); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// prattling on
fn test_prattling_on() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Wait! Hang on. Are you going to be OK?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// forceful question
fn test_forceful_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "WHAT'S GOING ON?"); hm}, "Calm down, I know what I'm doing!");
}

//NEW
#[test]
#[ignore]
/// shouting with no exclamation mark
fn test_shouting_with_no_exclamation_mark() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "I HATE THE DENTIST"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// asking gibberish
fn test_asking_gibberish() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "fffbbcbeab?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// question with no letters
fn test_question_with_no_letters() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "4?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// no letters
fn test_no_letters() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "1, 2, 3"); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// statement containing question mark
fn test_statement_containing_question_mark() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Ending with ? means a question."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// multiple line question
fn test_multiple_line_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "
Does this cryogenic chamber make me look fat?
No."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// non-question ending with whitespace
fn test_nonquestion_ending_with_whitespace() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "This is a statement ending with whitespace      "); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// shouting
fn test_shouting() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "WATCH OUT!"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// non-letters with question
fn test_nonletters_with_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", ":) ?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// stating something
fn test_stating_something() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Tom-ay-to, tom-aaaah-to."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// shouting gibberish
fn test_shouting_gibberish() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "FCECDFCAAB"); hm}, "Whoa, chill out!");
}

//NEW
#[test]
#[ignore]
/// asking a question
fn test_asking_a_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "Does this cryogenic chamber make me look fat?"); hm}, "Sure.");
}

//NEW
/// Process a single test case for the property `response`
///
/// All cases for the `response` property are implemented
/// in terms of this function.
///
/// Note that you'll need to both name the expected transform which
/// the student needs to write, and name the types of the inputs and outputs.
/// While rustc _may_ be able to handle things properly given a working example,
/// students will face confusing errors if the `I` and `O` types are not concrete.
///
fn process_response_case<I, O>(input: I, expected: O) {
//  typical implementation:
//  assert_eq!(
//      student_response_func(input),
//      expected
//  )
    unimplemented!()
}

//NEW
#[test]
#[ignore]
/// asking a numeric question
fn test_asking_a_numeric_question() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "You are, what, like 15?"); hm}, "Sure.");
}

//NEW
#[test]
#[ignore]
/// silence
fn test_silence() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", ""); hm}, "Fine. Be that way!");
}

//NEW
#[test]
#[ignore]
/// starting with whitespace
fn test_starting_with_whitespace() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "         hmmmmmmm..."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// using acronyms in regular speech
fn test_using_acronyms_in_regular_speech() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "It's OK if you don't want to go work for NASA."); hm}, "Whatever.");
}

//NEW
#[test]
#[ignore]
/// alternate silence
fn test_alternate_silence() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "										"); hm}, "Fine. Be that way!");
}

//NEW
#[test]
#[ignore]
/// prolonged silence
fn test_prolonged_silence() {
process_response_case({let mut hm = ::std::collections::HashMap::new(); hm.insert("heyBob", "          "); hm}, "Fine. Be that way!");
}

<|endoftext|>


<commit_before>#![allow(unused_imports)]
#![allow(non_snake_case)]

use std::cmp::{max, min, Ordering};
use std::collections::*;
use std::fmt::{Debug, Formatter};
use std::io::*;
use std::ops::*;
use std::*;

// -----------------------------------------------
// Framework
// -----------------------------------------------

#[allow(dead_code)]
fn rl() -> String {
    let mut buf = String::new();
    io::stdin().read_line(&mut buf).unwrap();
    buf.trim_right().to_owned()
}

#[allow(unused_macros)]
macro_rules! read {
    ([$t:ty] ; $n:expr) =>
        ((0..$n).map(|_| read!([$t])).collect::<Vec<_>>());
    ($($t:ty),+ ; $n:expr) =>
        ((0..$n).map(|_| read!($($t),+)).collect::<Vec<_>>());
    ([$t:ty]) =>
        (rl().split_whitespace().map(|w| w.parse().unwrap()).collect::<Vec<$t>>());
    ($t:ty) =>
        (rl().parse::<$t>().unwrap());
    ($($t:ty),*) => {{
        let buf = rl();
        let mut w = buf.split_whitespace();
        ($(w.next().unwrap().parse::<$t>().unwrap()),*)
    }};
}

trait IteratorExt: Iterator + Sized {
    fn vec(self) -> Vec<Self::Item> {
        self.collect()
    }
}

impl<T: Iterator> IteratorExt for T {}

#[allow(unused_macros)]
macro_rules! debug {
    ($($arg:expr),*) => {
        #[cfg(debug_assertions)]
        {
            let es = &[$((&stringify!($arg) as &Debug, &$arg as &Debug)),*];
            let mut err = std::io::stderr();
            err.write_fmt(format_args!("{:?}\n", MyDebugMap(es))).unwrap();
        }
    };
}

#[allow(dead_code)]
struct MyDebugMap<'a>(&'a [(&'a Debug, &'a Debug)]);

impl<'a> Debug for MyDebugMap<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        let mut m = fmt.debug_map();
        for &(key, value) in self.0.iter() {
            m.entry(key, value);
        }
        m.finish()
    }
}

// -----------------------------------------------
// Solution
// -----------------------------------------------

pub fn main() {
    return;
}

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

    #[test]
    fn test_ok() {
        assert_eq!(7, 1 + 2 * 3);
    }
}
<commit_msg>Print debug output in pretty layout<commit_after>#![allow(unused_imports)]
#![allow(non_snake_case)]

use std::cmp::{max, min, Ordering};
use std::collections::*;
use std::fmt::{Debug, Formatter};
use std::io::*;
use std::ops::*;
use std::*;

// -----------------------------------------------
// Framework
// -----------------------------------------------

#[allow(dead_code)]
fn rl() -> String {
    let mut buf = String::new();
    io::stdin().read_line(&mut buf).unwrap();
    buf.trim_right().to_owned()
}

#[allow(unused_macros)]
macro_rules! read {
    ([$t:ty] ; $n:expr) =>
        ((0..$n).map(|_| read!([$t])).collect::<Vec<_>>());
    ($($t:ty),+ ; $n:expr) =>
        ((0..$n).map(|_| read!($($t),+)).collect::<Vec<_>>());
    ([$t:ty]) =>
        (rl().split_whitespace().map(|w| w.parse().unwrap()).collect::<Vec<$t>>());
    ($t:ty) =>
        (rl().parse::<$t>().unwrap());
    ($($t:ty),*) => {{
        let buf = rl();
        let mut w = buf.split_whitespace();
        ($(w.next().unwrap().parse::<$t>().unwrap()),*)
    }};
}

trait IteratorExt: Iterator + Sized {
    fn vec(self) -> Vec<Self::Item> {
        self.collect()
    }
}

impl<T: Iterator> IteratorExt for T {}

#[allow(unused_macros)]
macro_rules! debug {
    ($($arg:expr),*) => {
        #[cfg(debug_assertions)]
        {
            let es = &[$((&stringify!($arg) as &Debug, &$arg as &Debug)),*];
            let mut err = std::io::stderr();
            err.write_fmt(format_args!("{:#?}\n", MyDebugMap(es))).unwrap();
        }
    };
}

#[allow(dead_code)]
struct MyDebugMap<'a>(&'a [(&'a Debug, &'a Debug)]);

impl<'a> Debug for MyDebugMap<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        let mut m = fmt.debug_map();
        for &(key, value) in self.0.iter() {
            m.entry(key, value);
        }
        m.finish()
    }
}

// -----------------------------------------------
// Solution
// -----------------------------------------------

pub fn main() {
    return;
}

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

    #[test]
    fn test_ok() {
        assert_eq!(7, 1 + 2 * 3);
    }
}
<|endoftext|>


<commit_before>use super::command::{
    Help,
    HelpOptions,
    HelpFunction
};
pub use super::{
    Args,
    CommandGroup,
    CommandOptions,
    CommandError,
    HelpBehaviour
};

use utils::Colour;
use std::{
    fmt::Write,
    sync::Arc
};

pub struct CreateHelpCommand(pub HelpOptions, pub HelpFunction);

impl CreateHelpCommand {

    /// Sets a message displaying if input could not be found
    /// but a similar command is available.
    ///
    /// **Note**: `{}` will be substituted with the actual suggested command-name.
    /// Hence no `{}` results in no command-name.
    pub fn suggestion_text(mut self, text: &str) -> Self {
        self.0.suggestion_text = text.to_string();

        self
    }

    /// Sets a message displaying if there is no help available.
    pub fn no_help_available_text(mut self, text: &str) -> Self {
        self.0.no_help_available_text = text.to_string();

        self
    }

    /// Sets a label for usage of a command.
    pub fn usage_label(mut self, text: &str) -> Self {
        self.0.usage_label = text.to_string();

        self
    }

    /// Sets a label for the usage examples of a command.
    pub fn usage_sample_label(mut self, text: &str) -> Self {
        self.0.usage_sample_label = text.to_string();

        self
    }

    /// Sets a label for ungrouped-commands
    pub fn ungrouped_label(mut self, text: &str) -> Self {
        self.0.ungrouped_label = text.to_string();

        self
    }

    /// Sets a label for grouped-commands.
    pub fn grouped_label(mut self, text: &str) -> Self {
        self.0.grouped_label = text.to_string();

        self
    }

    /// Sets a label for aliases.
    pub fn aliases_label(mut self, text: &str) -> Self {
        self.0.aliases_label = text.to_string();

        self
    }

    /// Sets a message displaying if a command is only available
    /// in guilds.
    pub fn guild_only_text(mut self, text: &str) -> Self {
        self.0.guild_only_text = text.to_string();

        self
    }

    /// Sets a message displaying if a command is only available
    /// in direct messages (DMs);
    pub fn dm_only_text(mut self, text: &str) -> Self {
        self.0.dm_only_text = text.to_string();

        self
    }

    /// Sets a message displaying if a command is available in
    /// guilds and DMs.
    pub fn dm_and_guilds_text(mut self, text: &str) -> Self {
        self.0.dm_and_guild_text = text.to_string();

        self
    }

    /// Sets a message displaying if a command is available to use.
    pub fn available_text(mut self, text: &str) -> Self {
        self.0.available_text = text.to_string();

        self
    }

    /// Sets a message that will appear upon failing to find
    /// an individual command.
    /// As in: `{prefix}help {command_name}`, but a command or
    /// alias like `{command_name}` does not exist.
    ///
    /// **Note**: `{}` will be substituted with the actual suggested command-name.
    /// Hence no `{}` results in no command-name.
    pub fn command_not_found_text(mut self, text: &str) -> Self {
        self.0.command_not_found_text = text.to_string();

        self
    }

    /// Sets the message on top of the help-menu, informing the
    /// user how to obtain more information about a single command.
    pub fn individual_command_tip(mut self, text: &str) -> Self {
        self.0.individual_command_tip = text.to_string();

        self
    }

    /// Sets how the group-prexix shall be labeled.
    pub fn group_prefix(mut self, text: &str) -> Self {
        self.0.group_prefix = text.to_string();

        self
    }

    /// Sets how a command requiring roles, that a user is lacking,
    /// shall appear in the help-menu.
    pub fn lacking_role(mut self, behaviour: HelpBehaviour) -> Self {
        self.0.lacking_role = behaviour;

        self
    }

    /// Sets how a command requiring permission, that a user is lacking,
    /// shall be appear in the help-menu.
    pub fn lacking_permissions(mut self, behaviour: HelpBehaviour) -> Self {
        self.0.lacking_permissions = behaviour;

        self
    }

    /// Sets how a command requiring to be sent in either via DM
    /// or a guild should be treated in the help-menu.
    pub fn wrong_channel(mut self, behaviour: HelpBehaviour) -> Self {
        self.0.wrong_channel = behaviour;

        self
    }

    /// Sets the tip (or legend) explaining why some commands are striked,
    /// given text will be used in guilds and direct messages.
    ///
    /// By default this is `Some(String)` and the `String` is empty resulting
    /// in an automated substitution based on your `HelpBehaviour`-settings.
    /// If set to `None`, no tip will be given nor will it be substituted.
    /// If set to a non-empty `Some(String)`, the `String` will be displayed as tip.
    ///
    /// **Note**: [`CreateHelpCommand::striked_commands_tip_in_direct_message`] and
    /// [`CreateHelpCommand::striked_commands_tip_in_guild`] can specifically set this text
    /// for direct messages and guilds.
    ///
    /// [`CreateHelpCommand::striked_commands_tip_in_direct_message`]: #method.striked_commands_tip_in_direct_message
    /// [`CreateHelpCommand::striked_commands_tip_in_guild`]: #method.striked_commands_tip_in_guild
    pub fn striked_commands_tip(mut self, text: Option<String>) -> Self {
        self.0.striked_commands_tip_in_dm = text.clone();
        self.0.striked_commands_tip_in_guild = text;

        self
    }

    /// Sets the tip (or legend) explaining why some commands are striked,
    /// given text will be used in guilds.
    ///
    /// By default this is `Some(String)` and the `String` is empty resulting
    /// in an automated substitution based on your `HelpBehaviour`-settings.
    /// If set to `None`, no tip will be given nor will it be substituted.
    /// If set to a non-empty `Some(String)`, the `String` will be displayed as tip.
    pub fn striked_commands_tip_in_guild(mut self, text: Option<String>) -> Self {
        self.0.striked_commands_tip_in_guild = text;

        self
    }

    /// Sets the tip (or legend) explaining why some commands are striked,
    /// given text will be used in direct messages.
    ///
    /// By default this is `Some(String)` and the `String` is empty resulting
    /// in an automated substitution based on your `HelpBehaviour`-settings.
    /// If set to `None`, no tip will be given nor will it be substituted.
    /// If set to a non-empty `Some(String)`, the `String` will be displayed as tip.
    pub fn striked_commands_tip_in_direct_message(mut self, text: Option<String>) -> Self {
        self.0.striked_commands_tip_in_dm = text;

        self
    }

    /// Sets the colour for the embed if no error occured.
    pub fn embed_success_colour(mut self, colour: Colour) -> Self {
        self.0.embed_success_colour = colour;

        self
    }

    /// Sets the colour for the embed if an error occured.
    pub fn embed_error_colour(mut self, colour: Colour) -> Self {
        self.0.embed_error_colour = colour;

        self
    }

    fn produce_strike_text(&self, dm_or_guild: &str) -> String {
        let mut strike_text = String::from("~~`Strikethrough commands`~~ are unavailable because they");

        let mut concat_with_comma = if self.0.lacking_permissions == HelpBehaviour::Strike {
            let _ = write!(strike_text, " require permissions");
            true
        } else {
            false
        };

        if self.0.lacking_role == HelpBehaviour::Strike {

            if concat_with_comma {
                let _ = write!(strike_text, ", require a specific role");
            } else {
                let _ = write!(strike_text, " require a specific role");
                concat_with_comma = true;
            }
        }

        if self.0.wrong_channel == HelpBehaviour::Strike {

            if concat_with_comma {
                strike_text.push_str(&format!(", or are limited to {}", dm_or_guild));
            } else {
                strike_text.push_str(&format!(" are limited to {}", dm_or_guild));
            }
        }
        let _ = write!(strike_text, ".");
        strike_text
    }

    /// Finishes the creation of a help-command, returning `Help`.
    /// If `Some(String)` was set as `striked_commands_tip` and the `String` is empty,
    /// the creator will substitute content based on the `HelpBehaviour`-settings.
    pub(crate) fn finish(mut self) -> Arc<Help> {

        if &self.0.striked_commands_tip_in_dm == &Some(String::new()) {
            let strike_text = self.produce_strike_text("direct messages");
            self.0.striked_commands_tip_in_dm = Some(strike_text);
        }

        if self.0.striked_commands_tip_in_guild == Some(String::new()) {
            let strike_text = self.produce_strike_text("guild messages");
            self.0.striked_commands_tip_in_guild = Some(strike_text);
        }

        let CreateHelpCommand(options, function) = self;

        Arc::new(Help(function, Arc::new(options)))
    }
}
<commit_msg>do not show the strikethrough-tip If no HelpBehaviour is `Strike` (#321)<commit_after>use super::command::{
    Help,
    HelpOptions,
    HelpFunction
};
pub use super::{
    Args,
    CommandGroup,
    CommandOptions,
    CommandError,
    HelpBehaviour
};

use utils::Colour;
use std::{
    fmt::Write,
    sync::Arc
};

pub struct CreateHelpCommand(pub HelpOptions, pub HelpFunction);

impl CreateHelpCommand {

    /// Sets a message displaying if input could not be found
    /// but a similar command is available.
    ///
    /// **Note**: `{}` will be substituted with the actual suggested command-name.
    /// Hence no `{}` results in no command-name.
    pub fn suggestion_text(mut self, text: &str) -> Self {
        self.0.suggestion_text = text.to_string();

        self
    }

    /// Sets a message displaying if there is no help available.
    pub fn no_help_available_text(mut self, text: &str) -> Self {
        self.0.no_help_available_text = text.to_string();

        self
    }

    /// Sets a label for usage of a command.
    pub fn usage_label(mut self, text: &str) -> Self {
        self.0.usage_label = text.to_string();

        self
    }

    /// Sets a label for the usage examples of a command.
    pub fn usage_sample_label(mut self, text: &str) -> Self {
        self.0.usage_sample_label = text.to_string();

        self
    }

    /// Sets a label for ungrouped-commands
    pub fn ungrouped_label(mut self, text: &str) -> Self {
        self.0.ungrouped_label = text.to_string();

        self
    }

    /// Sets a label for grouped-commands.
    pub fn grouped_label(mut self, text: &str) -> Self {
        self.0.grouped_label = text.to_string();

        self
    }

    /// Sets a label for aliases.
    pub fn aliases_label(mut self, text: &str) -> Self {
        self.0.aliases_label = text.to_string();

        self
    }

    /// Sets a message displaying if a command is only available
    /// in guilds.
    pub fn guild_only_text(mut self, text: &str) -> Self {
        self.0.guild_only_text = text.to_string();

        self
    }

    /// Sets a message displaying if a command is only available
    /// in direct messages (DMs);
    pub fn dm_only_text(mut self, text: &str) -> Self {
        self.0.dm_only_text = text.to_string();

        self
    }

    /// Sets a message displaying if a command is available in
    /// guilds and DMs.
    pub fn dm_and_guilds_text(mut self, text: &str) -> Self {
        self.0.dm_and_guild_text = text.to_string();

        self
    }

    /// Sets a message displaying if a command is available to use.
    pub fn available_text(mut self, text: &str) -> Self {
        self.0.available_text = text.to_string();

        self
    }

    /// Sets a message that will appear upon failing to find
    /// an individual command.
    /// As in: `{prefix}help {command_name}`, but a command or
    /// alias like `{command_name}` does not exist.
    ///
    /// **Note**: `{}` will be substituted with the actual suggested command-name.
    /// Hence no `{}` results in no command-name.
    pub fn command_not_found_text(mut self, text: &str) -> Self {
        self.0.command_not_found_text = text.to_string();

        self
    }

    /// Sets the message on top of the help-menu, informing the
    /// user how to obtain more information about a single command.
    pub fn individual_command_tip(mut self, text: &str) -> Self {
        self.0.individual_command_tip = text.to_string();

        self
    }

    /// Sets how the group-prexix shall be labeled.
    pub fn group_prefix(mut self, text: &str) -> Self {
        self.0.group_prefix = text.to_string();

        self
    }

    /// Sets how a command requiring roles, that a user is lacking,
    /// shall appear in the help-menu.
    pub fn lacking_role(mut self, behaviour: HelpBehaviour) -> Self {
        self.0.lacking_role = behaviour;

        self
    }

    /// Sets how a command requiring permission, that a user is lacking,
    /// shall be appear in the help-menu.
    pub fn lacking_permissions(mut self, behaviour: HelpBehaviour) -> Self {
        self.0.lacking_permissions = behaviour;

        self
    }

    /// Sets how a command requiring to be sent in either via DM
    /// or a guild should be treated in the help-menu.
    pub fn wrong_channel(mut self, behaviour: HelpBehaviour) -> Self {
        self.0.wrong_channel = behaviour;

        self
    }

    /// Sets the tip (or legend) explaining why some commands are striked,
    /// given text will be used in guilds and direct messages.
    ///
    /// By default this is `Some(String)` and the `String` is empty resulting
    /// in an automated substitution based on your `HelpBehaviour`-settings.
    /// If set to `None`, no tip will be given nor will it be substituted.
    /// If set to a non-empty `Some(String)`, the `String` will be displayed as tip.
    ///
    /// **Note**: [`CreateHelpCommand::striked_commands_tip_in_direct_message`] and
    /// [`CreateHelpCommand::striked_commands_tip_in_guild`] can specifically set this text
    /// for direct messages and guilds.
    ///
    /// [`CreateHelpCommand::striked_commands_tip_in_direct_message`]: #method.striked_commands_tip_in_direct_message
    /// [`CreateHelpCommand::striked_commands_tip_in_guild`]: #method.striked_commands_tip_in_guild
    pub fn striked_commands_tip(mut self, text: Option<String>) -> Self {
        self.0.striked_commands_tip_in_dm = text.clone();
        self.0.striked_commands_tip_in_guild = text;

        self
    }

    /// Sets the tip (or legend) explaining why some commands are striked,
    /// given text will be used in guilds.
    ///
    /// By default this is `Some(String)` and the `String` is empty resulting
    /// in an automated substitution based on your `HelpBehaviour`-settings.
    /// If set to `None`, no tip will be given nor will it be substituted.
    /// If set to a non-empty `Some(String)`, the `String` will be displayed as tip.
    pub fn striked_commands_tip_in_guild(mut self, text: Option<String>) -> Self {
        self.0.striked_commands_tip_in_guild = text;

        self
    }

    /// Sets the tip (or legend) explaining why some commands are striked,
    /// given text will be used in direct messages.
    ///
    /// By default this is `Some(String)` and the `String` is empty resulting
    /// in an automated substitution based on your `HelpBehaviour`-settings.
    /// If set to `None`, no tip will be given nor will it be substituted.
    /// If set to a non-empty `Some(String)`, the `String` will be displayed as tip.
    pub fn striked_commands_tip_in_direct_message(mut self, text: Option<String>) -> Self {
        self.0.striked_commands_tip_in_dm = text;

        self
    }

    /// Sets the colour for the embed if no error occured.
    pub fn embed_success_colour(mut self, colour: Colour) -> Self {
        self.0.embed_success_colour = colour;

        self
    }

    /// Sets the colour for the embed if an error occured.
    pub fn embed_error_colour(mut self, colour: Colour) -> Self {
        self.0.embed_error_colour = colour;

        self
    }

    fn produce_strike_text(&self, dm_or_guild: &str) -> Option<String> {
        let mut strike_text = String::from("~~`Strikethrough commands`~~ are unavailable because they");
        let mut is_any_option_strike = false;

        let mut concat_with_comma = if self.0.lacking_permissions == HelpBehaviour::Strike {
            is_any_option_strike = true;
            let _ = write!(strike_text, " require permissions");

            true
        } else {
            false
        };

        if self.0.lacking_role == HelpBehaviour::Strike {
            is_any_option_strike = true;

            if concat_with_comma {
                let _ = write!(strike_text, ", require a specific role");
            } else {
                let _ = write!(strike_text, " require a specific role");
                concat_with_comma = true;
            }
        }

        if self.0.wrong_channel == HelpBehaviour::Strike {
            is_any_option_strike = true;

            if concat_with_comma {
                strike_text.push_str(&format!(", or are limited to {}", dm_or_guild));
            } else {
                strike_text.push_str(&format!(" are limited to {}", dm_or_guild));
            }
        }
        let _ = write!(strike_text, ".");

        if is_any_option_strike {
            Some(strike_text)
        } else {
            None
        }
    }

    /// Finishes the creation of a help-command, returning `Help`.
    /// If `Some(String)` was set as `striked_commands_tip` and the `String` is empty,
    /// the creator will substitute content based on the `HelpBehaviour`-settings.
    pub(crate) fn finish(mut self) -> Arc<Help> {

        if &self.0.striked_commands_tip_in_dm == &Some(String::new()) {
            self.0.striked_commands_tip_in_dm = self.produce_strike_text("direct messages");
        }

        if self.0.striked_commands_tip_in_guild == Some(String::new()) {
            self.0.striked_commands_tip_in_guild = self.produce_strike_text("guild messages");
        }

        let CreateHelpCommand(options, function) = self;

        Arc::new(Help(function, Arc::new(options)))
    }
}
<|endoftext|>