<commit_before>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Interactions with the file system.

use std::collections::HashMap;
use std::fmt;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;

use xi_rope::Rope;
use xi_rpc::RemoteError;

use tabs::BufferId;

#[cfg(feature = "notify")]
use tabs::OPEN_FILE_EVENT_TOKEN;
#[cfg(feature = "notify")]
use watcher::FileWatcher;

const UTF8_BOM: &str = "\u{feff}";

/// Tracks all state related to open files.
pub struct FileManager {
    open_files: HashMap<PathBuf, BufferId>,
    file_info: HashMap<BufferId, FileInfo>,
    /// A monitor of filesystem events, for things like reloading changed files.
    #[cfg(feature = "notify")]
    watcher: FileWatcher,
}

#[derive(Debug)]
pub struct FileInfo {
    pub encoding: CharacterEncoding,
    pub path: PathBuf,
    pub mod_time: Option<SystemTime>,
    pub has_changed: bool,
}

pub enum FileError {
    Io(io::Error, PathBuf),
    UnknownEncoding(PathBuf),
    HasChanged(PathBuf),
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
    Utf8,
    Utf8WithBom,
}

impl FileManager {
    #[cfg(feature = "notify")]
    pub fn new(watcher: FileWatcher) -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new(), watcher }
    }

    #[cfg(not(feature = "notify"))]
    pub fn new() -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new() }
    }

    #[cfg(feature = "notify")]
    pub fn watcher(&mut self) -> &mut FileWatcher {
        &mut self.watcher
    }

    pub fn get_info(&self, id: BufferId) -> Option<&FileInfo> {
        self.file_info.get(&id)
    }

    pub fn get_editor(&self, path: &Path) -> Option<BufferId> {
        self.open_files.get(path).cloned()
    }

    /// Returns `true` if this file is open and has changed on disk.
    /// This state is stashed.
    pub fn check_file(&mut self, path: &Path, id: BufferId) -> bool {
        if let Some(info) = self.file_info.get_mut(&id) {
            let mod_t = get_mod_time(path);
            if mod_t != info.mod_time {
                info.has_changed = true
            }
            return info.has_changed;
        }
        false
    }

    pub fn open(&mut self, path: &Path, id: BufferId) -> Result<Rope, FileError> {
        if !path.exists() {
            let _ = File::create(path).map_err(|e| FileError::Io(e, path.to_owned()))?;
        }

        let (rope, info) = try_load_file(path)?;

        self.open_files.insert(path.to_owned(), id);
        if self.file_info.insert(id, info).is_none() {
            #[cfg(feature = "notify")]
            self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        }
        Ok(rope)
    }

    pub fn close(&mut self, id: BufferId) {
        if let Some(info) = self.file_info.remove(&id) {
            self.open_files.remove(&info.path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&info.path, OPEN_FILE_EVENT_TOKEN);
        }
    }

    pub fn save(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let is_existing = self.file_info.contains_key(&id);
        if is_existing {
            self.save_existing(path, text, id)
        } else {
            self.save_new(path, text, id)
        }
    }

    fn save_new(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        try_save(path, text, CharacterEncoding::Utf8)
            .map_err(|e| FileError::Io(e, path.to_owned()))?;
        let info = FileInfo {
            encoding: CharacterEncoding::Utf8,
            path: path.to_owned(),
            mod_time: get_mod_time(path),
            has_changed: false,
        };
        self.open_files.insert(path.to_owned(), id);
        self.file_info.insert(id, info);
        #[cfg(feature = "notify")]
        self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        Ok(())
    }

    fn save_existing(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let prev_path = self.file_info[&id].path.clone();
        if prev_path != path {
            self.save_new(path, text, id)?;
            self.open_files.remove(&prev_path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&prev_path, OPEN_FILE_EVENT_TOKEN);
        } else if self.file_info[&id].has_changed {
            return Err(FileError::HasChanged(path.to_owned()));
        } else {
            let encoding = self.file_info[&id].encoding;
            try_save(path, text, encoding).map_err(|e| FileError::Io(e, path.to_owned()))?;
            self.file_info.get_mut(&id).unwrap().mod_time = get_mod_time(path);
        }
        Ok(())
    }
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
where
    P: AsRef<Path>,
{
    // TODO: support for non-utf8
    // it's arguable that the rope crate should have file loading functionality
    let mut f =
        File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
    let mod_time =
        f.metadata().map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?.modified().ok();
    let mut bytes = Vec::new();
    f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

    let encoding = CharacterEncoding::guess(&bytes);
    let rope = try_decode(bytes, encoding, path.as_ref())?;
    let info = FileInfo { encoding, mod_time, path: path.as_ref().to_owned(), has_changed: false };
    Ok((rope, info))
}

fn try_save(path: &Path, text: &Rope, encoding: CharacterEncoding) -> io::Result<()> {
    let tmp_extension = path
        .extension()
        .map_or_else(|| OsString::from("swp"), |ext| {
            let mut ext = ext.to_os_string();
            ext.push(".swp");
            ext
        });
    let tmp_path = &path.with_extension(tmp_extension);

    let mut f = File::create(tmp_path)?;
    match encoding {
        CharacterEncoding::Utf8WithBom => f.write_all(UTF8_BOM.as_bytes())?,
        CharacterEncoding::Utf8 => (),
    }

    for chunk in text.iter_chunks(..text.len()) {
        f.write_all(chunk.as_bytes())?;
    }

    fs::rename(tmp_path, path)?;

    Ok(())
}

fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<Rope, FileError> {
    match encoding {
        CharacterEncoding::Utf8 => Ok(Rope::from(
            str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
        )),
        CharacterEncoding::Utf8WithBom => {
            let s = String::from_utf8(bytes)
                .map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
            Ok(Rope::from(&s[UTF8_BOM.len()..]))
        }
    }
}

impl CharacterEncoding {
    fn guess(s: &[u8]) -> Self {
        if s.starts_with(UTF8_BOM.as_bytes()) {
            CharacterEncoding::Utf8WithBom
        } else {
            CharacterEncoding::Utf8
        }
    }
}

/// Returns the modification timestamp for the file at a given path,
/// if present.
fn get_mod_time<P>(path: P) -> Option<SystemTime>
where
    P: AsRef<Path>,
{
    File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
}

impl From<FileError> for RemoteError {
    fn from(src: FileError) -> RemoteError {
        //TODO: when we migrate to using the failure crate for error handling,
        // this should return a better message
        let code = src.error_code();
        let message = src.to_string();
        RemoteError::custom(code, message, None)
    }
}

impl FileError {
    fn error_code(&self) -> i64 {
        match self {
            FileError::Io(_, _) => 5,
            FileError::UnknownEncoding(_) => 6,
            FileError::HasChanged(_) => 7,
        }
    }
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::Io(ref e, ref p) => write!(f, "{}. File path: {:?}", e, p),
            FileError::UnknownEncoding(ref p) => write!(f, "Error decoding file: {:?}", p),
            FileError::HasChanged(ref p) => write!(
                f,
                "File has changed on disk. \
                 Please save elsewhere and reload the file. File path: {:?}",
                p
            ),
        }
    }
}
<commit_msg>Rustfmt<commit_after>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Interactions with the file system.

use std::collections::HashMap;
use std::ffi::OsString;
use std::fmt;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;

use xi_rope::Rope;
use xi_rpc::RemoteError;

use tabs::BufferId;

#[cfg(feature = "notify")]
use tabs::OPEN_FILE_EVENT_TOKEN;
#[cfg(feature = "notify")]
use watcher::FileWatcher;

const UTF8_BOM: &str = "\u{feff}";

/// Tracks all state related to open files.
pub struct FileManager {
    open_files: HashMap<PathBuf, BufferId>,
    file_info: HashMap<BufferId, FileInfo>,
    /// A monitor of filesystem events, for things like reloading changed files.
    #[cfg(feature = "notify")]
    watcher: FileWatcher,
}

#[derive(Debug)]
pub struct FileInfo {
    pub encoding: CharacterEncoding,
    pub path: PathBuf,
    pub mod_time: Option<SystemTime>,
    pub has_changed: bool,
}

pub enum FileError {
    Io(io::Error, PathBuf),
    UnknownEncoding(PathBuf),
    HasChanged(PathBuf),
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
    Utf8,
    Utf8WithBom,
}

impl FileManager {
    #[cfg(feature = "notify")]
    pub fn new(watcher: FileWatcher) -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new(), watcher }
    }

    #[cfg(not(feature = "notify"))]
    pub fn new() -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new() }
    }

    #[cfg(feature = "notify")]
    pub fn watcher(&mut self) -> &mut FileWatcher {
        &mut self.watcher
    }

    pub fn get_info(&self, id: BufferId) -> Option<&FileInfo> {
        self.file_info.get(&id)
    }

    pub fn get_editor(&self, path: &Path) -> Option<BufferId> {
        self.open_files.get(path).cloned()
    }

    /// Returns `true` if this file is open and has changed on disk.
    /// This state is stashed.
    pub fn check_file(&mut self, path: &Path, id: BufferId) -> bool {
        if let Some(info) = self.file_info.get_mut(&id) {
            let mod_t = get_mod_time(path);
            if mod_t != info.mod_time {
                info.has_changed = true
            }
            return info.has_changed;
        }
        false
    }

    pub fn open(&mut self, path: &Path, id: BufferId) -> Result<Rope, FileError> {
        if !path.exists() {
            let _ = File::create(path).map_err(|e| FileError::Io(e, path.to_owned()))?;
        }

        let (rope, info) = try_load_file(path)?;

        self.open_files.insert(path.to_owned(), id);
        if self.file_info.insert(id, info).is_none() {
            #[cfg(feature = "notify")]
            self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        }
        Ok(rope)
    }

    pub fn close(&mut self, id: BufferId) {
        if let Some(info) = self.file_info.remove(&id) {
            self.open_files.remove(&info.path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&info.path, OPEN_FILE_EVENT_TOKEN);
        }
    }

    pub fn save(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let is_existing = self.file_info.contains_key(&id);
        if is_existing {
            self.save_existing(path, text, id)
        } else {
            self.save_new(path, text, id)
        }
    }

    fn save_new(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        try_save(path, text, CharacterEncoding::Utf8)
            .map_err(|e| FileError::Io(e, path.to_owned()))?;
        let info = FileInfo {
            encoding: CharacterEncoding::Utf8,
            path: path.to_owned(),
            mod_time: get_mod_time(path),
            has_changed: false,
        };
        self.open_files.insert(path.to_owned(), id);
        self.file_info.insert(id, info);
        #[cfg(feature = "notify")]
        self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        Ok(())
    }

    fn save_existing(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let prev_path = self.file_info[&id].path.clone();
        if prev_path != path {
            self.save_new(path, text, id)?;
            self.open_files.remove(&prev_path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&prev_path, OPEN_FILE_EVENT_TOKEN);
        } else if self.file_info[&id].has_changed {
            return Err(FileError::HasChanged(path.to_owned()));
        } else {
            let encoding = self.file_info[&id].encoding;
            try_save(path, text, encoding).map_err(|e| FileError::Io(e, path.to_owned()))?;
            self.file_info.get_mut(&id).unwrap().mod_time = get_mod_time(path);
        }
        Ok(())
    }
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
where
    P: AsRef<Path>,
{
    // TODO: support for non-utf8
    // it's arguable that the rope crate should have file loading functionality
    let mut f =
        File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
    let mod_time =
        f.metadata().map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?.modified().ok();
    let mut bytes = Vec::new();
    f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

    let encoding = CharacterEncoding::guess(&bytes);
    let rope = try_decode(bytes, encoding, path.as_ref())?;
    let info = FileInfo { encoding, mod_time, path: path.as_ref().to_owned(), has_changed: false };
    Ok((rope, info))
}

fn try_save(path: &Path, text: &Rope, encoding: CharacterEncoding) -> io::Result<()> {
    let tmp_extension = path.extension().map_or_else(
        || OsString::from("swp"),
        |ext| {
            let mut ext = ext.to_os_string();
            ext.push(".swp");
            ext
        },
    );
    let tmp_path = &path.with_extension(tmp_extension);

    let mut f = File::create(tmp_path)?;
    match encoding {
        CharacterEncoding::Utf8WithBom => f.write_all(UTF8_BOM.as_bytes())?,
        CharacterEncoding::Utf8 => (),
    }

    for chunk in text.iter_chunks(..text.len()) {
        f.write_all(chunk.as_bytes())?;
    }

    fs::rename(tmp_path, path)?;

    Ok(())
}

fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<Rope, FileError> {
    match encoding {
        CharacterEncoding::Utf8 => Ok(Rope::from(
            str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
        )),
        CharacterEncoding::Utf8WithBom => {
            let s = String::from_utf8(bytes)
                .map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
            Ok(Rope::from(&s[UTF8_BOM.len()..]))
        }
    }
}

impl CharacterEncoding {
    fn guess(s: &[u8]) -> Self {
        if s.starts_with(UTF8_BOM.as_bytes()) {
            CharacterEncoding::Utf8WithBom
        } else {
            CharacterEncoding::Utf8
        }
    }
}

/// Returns the modification timestamp for the file at a given path,
/// if present.
fn get_mod_time<P>(path: P) -> Option<SystemTime>
where
    P: AsRef<Path>,
{
    File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
}

impl From<FileError> for RemoteError {
    fn from(src: FileError) -> RemoteError {
        //TODO: when we migrate to using the failure crate for error handling,
        // this should return a better message
        let code = src.error_code();
        let message = src.to_string();
        RemoteError::custom(code, message, None)
    }
}

impl FileError {
    fn error_code(&self) -> i64 {
        match self {
            FileError::Io(_, _) => 5,
            FileError::UnknownEncoding(_) => 6,
            FileError::HasChanged(_) => 7,
        }
    }
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::Io(ref e, ref p) => write!(f, "{}. File path: {:?}", e, p),
            FileError::UnknownEncoding(ref p) => write!(f, "Error decoding file: {:?}", p),
            FileError::HasChanged(ref p) => write!(
                f,
                "File has changed on disk. \
                 Please save elsewhere and reload the file. File path: {:?}",
                p
            ),
        }
    }
}
<|endoftext|>


<commit_before>use build::Arg;
use std::collections::hash_map;
use std::collections::hash_map::DefaultHasher;
use std::collections::{HashMap, HashSet};
use std::ffi::OsString;
use std::hash::{Hash, Hasher};
use std::slice;
// ! rustdoc

#[derive(Default, PartialEq, Debug, Clone)]
pub struct MKeyMap<T> {
    keys: HashMap<KeyType, usize>,
    value_index: Vec<T>,
    values: HashMap<u64, HashSet<usize>>,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum KeyType {
    Short(char),
    Long(OsString),
    Position(u64),
}

impl KeyType {
    pub(crate) fn is_position(&self) -> bool {
        match *self {
            KeyType::Position(_) => true,
            _ => false,
        }
    }
    pub(crate) fn is_short(&self) -> bool {
        match *self {
            KeyType::Short(_) => true,
            _ => false,
        }
    }
    pub(crate) fn is_long(&self) -> bool {
        match *self {
            KeyType::Long(_) => true,
            _ => false,
        }
    }
}

impl<T> MKeyMap<T>
where
    T: Sized + Hash + PartialEq + Default + Eq,
{
    pub fn new() -> Self { MKeyMap::default() }
    //TODO ::from(x), ::with_capacity(n) etc
    //? set theory ops?

    pub fn insert(&mut self, key: KeyType, value: T) -> usize {
        let index = self.push(value);
        self.keys.insert(key, index);
        index
    }

    pub fn push(&mut self, value: T) -> usize {
        let index;
        let mut hasher = DefaultHasher::new();

        value.hash(&mut hasher);

        let hash = hasher.finish();

        if let Some((idx, _)) = self.values.get(&hash).and_then(|ids| {
            ids.iter()
                .map(|&x| (x, &self.value_index[x]))
                .find(|(_i, x)| x == &&value)
        }) {
            debug_assert!(false, "Non-unique value found");
            index = idx;
        } else {
            self.value_index.push(value);
            index = self.value_index.len() - 1;
            self.values
                .entry(hash)
                .and_modify(|x| {
                    x.insert(index);
                }).or_insert({
                    let mut set = HashSet::new();
                    set.insert(index);
                    set
                });
        }

        index
    }
    //TODO ::push_many([x, y])

    pub fn insert_key(&mut self, key: KeyType, index: usize) {
        if index >= self.values.len() {
            panic!("Index out of bounds");
        }

        self.keys.insert(key, index);
    }
    //TODO ::insert_keyset([Long, Key2])

    // ! Arg mutation functionality

    pub fn get(&self, key: KeyType) -> Option<&T> {
        self.keys
            .get(&key)
            .and_then(|&idx| self.value_index.get(idx))
    }
    //TODO ::get_first([KeyA, KeyB])

    pub fn get_mut(&mut self, key: KeyType) -> Option<&mut T> {
        if let Some(&idx) = self.keys.get(&key) {
            self.value_index.get_mut(idx)
        } else {
            None
        }
    }

    pub fn is_empty(&self) -> bool { self.keys.is_empty() && self.values.is_empty() }

    pub fn remove_by_name(&mut self, _name: &str) -> Option<T> { unimplemented!() }

    pub fn remove(&mut self, _key: KeyType) -> Option<T> { unimplemented!() }
    //TODO ::remove_many([KeyA, KeyB])
    //? probably shouldn't add a possibility for removal?
    //? or remove by replacement by some dummy object, so the order is preserved

    pub fn remove_key(&mut self, key: KeyType) { self.keys.remove(&key); }
    //TODO ::remove_keys([KeyA, KeyB])

    pub fn keys(&self) -> Keys<usize> {
        Keys {
            iter: self.keys.keys(),
        }
    }

    pub fn values(&self) -> Values<T> {
        Values {
            iter: self.value_index.iter(),
        }
    }

    pub fn values_mut(&mut self) -> ValuesMut<T> {
        ValuesMut {
            iter: self.value_index.iter_mut(),
        }
    }

    pub fn iter(&self) -> Iter<T> {
        Iter {
            map: self,
            keys: self.keys(),
        }
    }
}

impl<'a, 'b> MKeyMap<Arg<'a, 'b>> {
    pub fn insert_key_by_name(&mut self, key: KeyType, name: &str) {
        let index = self.find_by_name(name);

        self.keys.insert(key, index);
    }

    pub fn make_entries(&mut self, arg: Arg<'a, 'b>) -> usize {
        let short = arg.short.map(|c| KeyType::Short(c));
        let positional = arg.index.map(|n| KeyType::Position(n));

        let mut longs = arg
            .aliases
            .clone()
            .map(|v| {
                v.iter()
                    .map(|(n, _)| KeyType::Long(OsString::from(n)))
                    .collect()
            }).unwrap_or(Vec::new());

        longs.extend(arg.long.map(|l| KeyType::Long(OsString::from(l))));

        let index = self.push(arg);
        short.map(|s| self.insert_key(s, index));
        positional.map(|p| self.insert_key(p, index));
        longs.into_iter().map(|l| self.insert_key(l, index)).count();

        index
    }

    pub fn make_entries_by_index(&mut self, index: usize) {
        let short;
        let positional;
        let mut longs;

        {
            let arg = &self.value_index[index];
            short = arg.short.map(|c| KeyType::Short(c));
            positional = arg.index.map(|n| KeyType::Position(n));

            longs = arg
                .aliases
                .clone()
                .map(|v| {
                    v.iter()
                        .map(|(n, _)| KeyType::Long(OsString::from(n)))
                        .collect()
                }).unwrap_or(Vec::new());
            longs.extend(arg.long.map(|l| KeyType::Long(OsString::from(l))));
        }

        short.map(|s| self.insert_key(s, index));
        positional.map(|p| self.insert_key(p, index));
        longs.into_iter().map(|l| self.insert_key(l, index)).count();
    }

    pub fn mut_arg<F>(&mut self, name: &str, f: F)
    where
        F: FnOnce(Arg<'a, 'b>) -> Arg<'a, 'b>,
    {
        let index = self.find_by_name(name);
        let new_arg = f(self.value_index[index].clone());

        let value_key = self
            .values
            .iter()
            .filter(|(_, v)| v.contains(&index))
            .map(|(k, _)| k)
            .next()
            .map(|&x| x);
        value_key.map(|k| {
            self.values.entry(k).and_modify(|v| {
                v.remove(&index);
            })
        });

        let mut hasher = DefaultHasher::new();

        new_arg.hash(&mut hasher);

        let hash = hasher.finish();
        self.values
            .entry(hash)
            .and_modify(|x| {
                x.insert(index);
            }).or_insert({
                let mut set = HashSet::new();
                set.insert(index);
                set
            });

        self.value_index.push(new_arg);
        self.value_index.swap_remove(index);
        self.make_entries_by_index(index);
    }

    pub fn find_by_name(&mut self, name: &str) -> usize {
        self.value_index
            .iter()
            .position(|x| x.name == name)
            .expect("No such name found")
    }
}

#[derive(Debug)]
pub struct Keys<'a, V: 'a> {
    iter: hash_map::Keys<'a, KeyType, V>,
}

impl<'a, V> Iterator for Keys<'a, V> {
    type Item = &'a KeyType;

    fn next(&mut self) -> Option<Self::Item> { self.iter.next() }
}

#[derive(Debug)]
pub struct Values<'a, V: 'a> {
    iter: slice::Iter<'a, V>,
}

impl<'a, V> Iterator for Values<'a, V> {
    type Item = &'a V;

    fn next(&mut self) -> Option<Self::Item> { self.iter.next() }
}

#[derive(Debug)]
pub struct ValuesMut<'a, V: 'a> {
    iter: slice::IterMut<'a, V>,
}

impl<'a, V> Iterator for ValuesMut<'a, V> {
    type Item = &'a mut V;

    fn next(&mut self) -> Option<Self::Item> { self.iter.next() }
}

#[derive(Debug)]
pub struct Iter<'c, T>
where
    T: 'c,
{
    map: &'c MKeyMap<T>,
    keys: Keys<'c, usize>,
}

impl<'c, T> Iterator for Iter<'c, T>
where
    T: 'c + Sized + Hash + PartialEq + Default + Eq,
{
    type Item = (&'c KeyType, &'c T);

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(key) = self.keys.next() {
            Some((key, self.map.get(key.clone()).unwrap()))
        } else {
            None
        }
    }
}

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

    #[test]
    fn get_some_value() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        assert_eq!(
            map.get(Long(OsString::from("One"))),
            Some(&Arg::with_name("Value1"))
        );
    }

    #[test]
    fn get_none_value() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
        map.get(Long(OsString::from("Two")));

        assert_eq!(map.get(Long(OsString::from("Two"))), None);
    }

    //    #[test]
    //    fn insert_delete_value() {
    //        let mut map = MKeyMap::new();
    //        map.insert("One", clap::Arg::with_name("Value1"));
    //        assert_eq!(map.remove("One"), Some(clap::Arg::with_name("Value1")));
    //        assert!(map.is_empty());
    //    }

    #[test]
    fn insert_duplicate_key() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        assert_eq!(
            map.insert(Long(OsString::from("One")), Arg::with_name("Value2")),
            1
        );
    }

    #[test]
    fn insert_duplicate_value() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        let orig_len = map.values.len();

        map.insert(Long(OsString::from("Two")), Arg::with_name("Value1"));

        assert_eq!(map.values.len(), orig_len);
        assert_eq!(
            map.get(Long(OsString::from("One"))),
            map.get(Long(OsString::from("Two")))
        );
    }

    //    #[test]
    //    fn insert_delete_none() {
    //        let mut map = MKeyMap::new();
    //        map.insert("One", clap::Arg::with_name("Value1"));
    //        assert_eq!(map.remove("Two"), None);
    //        assert!(!map.is_empty());
    //        assert_eq!(map.get("One"), Some(clap::Arg::with_name("Value1")));
    //    }

    #[test]
    fn insert_multiple_keys() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();
        let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        map.insert_key(Long(OsString::from("Two")), index);

        assert_eq!(
            map.get(Long(OsString::from("One"))),
            map.get(Long(OsString::from("Two")))
        );
        assert_eq!(map.values.len(), 1);
    }

    // #[test]
    // fn insert_by_name() {
    //     let mut map: MKeyMap<Arg> = MKeyMap::new();
    //     let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

    //     map.insert_key_by_name(Long(OsString::from("Two")), "Value1");

    //     assert_eq!(
    //         map.get(Long(OsString::from("One"))),
    //         map.get(Long(OsString::from("Two")))
    //     );
    //     assert_eq!(map.values.len(), 1);
    // }

    #[test]
    fn get_mutable() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        assert_eq!(
            map.get_mut(Long(OsString::from("One"))),
            Some(&mut Arg::with_name("Value1"))
        );
    }

    #[test]
    fn remove_key() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();
        let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        map.insert_key(Long(OsString::from("Two")), index);
        map.remove_key(Long(OsString::from("One")));

        assert_eq!(map.keys.len(), 1);
        assert_eq!(map.values.len(), 1);
    }

    #[test]
    fn iter_keys() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
        map.insert(Long(OsString::from("Two")), Arg::with_name("Value2"));
        map.insert(Position(1), Arg::with_name("Value1"));

        let iter = map.keys().cloned();
        let mut ground_truth = HashSet::new();

        ground_truth.insert(Long(OsString::from("One")));
        ground_truth.insert(Long(OsString::from("Two")));
        ground_truth.insert(Position(1));

        assert_eq!(
            ground_truth.symmetric_difference(&iter.collect()).count(),
            0
        );
    }
}
<commit_msg>tests: fixes mkeymap tests<commit_after>use build::Arg;
use std::collections::hash_map;
use std::collections::hash_map::DefaultHasher;
use std::collections::{HashMap, HashSet};
use std::ffi::OsString;
use std::hash::{Hash, Hasher};
use std::slice;
// ! rustdoc

#[derive(Default, PartialEq, Debug, Clone)]
pub struct MKeyMap<T> {
    keys: HashMap<KeyType, usize>,
    value_index: Vec<T>,
    values: HashMap<u64, HashSet<usize>>,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum KeyType {
    Short(char),
    Long(OsString),
    Position(u64),
}

impl KeyType {
    pub(crate) fn is_position(&self) -> bool {
        match *self {
            KeyType::Position(_) => true,
            _ => false,
        }
    }
    pub(crate) fn is_short(&self) -> bool {
        match *self {
            KeyType::Short(_) => true,
            _ => false,
        }
    }
    pub(crate) fn is_long(&self) -> bool {
        match *self {
            KeyType::Long(_) => true,
            _ => false,
        }
    }
}

impl<T> MKeyMap<T>
where
    T: Sized + Hash + PartialEq + Default + Eq,
{
    pub fn new() -> Self { MKeyMap::default() }
    //TODO ::from(x), ::with_capacity(n) etc
    //? set theory ops?

    pub fn insert(&mut self, key: KeyType, value: T) -> usize {
        let index = self.push(value);
        self.keys.insert(key, index);
        index
    }

    pub fn push(&mut self, value: T) -> usize {
        let index;
        let mut hasher = DefaultHasher::new();

        value.hash(&mut hasher);

        let hash = hasher.finish();

        if let Some((idx, _)) = self.values.get(&hash).and_then(|ids| {
            ids.iter()
                .map(|&x| (x, &self.value_index[x]))
                .find(|(_i, x)| x == &&value)
        }) {
            debug_assert!(false, "Non-unique value found");
            index = idx;
        } else {
            self.value_index.push(value);
            index = self.value_index.len() - 1;
            self.values
                .entry(hash)
                .and_modify(|x| {
                    x.insert(index);
                }).or_insert({
                    let mut set = HashSet::new();
                    set.insert(index);
                    set
                });
        }

        index
    }
    //TODO ::push_many([x, y])

    pub fn insert_key(&mut self, key: KeyType, index: usize) {
        if index >= self.values.len() {
            panic!("Index out of bounds");
        }

        self.keys.insert(key, index);
    }
    //TODO ::insert_keyset([Long, Key2])

    // ! Arg mutation functionality

    pub fn get(&self, key: KeyType) -> Option<&T> {
        self.keys
            .get(&key)
            .and_then(|&idx| self.value_index.get(idx))
    }
    //TODO ::get_first([KeyA, KeyB])

    pub fn get_mut(&mut self, key: KeyType) -> Option<&mut T> {
        if let Some(&idx) = self.keys.get(&key) {
            self.value_index.get_mut(idx)
        } else {
            None
        }
    }

    pub fn is_empty(&self) -> bool { self.keys.is_empty() && self.values.is_empty() }

    pub fn remove_by_name(&mut self, _name: &str) -> Option<T> { unimplemented!() }

    pub fn remove(&mut self, _key: KeyType) -> Option<T> { unimplemented!() }
    //TODO ::remove_many([KeyA, KeyB])
    //? probably shouldn't add a possibility for removal?
    //? or remove by replacement by some dummy object, so the order is preserved

    pub fn remove_key(&mut self, key: KeyType) { self.keys.remove(&key); }
    //TODO ::remove_keys([KeyA, KeyB])

    pub fn keys(&self) -> Keys<usize> {
        Keys {
            iter: self.keys.keys(),
        }
    }

    pub fn values(&self) -> Values<T> {
        Values {
            iter: self.value_index.iter(),
        }
    }

    pub fn values_mut(&mut self) -> ValuesMut<T> {
        ValuesMut {
            iter: self.value_index.iter_mut(),
        }
    }

    pub fn iter(&self) -> Iter<T> {
        Iter {
            map: self,
            keys: self.keys(),
        }
    }
}

impl<'a, 'b> MKeyMap<Arg<'a, 'b>> {
    pub fn insert_key_by_name(&mut self, key: KeyType, name: &str) {
        let index = self.find_by_name(name);

        self.keys.insert(key, index);
    }

    pub fn make_entries(&mut self, arg: Arg<'a, 'b>) -> usize {
        let short = arg.short.map(|c| KeyType::Short(c));
        let positional = arg.index.map(|n| KeyType::Position(n));

        let mut longs = arg
            .aliases
            .clone()
            .map(|v| {
                v.iter()
                    .map(|(n, _)| KeyType::Long(OsString::from(n)))
                    .collect()
            }).unwrap_or(Vec::new());

        longs.extend(arg.long.map(|l| KeyType::Long(OsString::from(l))));

        let index = self.push(arg);
        short.map(|s| self.insert_key(s, index));
        positional.map(|p| self.insert_key(p, index));
        longs.into_iter().map(|l| self.insert_key(l, index)).count();

        index
    }

    pub fn make_entries_by_index(&mut self, index: usize) {
        let short;
        let positional;
        let mut longs;

        {
            let arg = &self.value_index[index];
            short = arg.short.map(|c| KeyType::Short(c));
            positional = arg.index.map(|n| KeyType::Position(n));

            longs = arg
                .aliases
                .clone()
                .map(|v| {
                    v.iter()
                        .map(|(n, _)| KeyType::Long(OsString::from(n)))
                        .collect()
                }).unwrap_or(Vec::new());
            longs.extend(arg.long.map(|l| KeyType::Long(OsString::from(l))));
        }

        short.map(|s| self.insert_key(s, index));
        positional.map(|p| self.insert_key(p, index));
        longs.into_iter().map(|l| self.insert_key(l, index)).count();
    }

    pub fn mut_arg<F>(&mut self, name: &str, f: F)
    where
        F: FnOnce(Arg<'a, 'b>) -> Arg<'a, 'b>,
    {
        let index = self.find_by_name(name);
        let new_arg = f(self.value_index[index].clone());

        let value_key = self
            .values
            .iter()
            .filter(|(_, v)| v.contains(&index))
            .map(|(k, _)| k)
            .next()
            .map(|&x| x);
        value_key.map(|k| {
            self.values.entry(k).and_modify(|v| {
                v.remove(&index);
            })
        });

        let mut hasher = DefaultHasher::new();

        new_arg.hash(&mut hasher);

        let hash = hasher.finish();
        self.values
            .entry(hash)
            .and_modify(|x| {
                x.insert(index);
            }).or_insert({
                let mut set = HashSet::new();
                set.insert(index);
                set
            });

        self.value_index.push(new_arg);
        self.value_index.swap_remove(index);
        self.make_entries_by_index(index);
    }

    pub fn find_by_name(&mut self, name: &str) -> usize {
        self.value_index
            .iter()
            .position(|x| x.name == name)
            .expect("No such name found")
    }
}

#[derive(Debug)]
pub struct Keys<'a, V: 'a> {
    iter: hash_map::Keys<'a, KeyType, V>,
}

impl<'a, V> Iterator for Keys<'a, V> {
    type Item = &'a KeyType;

    fn next(&mut self) -> Option<Self::Item> { self.iter.next() }
}

#[derive(Debug)]
pub struct Values<'a, V: 'a> {
    iter: slice::Iter<'a, V>,
}

impl<'a, V> Iterator for Values<'a, V> {
    type Item = &'a V;

    fn next(&mut self) -> Option<Self::Item> { self.iter.next() }
}

#[derive(Debug)]
pub struct ValuesMut<'a, V: 'a> {
    iter: slice::IterMut<'a, V>,
}

impl<'a, V> Iterator for ValuesMut<'a, V> {
    type Item = &'a mut V;

    fn next(&mut self) -> Option<Self::Item> { self.iter.next() }
}

#[derive(Debug)]
pub struct Iter<'c, T>
where
    T: 'c,
{
    map: &'c MKeyMap<T>,
    keys: Keys<'c, usize>,
}

impl<'c, T> Iterator for Iter<'c, T>
where
    T: 'c + Sized + Hash + PartialEq + Default + Eq,
{
    type Item = (&'c KeyType, &'c T);

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(key) = self.keys.next() {
            Some((key, self.map.get(key.clone()).unwrap()))
        } else {
            None
        }
    }
}

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

    #[test]
    fn get_some_value() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        assert_eq!(
            map.get(Long(OsString::from("One"))),
            Some(&Arg::with_name("Value1"))
        );
    }

    #[test]
    fn get_none_value() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
        map.get(Long(OsString::from("Two")));

        assert_eq!(map.get(Long(OsString::from("Two"))), None);
    }

    //    #[test]
    //    fn insert_delete_value() {
    //        let mut map = MKeyMap::new();
    //        map.insert("One", clap::Arg::with_name("Value1"));
    //        assert_eq!(map.remove("One"), Some(clap::Arg::with_name("Value1")));
    //        assert!(map.is_empty());
    //    }

    #[test]
    fn insert_duplicate_key() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        assert_eq!(
            map.insert(Long(OsString::from("One")), Arg::with_name("Value2")),
            1
        );
    }

    #[test]
    #[should_panic]
    fn insert_duplicate_value() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        let orig_len = map.values.len();

        map.insert(Long(OsString::from("Two")), Arg::with_name("Value1"));

        assert_eq!(map.values.len(), orig_len);
        assert_eq!(
            map.get(Long(OsString::from("One"))),
            map.get(Long(OsString::from("Two")))
        );
    }

    //    #[test]
    //    fn insert_delete_none() {
    //        let mut map = MKeyMap::new();
    //        map.insert("One", clap::Arg::with_name("Value1"));
    //        assert_eq!(map.remove("Two"), None);
    //        assert!(!map.is_empty());
    //        assert_eq!(map.get("One"), Some(clap::Arg::with_name("Value1")));
    //    }

    #[test]
    fn insert_multiple_keys() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();
        let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        map.insert_key(Long(OsString::from("Two")), index);

        assert_eq!(
            map.get(Long(OsString::from("One"))),
            map.get(Long(OsString::from("Two")))
        );
        assert_eq!(map.values.len(), 1);
    }

    // #[test]
    // fn insert_by_name() {
    //     let mut map: MKeyMap<Arg> = MKeyMap::new();
    //     let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

    //     map.insert_key_by_name(Long(OsString::from("Two")), "Value1");

    //     assert_eq!(
    //         map.get(Long(OsString::from("One"))),
    //         map.get(Long(OsString::from("Two")))
    //     );
    //     assert_eq!(map.values.len(), 1);
    // }

    #[test]
    fn get_mutable() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        assert_eq!(
            map.get_mut(Long(OsString::from("One"))),
            Some(&mut Arg::with_name("Value1"))
        );
    }

    #[test]
    fn remove_key() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();
        let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));

        map.insert_key(Long(OsString::from("Two")), index);
        map.remove_key(Long(OsString::from("One")));

        assert_eq!(map.keys.len(), 1);
        assert_eq!(map.values.len(), 1);
    }

    #[test]
    fn iter_keys() {
        let mut map: MKeyMap<Arg> = MKeyMap::new();

        map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
        map.insert(Long(OsString::from("Two")), Arg::with_name("Value2"));
        map.insert(Position(1), Arg::with_name("Value3"));

        let iter = map.keys().cloned();
        let mut ground_truth = HashSet::new();

        ground_truth.insert(Long(OsString::from("One")));
        ground_truth.insert(Long(OsString::from("Two")));
        ground_truth.insert(Position(1));

        assert_eq!(
            ground_truth.symmetric_difference(&iter.collect()).count(),
            0
        );
    }
}
<|endoftext|>


<commit_before>use conduit::{Request, Response};
use conduit_router::RequestParams;
use conduit_json_parser;
use pg::{PostgresConnection, PostgresRow};

use app::RequestApp;
use db::Connection;
use git;
use user::{RequestUser, User};
use util::{RequestUtils, CargoResult, Require, internal, ChainError};
use util::errors::{NotFound, CargoError};

pub struct Package {
    pub id: i32,
    pub name: String,
}

#[deriving(Encodable)]
pub struct EncodablePackage {
    pub id: String,
    pub name: String,
}

impl Package {
    fn from_row(row: &PostgresRow) -> Package {
        Package {
            id: row.get("id"),
            name: row.get("name"),
        }
    }

    pub fn find_by_name(conn: &Connection, name: &str) -> CargoResult<Package> {
        let stmt = try!(conn.prepare("SELECT * FROM packages \
                                      WHERE name = $1 LIMIT 1"));
        match try!(stmt.query([&name])).next() {
            Some(row) => Ok(Package::from_row(&row)),
            None => Err(NotFound.box_error()),
        }
    }

    pub fn valid_name(name: &str) -> bool {
        if name.len() == 0 { return false }
        name.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-')
    }

    fn encodable(self) -> EncodablePackage {
        let Package { name, .. } = self;
        EncodablePackage { id: name.clone(), name: name }
    }
}

pub fn setup(conn: &PostgresConnection) {
    conn.execute("DROP TABLE IF EXISTS packages", []).unwrap();
    conn.execute("CREATE TABLE packages (
                    id              SERIAL PRIMARY KEY,
                    name            VARCHAR NOT NULL
                  )", []).unwrap();

    conn.execute("ALTER TABLE packages ADD CONSTRAINT \
                  unique_name UNIQUE (name)", []).unwrap();
    conn.execute("INSERT INTO packages (name) VALUES ($1)",
                 [&"Test"]).unwrap();
    conn.execute("INSERT INTO packages (name) VALUES ($1)",
                 [&"Test2"]).unwrap();
}

pub fn index(req: &mut Request) -> CargoResult<Response> {
    let limit = 10i64;
    let offset = 0i64;
    let conn = req.app().db();
    let stmt = try!(conn.prepare("SELECT * FROM packages LIMIT $1 OFFSET $2"));

    let mut pkgs = Vec::new();
    for row in try!(stmt.query([&limit, &offset])) {
        pkgs.push(Package::from_row(&row).encodable());
    }

    let stmt = try!(conn.prepare("SELECT COUNT(*) FROM packages"));
    let row = try!(stmt.query([])).next().unwrap();
    let total = row.get(0u);

    #[deriving(Encodable)]
    struct R { packages: Vec<EncodablePackage>, meta: Meta }
    #[deriving(Encodable)]
    struct Meta { total: i64, page: i64 }

    Ok(req.json(&R {
        packages: pkgs,
        meta: Meta { total: total, page: offset / limit }
    }))
}

pub fn show(req: &mut Request) -> CargoResult<Response> {
    let name = req.params()["package_id"];
    let pkg = try!(Package::find_by_name(&req.app().db(), name.as_slice()));

    #[deriving(Encodable)]
    struct R { package: EncodablePackage }
    Ok(req.json(&R { package: pkg.encodable() }))
}

#[deriving(Decodable)]
pub struct UpdateRequest { package: UpdatePackage }

#[deriving(Decodable)]
pub struct UpdatePackage {
    name: String,
}

pub fn update(req: &mut Request) -> CargoResult<Response> {
    try!(req.user());
    let conn = req.app().db();
    let name = req.params()["package_id"];
    let pkg = try!(Package::find_by_name(&conn, name.as_slice()));

    let update = conduit_json_parser::json_params::<UpdateRequest>(req).unwrap();
    // TODO: this should do something
    println!("new name: {}", update.package.name);

    #[deriving(Encodable)]
    struct R { package: EncodablePackage }
    Ok(req.json(&R { package: pkg.encodable() }))
}

#[deriving(Decodable)]
pub struct NewRequest { package: NewPackage }

#[deriving(Decodable)]
pub struct NewPackage {
    name: String,
}

pub fn new(req: &mut Request) -> CargoResult<Response> {
    let app = req.app();
    let db = app.db();
    let tx = try!(db.transaction());
    tx.set_rollback();
    let _user = {
        let header = try!(req.headers().find("X-Cargo-Auth").require(|| {
            internal("missing X-Cargo-Auth header")
        }));
        try!(User::find_by_api_token(&tx, header.get(0).as_slice()))
    };

    let update = conduit_json_parser::json_params::<NewRequest>(req).unwrap();
    let name = update.package.name.as_slice();
    if !Package::valid_name(name) {
        return Err(internal(format!("invalid crate name: `{}`", name)))
    }
    try!(tx.execute("INSERT INTO packages (name) VALUES ($1)", [&name]));

    let pkg = try!(Package::find_by_name(&tx, name.as_slice()));
    try!(git::add_package(app, &pkg).chain_error(|| {
        internal(format!("could not add package `{}` to the git repo", pkg.name))
    }));
    tx.set_commit();

    #[deriving(Encodable)]
    struct R { package: EncodablePackage }
    Ok(req.json(&R { package: pkg.encodable() }))
}
<commit_msg>Lowercase package names<commit_after>use conduit::{Request, Response};
use conduit_router::RequestParams;
use conduit_json_parser;
use pg::{PostgresConnection, PostgresRow};

use app::RequestApp;
use db::Connection;
use git;
use user::{RequestUser, User};
use util::{RequestUtils, CargoResult, Require, internal, ChainError};
use util::errors::{NotFound, CargoError};

pub struct Package {
    pub id: i32,
    pub name: String,
}

#[deriving(Encodable)]
pub struct EncodablePackage {
    pub id: String,
    pub name: String,
}

impl Package {
    fn from_row(row: &PostgresRow) -> Package {
        Package {
            id: row.get("id"),
            name: row.get("name"),
        }
    }

    pub fn find_by_name(conn: &Connection, name: &str) -> CargoResult<Package> {
        let stmt = try!(conn.prepare("SELECT * FROM packages \
                                      WHERE name = $1 LIMIT 1"));
        match try!(stmt.query([&name])).next() {
            Some(row) => Ok(Package::from_row(&row)),
            None => Err(NotFound.box_error()),
        }
    }

    pub fn valid_name(name: &str) -> bool {
        if name.len() == 0 { return false }
        name.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-')
    }

    fn encodable(self) -> EncodablePackage {
        let Package { name, .. } = self;
        EncodablePackage { id: name.clone(), name: name }
    }
}

pub fn setup(conn: &PostgresConnection) {
    conn.execute("DROP TABLE IF EXISTS packages", []).unwrap();
    conn.execute("CREATE TABLE packages (
                    id              SERIAL PRIMARY KEY,
                    name            VARCHAR NOT NULL
                  )", []).unwrap();

    conn.execute("ALTER TABLE packages ADD CONSTRAINT \
                  unique_name UNIQUE (name)", []).unwrap();
    conn.execute("INSERT INTO packages (name) VALUES ($1)",
                 [&"Test"]).unwrap();
    conn.execute("INSERT INTO packages (name) VALUES ($1)",
                 [&"Test2"]).unwrap();
}

pub fn index(req: &mut Request) -> CargoResult<Response> {
    let limit = 10i64;
    let offset = 0i64;
    let conn = req.app().db();
    let stmt = try!(conn.prepare("SELECT * FROM packages LIMIT $1 OFFSET $2"));

    let mut pkgs = Vec::new();
    for row in try!(stmt.query([&limit, &offset])) {
        pkgs.push(Package::from_row(&row).encodable());
    }

    let stmt = try!(conn.prepare("SELECT COUNT(*) FROM packages"));
    let row = try!(stmt.query([])).next().unwrap();
    let total = row.get(0u);

    #[deriving(Encodable)]
    struct R { packages: Vec<EncodablePackage>, meta: Meta }
    #[deriving(Encodable)]
    struct Meta { total: i64, page: i64 }

    Ok(req.json(&R {
        packages: pkgs,
        meta: Meta { total: total, page: offset / limit }
    }))
}

pub fn show(req: &mut Request) -> CargoResult<Response> {
    let name = req.params()["package_id"];
    let pkg = try!(Package::find_by_name(&req.app().db(), name.as_slice()));

    #[deriving(Encodable)]
    struct R { package: EncodablePackage }
    Ok(req.json(&R { package: pkg.encodable() }))
}

#[deriving(Decodable)]
pub struct UpdateRequest { package: UpdatePackage }

#[deriving(Decodable)]
pub struct UpdatePackage {
    name: String,
}

pub fn update(req: &mut Request) -> CargoResult<Response> {
    try!(req.user());
    let conn = req.app().db();
    let name = req.params()["package_id"];
    let pkg = try!(Package::find_by_name(&conn, name.as_slice()));

    let update = conduit_json_parser::json_params::<UpdateRequest>(req).unwrap();
    // TODO: this should do something
    println!("new name: {}", update.package.name);

    #[deriving(Encodable)]
    struct R { package: EncodablePackage }
    Ok(req.json(&R { package: pkg.encodable() }))
}

#[deriving(Decodable)]
pub struct NewRequest { package: NewPackage }

#[deriving(Decodable)]
pub struct NewPackage {
    name: String,
}

pub fn new(req: &mut Request) -> CargoResult<Response> {
    let app = req.app();
    let db = app.db();
    let tx = try!(db.transaction());
    tx.set_rollback();
    let _user = {
        let header = try!(req.headers().find("X-Cargo-Auth").require(|| {
            internal("missing X-Cargo-Auth header")
        }));
        try!(User::find_by_api_token(&tx, header.get(0).as_slice()))
    };

    let update = conduit_json_parser::json_params::<NewRequest>(req).unwrap();
    let name: String = update.package.name.as_slice().chars()
                             .map(|c| c.to_lowercase()).collect();
    if !Package::valid_name(name.as_slice()) {
        return Err(internal(format!("invalid crate name: `{}`", name)))
    }
    try!(tx.execute("INSERT INTO packages (name) VALUES ($1)", [&name]));

    let pkg = try!(Package::find_by_name(&tx, name.as_slice()));
    try!(git::add_package(app, &pkg).chain_error(|| {
        internal(format!("could not add package `{}` to the git repo", pkg.name))
    }));
    tx.set_commit();

    #[deriving(Encodable)]
    struct R { package: EncodablePackage }
    Ok(req.json(&R { package: pkg.encodable() }))
}
<|endoftext|>


<commit_before>//! This module is strictly internal.
//!
//! These functions are used by `date()`, `time()` and `datetime()`.
//! They are currently not private, because the need to be accessible,
//! but are not useful by themselves.
//!
//! Please refer to the top-level functions instead, as they offer a better abstraction.
//!
//! **These functions may be made private later.**

use std::str::{self, FromStr};

use nom::{
    branch::alt,
    bytes::complete::{tag, take_while, take_while_m_n},
    character::complete::one_of,
    character::is_digit,
    combinator::{map, opt},
    sequence::preceded,
    IResult,
};

use crate::{Date, DateTime, Time};

#[cfg(test)]
mod tests;

fn take_n_digits(i: &[u8], n: usize) -> IResult<&[u8], u32> {
    let (i, digits) = take_while_m_n(n, n, is_digit)(i)?;

    let s = str::from_utf8(digits).expect("Invalid data, expected UTF-8 string");
    let res = s
        .parse()
        .expect("Invalid string, expected ASCII reprensation of a number");
    Ok((i, res))
}

// year
fn year_prefix(i: &[u8]) -> IResult<&[u8], &[u8]> {
    alt((tag(b"+"), tag(b"-")))(i)
}
fn year(i: &[u8]) -> IResult<&[u8], i32> {
    let (i, is_pos) = match opt(year_prefix)(i) {
        Ok((i, Some(b))) if b == b"-" => (i, false),
        Ok((i, _)) => (i, true),
        Err(e) => return Err(e),
    };
    let (i, year) = take_n_digits(i, 4)?;
    let mut year = year as i32;
    if !is_pos {
        year = -year
    }

    Ok((i, year))
}

// MM
fn month(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, m) = take_n_digits(i, 2)?;

    if m >= 1 && m <= 12 {
        Ok((new_i, m))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// DD
fn day(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, d) = take_n_digits(i, 2)?;

    if d >= 1 && d <= 31 {
        Ok((new_i, d))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// WW
fn week(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, w) = take_n_digits(i, 2)?;

    if w >= 1 && w <= 52 {
        Ok((new_i, w))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

fn week_day(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, d) = take_n_digits(i, 1)?;

    if d >= 1 && d <= 7 {
        Ok((new_i, d))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// ordinal DDD
fn ord_day(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, d) = take_n_digits(i, 3)?;

    if d >= 1 && d <= 366 {
        Ok((new_i, d))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// YYYY-MM-DD
fn ymd_date(i: &[u8]) -> IResult<&[u8], Date> {
    let (i, y) = year(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, m) = month(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, d) = day(i)?;
    Ok((
        i,
        Date::YMD {
            year: y,
            month: m,
            day: d,
        },
    ))
}

// YYYY-DDD
fn ordinal_date(i: &[u8]) -> IResult<&[u8], Date> {
    let (i, y) = year(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, d) = ord_day(i)?;
    Ok((i, Date::Ordinal { year: y, ddd: d }))
}

// YYYY-"W"WW-D
fn iso_week_date(i: &[u8]) -> IResult<&[u8], Date> {
    let (i, y) = year(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, _) = tag(b"W")(i)?;
    let (i, w) = week(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, d) = week_day(i)?;
    Ok((
        i,
        Date::Week {
            year: y,
            ww: w,
            d: d,
        },
    ))
}

pub fn parse_date(i: &[u8]) -> IResult<&[u8], Date> {
    alt((ymd_date, iso_week_date, ordinal_date))(i)
}

// TIME

// HH
fn hour(i: &[u8]) -> IResult<&[u8], u32> {
    let (i, m) = take_n_digits(i, 2)?;

    if m <= 24 {
        Ok((i, m))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// MM
fn minute(i: &[u8]) -> IResult<&[u8], u32> {
    let (i, m) = take_n_digits(i, 2)?;

    if m <= 59 {
        Ok((i, m))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

fn second(i: &[u8]) -> IResult<&[u8], u32> {
    let (i, m) = take_n_digits(i, 2)?;

    if m <= 60 {
        Ok((i, m))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

fn into_fraction_string(digits: &[u8]) -> Result<f32, ::std::num::ParseFloatError> {
    let mut s = String::from("0.");
    s += str::from_utf8(digits).unwrap();
    FromStr::from_str(&s)
}
fn fractions(i: &[u8]) -> IResult<&[u8], f32> {
    let (i, f) = take_while(is_digit)(i)?;
    let f = into_fraction_string(f).unwrap();

    Ok((i, f))
}

fn millisecond(fraction: f32) -> u32 {
    (1000.0 * fraction) as u32
}

// HH:MM:[SS][.(m*)][(Z|+...|-...)]
pub fn parse_time(i: &[u8]) -> IResult<&[u8], Time> {
    let (i, h) = hour(i)?;
    let (i, _) = opt(tag(b":"))(i)?;
    let (i, m) = minute(i)?;
    let (i, s) = opt(preceded(opt(tag(b":")), second))(i)?;
    let (i, ms) = opt(map(preceded(one_of(",."), fractions), millisecond))(i)?;
    let (i, z) = match opt(alt((timezone_hour, timezone_utc)))(i) {
        Ok(ok) => ok,
        Err(nom::Err::Incomplete(_)) => (i, None),
        Err(e) => return Err(e),
    };

    Ok((
        i,
        Time {
            hour: h,
            minute: m,
            second: s.unwrap_or(0),
            millisecond: ms.unwrap_or(0),
            tz_offset_hours: z.unwrap_or((0, 0)).0,
            tz_offset_minutes: z.unwrap_or((0, 0)).1,
        },
    ))
}

fn sign(i: &[u8]) -> IResult<&[u8], i32> {
    map(alt((tag(b"-"), tag(b"+"))), |s: &[u8]| match s {
        b"-" => -1,
        _ => 1,
    })(i)
}

fn timezone_hour(i: &[u8]) -> IResult<&[u8], (i32, i32)> {
    let (i, s) = sign(i)?;
    let (i, h) = hour(i)?;
    let (i, m) = if i.is_empty() {
        (i, 0)
    } else {
        let (i, _) = opt(tag(b":"))(i)?;
        minute(i)?
    };

    Ok((i, ((s * (h as i32), s * (m as i32)))))
}

fn timezone_utc(i: &[u8]) -> IResult<&[u8], (i32, i32)> {
    map(tag(b"Z"), |_| (0, 0))(i)
}

// Full ISO8601
pub fn parse_datetime(i: &[u8]) -> IResult<&[u8], DateTime> {
    let (i, d) = parse_date(i)?;
    let (i, _) = tag(b"T")(i)?;
    let (i, t) = parse_time(i)?;

    Ok((i, DateTime { date: d, time: t }))
}
<commit_msg>Inline fraction conversion<commit_after>//! This module is strictly internal.
//!
//! These functions are used by `date()`, `time()` and `datetime()`.
//! They are currently not private, because the need to be accessible,
//! but are not useful by themselves.
//!
//! Please refer to the top-level functions instead, as they offer a better abstraction.
//!
//! **These functions may be made private later.**

use std::str;

use nom::{
    branch::alt,
    bytes::complete::{tag, take_while, take_while_m_n},
    character::complete::one_of,
    character::is_digit,
    combinator::{map, opt},
    sequence::preceded,
    IResult,
};

use crate::{Date, DateTime, Time};

#[cfg(test)]
mod tests;

fn take_n_digits(i: &[u8], n: usize) -> IResult<&[u8], u32> {
    let (i, digits) = take_while_m_n(n, n, is_digit)(i)?;

    let s = str::from_utf8(digits).expect("Invalid data, expected UTF-8 string");
    let res = s
        .parse()
        .expect("Invalid string, expected ASCII reprensation of a number");
    Ok((i, res))
}

// year
fn year_prefix(i: &[u8]) -> IResult<&[u8], &[u8]> {
    alt((tag(b"+"), tag(b"-")))(i)
}
fn year(i: &[u8]) -> IResult<&[u8], i32> {
    let (i, is_pos) = match opt(year_prefix)(i) {
        Ok((i, Some(b))) if b == b"-" => (i, false),
        Ok((i, _)) => (i, true),
        Err(e) => return Err(e),
    };
    let (i, year) = take_n_digits(i, 4)?;
    let mut year = year as i32;
    if !is_pos {
        year = -year
    }

    Ok((i, year))
}

// MM
fn month(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, m) = take_n_digits(i, 2)?;

    if m >= 1 && m <= 12 {
        Ok((new_i, m))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// DD
fn day(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, d) = take_n_digits(i, 2)?;

    if d >= 1 && d <= 31 {
        Ok((new_i, d))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// WW
fn week(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, w) = take_n_digits(i, 2)?;

    if w >= 1 && w <= 52 {
        Ok((new_i, w))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

fn week_day(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, d) = take_n_digits(i, 1)?;

    if d >= 1 && d <= 7 {
        Ok((new_i, d))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// ordinal DDD
fn ord_day(i: &[u8]) -> IResult<&[u8], u32> {
    let (new_i, d) = take_n_digits(i, 3)?;

    if d >= 1 && d <= 366 {
        Ok((new_i, d))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// YYYY-MM-DD
fn ymd_date(i: &[u8]) -> IResult<&[u8], Date> {
    let (i, y) = year(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, m) = month(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, d) = day(i)?;
    Ok((
        i,
        Date::YMD {
            year: y,
            month: m,
            day: d,
        },
    ))
}

// YYYY-DDD
fn ordinal_date(i: &[u8]) -> IResult<&[u8], Date> {
    let (i, y) = year(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, d) = ord_day(i)?;
    Ok((i, Date::Ordinal { year: y, ddd: d }))
}

// YYYY-"W"WW-D
fn iso_week_date(i: &[u8]) -> IResult<&[u8], Date> {
    let (i, y) = year(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, _) = tag(b"W")(i)?;
    let (i, w) = week(i)?;
    let (i, _) = opt(tag(b"-"))(i)?;
    let (i, d) = week_day(i)?;
    Ok((
        i,
        Date::Week {
            year: y,
            ww: w,
            d: d,
        },
    ))
}

pub fn parse_date(i: &[u8]) -> IResult<&[u8], Date> {
    alt((ymd_date, iso_week_date, ordinal_date))(i)
}

// TIME

// HH
fn hour(i: &[u8]) -> IResult<&[u8], u32> {
    let (i, m) = take_n_digits(i, 2)?;

    if m <= 24 {
        Ok((i, m))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

// MM
fn minute(i: &[u8]) -> IResult<&[u8], u32> {
    let (i, m) = take_n_digits(i, 2)?;

    if m <= 59 {
        Ok((i, m))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

fn second(i: &[u8]) -> IResult<&[u8], u32> {
    let (i, m) = take_n_digits(i, 2)?;

    if m <= 60 {
        Ok((i, m))
    } else {
        Err(nom::Err::Error((i, nom::error::ErrorKind::Eof)))
    }
}

fn fractions(i: &[u8]) -> IResult<&[u8], f32> {
    let (i, digits) = take_while(is_digit)(i)?;
    let digits = str::from_utf8(digits).unwrap(); // This can't panic, `digits` will only include digits.
    let f = format!("0.{}", digits).parse().unwrap(); // This can't panic, the string is a valid `f32`.

    Ok((i, f))
}

fn millisecond(fraction: f32) -> u32 {
    (1000.0 * fraction) as u32
}

// HH:MM:[SS][.(m*)][(Z|+...|-...)]
pub fn parse_time(i: &[u8]) -> IResult<&[u8], Time> {
    let (i, h) = hour(i)?;
    let (i, _) = opt(tag(b":"))(i)?;
    let (i, m) = minute(i)?;
    let (i, s) = opt(preceded(opt(tag(b":")), second))(i)?;
    let (i, ms) = opt(map(preceded(one_of(",."), fractions), millisecond))(i)?;
    let (i, z) = match opt(alt((timezone_hour, timezone_utc)))(i) {
        Ok(ok) => ok,
        Err(nom::Err::Incomplete(_)) => (i, None),
        Err(e) => return Err(e),
    };

    Ok((
        i,
        Time {
            hour: h,
            minute: m,
            second: s.unwrap_or(0),
            millisecond: ms.unwrap_or(0),
            tz_offset_hours: z.unwrap_or((0, 0)).0,
            tz_offset_minutes: z.unwrap_or((0, 0)).1,
        },
    ))
}

fn sign(i: &[u8]) -> IResult<&[u8], i32> {
    map(alt((tag(b"-"), tag(b"+"))), |s: &[u8]| match s {
        b"-" => -1,
        _ => 1,
    })(i)
}

fn timezone_hour(i: &[u8]) -> IResult<&[u8], (i32, i32)> {
    let (i, s) = sign(i)?;
    let (i, h) = hour(i)?;
    let (i, m) = if i.is_empty() {
        (i, 0)
    } else {
        let (i, _) = opt(tag(b":"))(i)?;
        minute(i)?
    };

    Ok((i, ((s * (h as i32), s * (m as i32)))))
}

fn timezone_utc(i: &[u8]) -> IResult<&[u8], (i32, i32)> {
    map(tag(b"Z"), |_| (0, 0))(i)
}

// Full ISO8601
pub fn parse_datetime(i: &[u8]) -> IResult<&[u8], DateTime> {
    let (i, d) = parse_date(i)?;
    let (i, _) = tag(b"T")(i)?;
    let (i, t) = parse_time(i)?;

    Ok((i, DateTime { date: d, time: t }))
}
<|endoftext|>


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

use serde_json::value::Value as Json;

use crate::context::{merge_json, Context};
use crate::error::RenderError;
use crate::json::path::Path;
use crate::output::Output;
use crate::registry::Registry;
use crate::render::{Decorator, Evaluable, RenderContext, Renderable};
use crate::template::Template;

fn render_partial<'reg: 'rc, 'rc>(
    t: &'reg Template,
    d: &Decorator<'reg, 'rc>,
    r: &'reg Registry<'reg>,
    ctx: &'rc Context,
    local_rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> Result<(), RenderError> {
    // partial context path
    if let Some(ref param_ctx) = d.param(0) {
        if let (Some(p), Some(block)) = (param_ctx.context_path(), local_rc.block_mut()) {
            *block.base_path_mut() = p.clone();
        }
    }

    // @partial-block
    if let Some(t) = d.template() {
        local_rc.set_partial("@partial-block".to_owned(), t);
    }

    let result = if d.hash().is_empty() {
        t.render(r, ctx, local_rc, out)
    } else {
        let hash_ctx = d
            .hash()
            .iter()
            .map(|(k, v)| (k, v.value()))
            .collect::<HashMap<&&str, &Json>>();
        let current_path = Path::current();
        let partial_context =
            merge_json(local_rc.evaluate2(ctx, &current_path)?.as_json(), &hash_ctx);
        let ctx = Context::wraps(&partial_context)?;
        let mut partial_rc = local_rc.new_for_block();
        t.render(r, &ctx, &mut partial_rc, out)
    };

    local_rc.remove_partial("@partial-block");

    result
}

pub fn expand_partial<'reg: 'rc, 'rc>(
    d: &Decorator<'reg, 'rc>,
    r: &'reg Registry<'reg>,
    ctx: &'rc Context,
    rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> Result<(), RenderError> {
    // try eval inline partials first
    if let Some(t) = d.template() {
        t.eval(r, ctx, rc)?;
    }

    let tname = d.name();
    if rc.is_current_template(tname) {
        return Err(RenderError::new("Cannot include self in >"));
    }

    let partial = rc.get_partial(tname);

    match partial {
        Some(t) => {
            let mut local_rc = rc.clone();
            render_partial(&t, d, r, ctx, &mut local_rc, out)?;
        }
        None => {
            if let Some(t) = r.get_template(tname).or_else(|| d.template()) {
                let mut local_rc = rc.clone();
                render_partial(t, d, r, ctx, &mut local_rc, out)?;
            }
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use crate::registry::Registry;

    #[test]
    fn test() {
        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("t0", "{{> t1}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t1", "{{this}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t2", "{{#> t99}}not there{{/t99}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t3", "{{#*inline \"t31\"}}{{this}}{{/inline}}{{> t31}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t4",
                "{{#> t5}}{{#*inline \"nav\"}}navbar{{/inline}}{{/t5}}"
            )
            .is_ok());
        assert!(handlebars
            .register_template_string("t5", "include {{> nav}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t6", "{{> t1 a}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t7",
                "{{#*inline \"t71\"}}{{a}}{{/inline}}{{> t71 a=\"world\"}}"
            )
            .is_ok());
        assert!(handlebars.register_template_string("t8", "{{a}}").is_ok());
        assert!(handlebars
            .register_template_string("t9", "{{> t8 a=2}}")
            .is_ok());

        assert_eq!(handlebars.render("t0", &1).ok().unwrap(), "1".to_string());
        assert_eq!(
            handlebars.render("t2", &1).ok().unwrap(),
            "not there".to_string()
        );
        assert_eq!(handlebars.render("t3", &1).ok().unwrap(), "1".to_string());
        assert_eq!(
            handlebars.render("t4", &1).ok().unwrap(),
            "include navbar".to_string()
        );
        assert_eq!(
            handlebars
                .render("t6", &btreemap! {"a".to_string() => "2".to_string()})
                .ok()
                .unwrap(),
            "2".to_string()
        );
        assert_eq!(
            handlebars.render("t7", &1).ok().unwrap(),
            "world".to_string()
        );
        assert_eq!(handlebars.render("t9", &1).ok().unwrap(), "2".to_string());
    }

    #[test]
    fn test_include_partial_block() {
        let t0 = "hello {{> @partial-block}}";
        let t1 = "{{#> t0}}inner {{this}}{{/t0}}";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let r0 = handlebars.render("t1", &true);
        assert_eq!(r0.ok().unwrap(), "hello inner true".to_string());
    }

    #[test]
    fn test_self_inclusion() {
        let t0 = "hello {{> t1}} {{> t0}}";
        let t1 = "some template";
        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let r0 = handlebars.render("t0", &true);
        assert!(r0.is_err());
    }

    #[test]
    fn test_issue_143() {
        let main_template = "one{{> two }}three{{> two }}";
        let two_partial = "--- two ---";

        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("template", main_template)
            .is_ok());
        assert!(handlebars
            .register_template_string("two", two_partial)
            .is_ok());

        let r0 = handlebars.render("template", &true);
        assert_eq!(r0.ok().unwrap(), "one--- two ---three--- two ---");
    }

    #[test]
    fn test_hash_context_outscope() {
        let main_template = "In: {{> p a=2}} Out: {{a}}";
        let p_partial = "{{a}}";

        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("template", main_template)
            .is_ok());
        assert!(handlebars.register_template_string("p", p_partial).is_ok());

        let r0 = handlebars.render("template", &true);
        assert_eq!(r0.ok().unwrap(), "In: 2 Out: ");
    }

    #[test]
    fn test_nested_partial_scope() {
        let t = "{{#*inline \"pp\"}}{{a}} {{b}}{{/inline}}{{#each c}}{{> pp a=2}}{{/each}}";
        let data = json!({"c": [{"b": true}, {"b": false}]});

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t", t).is_ok());
        let r0 = handlebars.render("t", &data);
        assert_eq!(r0.ok().unwrap(), "2 true2 false");
    }
}
<commit_msg>(test) add partial context using literal value<commit_after>use std::collections::HashMap;

use serde_json::value::Value as Json;

use crate::context::{merge_json, Context};
use crate::error::RenderError;
use crate::json::path::Path;
use crate::output::Output;
use crate::registry::Registry;
use crate::render::{Decorator, Evaluable, RenderContext, Renderable};
use crate::template::Template;

fn render_partial<'reg: 'rc, 'rc>(
    t: &'reg Template,
    d: &Decorator<'reg, 'rc>,
    r: &'reg Registry<'reg>,
    ctx: &'rc Context,
    local_rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> Result<(), RenderError> {
    // partial context path
    if let Some(ref param_ctx) = d.param(0) {
        if let (Some(p), Some(block)) = (param_ctx.context_path(), local_rc.block_mut()) {
            *block.base_path_mut() = p.clone();
        }
    }

    // @partial-block
    if let Some(t) = d.template() {
        local_rc.set_partial("@partial-block".to_owned(), t);
    }

    let result = if d.hash().is_empty() {
        t.render(r, ctx, local_rc, out)
    } else {
        let hash_ctx = d
            .hash()
            .iter()
            .map(|(k, v)| (k, v.value()))
            .collect::<HashMap<&&str, &Json>>();
        let current_path = Path::current();
        let partial_context =
            merge_json(local_rc.evaluate2(ctx, &current_path)?.as_json(), &hash_ctx);
        let ctx = Context::wraps(&partial_context)?;
        let mut partial_rc = local_rc.new_for_block();
        t.render(r, &ctx, &mut partial_rc, out)
    };

    local_rc.remove_partial("@partial-block");

    result
}

pub fn expand_partial<'reg: 'rc, 'rc>(
    d: &Decorator<'reg, 'rc>,
    r: &'reg Registry<'reg>,
    ctx: &'rc Context,
    rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> Result<(), RenderError> {
    // try eval inline partials first
    if let Some(t) = d.template() {
        t.eval(r, ctx, rc)?;
    }

    let tname = d.name();
    if rc.is_current_template(tname) {
        return Err(RenderError::new("Cannot include self in >"));
    }

    let partial = rc.get_partial(tname);

    match partial {
        Some(t) => {
            let mut local_rc = rc.clone();
            render_partial(&t, d, r, ctx, &mut local_rc, out)?;
        }
        None => {
            if let Some(t) = r.get_template(tname).or_else(|| d.template()) {
                let mut local_rc = rc.clone();
                render_partial(t, d, r, ctx, &mut local_rc, out)?;
            }
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use crate::registry::Registry;

    #[test]
    fn test() {
        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("t0", "{{> t1}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t1", "{{this}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t2", "{{#> t99}}not there{{/t99}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t3", "{{#*inline \"t31\"}}{{this}}{{/inline}}{{> t31}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t4",
                "{{#> t5}}{{#*inline \"nav\"}}navbar{{/inline}}{{/t5}}"
            )
            .is_ok());
        assert!(handlebars
            .register_template_string("t5", "include {{> nav}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t6", "{{> t1 a}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t7",
                "{{#*inline \"t71\"}}{{a}}{{/inline}}{{> t71 a=\"world\"}}"
            )
            .is_ok());
        assert!(handlebars.register_template_string("t8", "{{a}}").is_ok());
        assert!(handlebars
            .register_template_string("t9", "{{> t8 a=2}}")
            .is_ok());

        assert_eq!(handlebars.render("t0", &1).ok().unwrap(), "1".to_string());
        assert_eq!(
            handlebars.render("t2", &1).ok().unwrap(),
            "not there".to_string()
        );
        assert_eq!(handlebars.render("t3", &1).ok().unwrap(), "1".to_string());
        assert_eq!(
            handlebars.render("t4", &1).ok().unwrap(),
            "include navbar".to_string()
        );
        assert_eq!(
            handlebars
                .render("t6", &btreemap! {"a".to_string() => "2".to_string()})
                .ok()
                .unwrap(),
            "2".to_string()
        );
        assert_eq!(
            handlebars.render("t7", &1).ok().unwrap(),
            "world".to_string()
        );
        assert_eq!(handlebars.render("t9", &1).ok().unwrap(), "2".to_string());
    }

    #[test]
    fn test_include_partial_block() {
        let t0 = "hello {{> @partial-block}}";
        let t1 = "{{#> t0}}inner {{this}}{{/t0}}";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let r0 = handlebars.render("t1", &true);
        assert_eq!(r0.ok().unwrap(), "hello inner true".to_string());
    }

    #[test]
    fn test_self_inclusion() {
        let t0 = "hello {{> t1}} {{> t0}}";
        let t1 = "some template";
        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let r0 = handlebars.render("t0", &true);
        assert!(r0.is_err());
    }

    #[test]
    fn test_issue_143() {
        let main_template = "one{{> two }}three{{> two }}";
        let two_partial = "--- two ---";

        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("template", main_template)
            .is_ok());
        assert!(handlebars
            .register_template_string("two", two_partial)
            .is_ok());

        let r0 = handlebars.render("template", &true);
        assert_eq!(r0.ok().unwrap(), "one--- two ---three--- two ---");
    }

    #[test]
    fn test_hash_context_outscope() {
        let main_template = "In: {{> p a=2}} Out: {{a}}";
        let p_partial = "{{a}}";

        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("template", main_template)
            .is_ok());
        assert!(handlebars.register_template_string("p", p_partial).is_ok());

        let r0 = handlebars.render("template", &true);
        assert_eq!(r0.ok().unwrap(), "In: 2 Out: ");
    }

    #[test]
    fn test_partial_context_hash() {
        let mut hbs = Registry::new();
        hbs.register_template_string("one", "This is a test. {{> two name=\"fred\" }}")
            .unwrap();
        hbs.register_template_string("two", "Lets test {{name}}")
            .unwrap();
        assert_eq!(
            "This is a test. Lets test fred",
            hbs.render("one", &0).unwrap()
        );
    }

    #[test]
    fn test_nested_partial_scope() {
        let t = "{{#*inline \"pp\"}}{{a}} {{b}}{{/inline}}{{#each c}}{{> pp a=2}}{{/each}}";
        let data = json!({"c": [{"b": true}, {"b": false}]});

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t", t).is_ok());
        let r0 = handlebars.render("t", &data);
        assert_eq!(r0.ok().unwrap(), "2 true2 false");
    }
}
<|endoftext|>


<commit_before>use rustc::mir::repr as mir;

use error::{EvalError, EvalResult};
use memory::Pointer;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PrimVal {
    Bool(bool),
    I8(i8), I16(i16), I32(i32), I64(i64),
    U8(u8), U16(u16), U32(u32), U64(u64),

    AbstractPtr(Pointer),
    FnPtr(Pointer),
    IntegerPtr(u64),
}

/// returns the result of the operation and whether the operation overflowed
pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> {
    use rustc::mir::repr::BinOp::*;
    use self::PrimVal::*;

    macro_rules! overflow {
        ($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({
            let (val, of) = $l.$op($r);
            if of {
                return Ok(($v(val), true));
            } else {
                $v(val)
            }
        })
    }

    macro_rules! int_binops {
        ($v:ident, $l:ident, $r:ident) => ({
            match bin_op {
                Add    => overflow!($v, $v, $l, overflowing_add, $r),
                Sub    => overflow!($v, $v, $l, overflowing_sub, $r),
                Mul    => overflow!($v, $v, $l, overflowing_mul, $r),
                Div    => overflow!($v, $v, $l, overflowing_div, $r),
                Rem    => overflow!($v, $v, $l, overflowing_rem, $r),
                BitXor => $v($l ^ $r),
                BitAnd => $v($l & $r),
                BitOr  => $v($l | $r),

                // these have already been handled
                Shl => unreachable!(),
                Shr => unreachable!(),

                Eq => Bool($l == $r),
                Ne => Bool($l != $r),
                Lt => Bool($l < $r),
                Le => Bool($l <= $r),
                Gt => Bool($l > $r),
                Ge => Bool($l >= $r),
            }
        })
    }

    fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> {
        use rustc::mir::repr::BinOp::*;
        match bin_op {
            Eq => Ok(Bool(false)),
            Ne => Ok(Bool(true)),
            Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath),
            _ => unimplemented!(),
        }
    }

    match bin_op {
        // can have rhs with a different numeric type
        Shl | Shr => {
            let mask_bits = match left {
                I8(_) => 3,
                I16(_) => 4,
                I32(_) => 5,
                I64(_) => 6,
                U8(_) => 3,
                U16(_) => 4,
                U32(_) => 5,
                U64(_) => 6,
                _ => unreachable!(),
            };
            let r = match right {
                I8(i) => (i & ((1 << mask_bits) - 1)) as u32,
                I16(i) => (i & ((1 << mask_bits) - 1)) as u32,
                I32(i) => (i & ((1 << mask_bits) - 1)) as u32,
                I64(i) => (i & ((1 << mask_bits) - 1)) as u32,
                U8(i) => (i & ((1 << mask_bits) - 1)) as u32,
                U16(i) => (i & ((1 << mask_bits) - 1)) as u32,
                U32(i) => (i & ((1 << mask_bits) - 1)) as u32,
                U64(i) => (i & ((1 << mask_bits) - 1)) as u32,
                _ => return Err(EvalError::InvalidBitShiftRhs(right)),
            };
            macro_rules! shift {
                ($v:ident, $l:ident, $r:ident) => ({
                    match bin_op {
                        Shl => overflow!($v, U32, $l, overflowing_shl, $r),
                        Shr => overflow!($v, U32, $l, overflowing_shr, $r),
                        _ => unreachable!(),
                    }
                })
            }
            let val = match left {
                I8(l) => shift!(I8, l, r),
                I16(l) => shift!(I16, l, r),
                I32(l) => shift!(I32, l, r),
                I64(l) => shift!(I64, l, r),
                U8(l) => shift!(U8, l, r),
                U16(l) => shift!(U16, l, r),
                U32(l) => shift!(U32, l, r),
                U64(l) => shift!(U64, l, r),
                _ => unreachable!(),
            };
            return Ok((val, false));
        },
        _ => {},
    }

    let val = match (left, right) {
        (I8(l),  I8(r))  => int_binops!(I8, l, r),
        (I16(l), I16(r)) => int_binops!(I16, l, r),
        (I32(l), I32(r)) => int_binops!(I32, l, r),
        (I64(l), I64(r)) => int_binops!(I64, l, r),
        (U8(l),  U8(r))  => int_binops!(U8, l, r),
        (U16(l), U16(r)) => int_binops!(U16, l, r),
        (U32(l), U32(r)) => int_binops!(U32, l, r),
        (U64(l), U64(r)) => int_binops!(U64, l, r),

        (Bool(l), Bool(r)) => {
            Bool(match bin_op {
                Eq => l == r,
                Ne => l != r,
                Lt => l < r,
                Le => l <= r,
                Gt => l > r,
                Ge => l >= r,
                BitOr => l | r,
                BitXor => l ^ r,
                BitAnd => l & r,
                Add | Sub | Mul | Div | Rem | Shl | Shr => return Err(EvalError::InvalidBoolOp(bin_op)),
            })
        }

        (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r),

        (AbstractPtr(_), IntegerPtr(_)) |
        (IntegerPtr(_), AbstractPtr(_)) |
        (FnPtr(_), AbstractPtr(_)) |
        (AbstractPtr(_), FnPtr(_)) |
        (FnPtr(_), IntegerPtr(_)) |
        (IntegerPtr(_), FnPtr(_)) =>
            unrelated_ptr_ops(bin_op)?,

        (FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op {
            Eq => Bool(l_ptr == r_ptr),
            Ne => Bool(l_ptr != r_ptr),
            _ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))),
        },

        (AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => {
            if l_ptr.alloc_id != r_ptr.alloc_id {
                return Ok((unrelated_ptr_ops(bin_op)?, false));
            }

            let l = l_ptr.offset;
            let r = r_ptr.offset;

            match bin_op {
                Eq => Bool(l == r),
                Ne => Bool(l != r),
                Lt => Bool(l < r),
                Le => Bool(l <= r),
                Gt => Bool(l > r),
                Ge => Bool(l >= r),
                _ => return Err(EvalError::Unimplemented(format!("unimplemented ptr op: {:?}", bin_op))),
            }
        }

        (l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))),
    };

    Ok((val, false))
}

pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> {
    use rustc::mir::repr::UnOp::*;
    use self::PrimVal::*;
    match (un_op, val) {
        (Not, Bool(b)) => Ok(Bool(!b)),
        (Not, I8(n))  => Ok(I8(!n)),
        (Neg, I8(n))  => Ok(I8(-n)),
        (Not, I16(n)) => Ok(I16(!n)),
        (Neg, I16(n)) => Ok(I16(-n)),
        (Not, I32(n)) => Ok(I32(!n)),
        (Neg, I32(n)) => Ok(I32(-n)),
        (Not, I64(n)) => Ok(I64(!n)),
        (Neg, I64(n)) => Ok(I64(-n)),
        (Not, U8(n))  => Ok(U8(!n)),
        (Not, U16(n)) => Ok(U16(!n)),
        (Not, U32(n)) => Ok(U32(!n)),
        (Not, U64(n)) => Ok(U64(!n)),
        _ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))),
    }
}
<commit_msg>add a comment explaining the magic numbers<commit_after>use rustc::mir::repr as mir;

use error::{EvalError, EvalResult};
use memory::Pointer;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PrimVal {
    Bool(bool),
    I8(i8), I16(i16), I32(i32), I64(i64),
    U8(u8), U16(u16), U32(u32), U64(u64),

    AbstractPtr(Pointer),
    FnPtr(Pointer),
    IntegerPtr(u64),
}

/// returns the result of the operation and whether the operation overflowed
pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> {
    use rustc::mir::repr::BinOp::*;
    use self::PrimVal::*;

    macro_rules! overflow {
        ($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({
            let (val, of) = $l.$op($r);
            if of {
                return Ok(($v(val), true));
            } else {
                $v(val)
            }
        })
    }

    macro_rules! int_binops {
        ($v:ident, $l:ident, $r:ident) => ({
            match bin_op {
                Add    => overflow!($v, $v, $l, overflowing_add, $r),
                Sub    => overflow!($v, $v, $l, overflowing_sub, $r),
                Mul    => overflow!($v, $v, $l, overflowing_mul, $r),
                Div    => overflow!($v, $v, $l, overflowing_div, $r),
                Rem    => overflow!($v, $v, $l, overflowing_rem, $r),
                BitXor => $v($l ^ $r),
                BitAnd => $v($l & $r),
                BitOr  => $v($l | $r),

                // these have already been handled
                Shl => unreachable!(),
                Shr => unreachable!(),

                Eq => Bool($l == $r),
                Ne => Bool($l != $r),
                Lt => Bool($l < $r),
                Le => Bool($l <= $r),
                Gt => Bool($l > $r),
                Ge => Bool($l >= $r),
            }
        })
    }

    fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> {
        use rustc::mir::repr::BinOp::*;
        match bin_op {
            Eq => Ok(Bool(false)),
            Ne => Ok(Bool(true)),
            Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath),
            _ => unimplemented!(),
        }
    }

    match bin_op {
        // can have rhs with a different numeric type
        Shl | Shr => {
            // these numbers are the maximum number of bits a bitshift rhs could possibly have
            // e.g. u16 can be bitshifted by 0..16, so 2^4 - 1 is the largest possible bitshift
            let mask_bits = match left {
                I8(_) => 3,
                I16(_) => 4,
                I32(_) => 5,
                I64(_) => 6,
                U8(_) => 3,
                U16(_) => 4,
                U32(_) => 5,
                U64(_) => 6,
                _ => unreachable!(),
            };
            let r = match right {
                I8(i) => (i & ((1 << mask_bits) - 1)) as u32,
                I16(i) => (i & ((1 << mask_bits) - 1)) as u32,
                I32(i) => (i & ((1 << mask_bits) - 1)) as u32,
                I64(i) => (i & ((1 << mask_bits) - 1)) as u32,
                U8(i) => (i & ((1 << mask_bits) - 1)) as u32,
                U16(i) => (i & ((1 << mask_bits) - 1)) as u32,
                U32(i) => (i & ((1 << mask_bits) - 1)) as u32,
                U64(i) => (i & ((1 << mask_bits) - 1)) as u32,
                _ => return Err(EvalError::InvalidBitShiftRhs(right)),
            };
            macro_rules! shift {
                ($v:ident, $l:ident, $r:ident) => ({
                    match bin_op {
                        Shl => overflow!($v, U32, $l, overflowing_shl, $r),
                        Shr => overflow!($v, U32, $l, overflowing_shr, $r),
                        _ => unreachable!(),
                    }
                })
            }
            let val = match left {
                I8(l) => shift!(I8, l, r),
                I16(l) => shift!(I16, l, r),
                I32(l) => shift!(I32, l, r),
                I64(l) => shift!(I64, l, r),
                U8(l) => shift!(U8, l, r),
                U16(l) => shift!(U16, l, r),
                U32(l) => shift!(U32, l, r),
                U64(l) => shift!(U64, l, r),
                _ => unreachable!(),
            };
            return Ok((val, false));
        },
        _ => {},
    }

    let val = match (left, right) {
        (I8(l),  I8(r))  => int_binops!(I8, l, r),
        (I16(l), I16(r)) => int_binops!(I16, l, r),
        (I32(l), I32(r)) => int_binops!(I32, l, r),
        (I64(l), I64(r)) => int_binops!(I64, l, r),
        (U8(l),  U8(r))  => int_binops!(U8, l, r),
        (U16(l), U16(r)) => int_binops!(U16, l, r),
        (U32(l), U32(r)) => int_binops!(U32, l, r),
        (U64(l), U64(r)) => int_binops!(U64, l, r),

        (Bool(l), Bool(r)) => {
            Bool(match bin_op {
                Eq => l == r,
                Ne => l != r,
                Lt => l < r,
                Le => l <= r,
                Gt => l > r,
                Ge => l >= r,
                BitOr => l | r,
                BitXor => l ^ r,
                BitAnd => l & r,
                Add | Sub | Mul | Div | Rem | Shl | Shr => return Err(EvalError::InvalidBoolOp(bin_op)),
            })
        }

        (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r),

        (AbstractPtr(_), IntegerPtr(_)) |
        (IntegerPtr(_), AbstractPtr(_)) |
        (FnPtr(_), AbstractPtr(_)) |
        (AbstractPtr(_), FnPtr(_)) |
        (FnPtr(_), IntegerPtr(_)) |
        (IntegerPtr(_), FnPtr(_)) =>
            unrelated_ptr_ops(bin_op)?,

        (FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op {
            Eq => Bool(l_ptr == r_ptr),
            Ne => Bool(l_ptr != r_ptr),
            _ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))),
        },

        (AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => {
            if l_ptr.alloc_id != r_ptr.alloc_id {
                return Ok((unrelated_ptr_ops(bin_op)?, false));
            }

            let l = l_ptr.offset;
            let r = r_ptr.offset;

            match bin_op {
                Eq => Bool(l == r),
                Ne => Bool(l != r),
                Lt => Bool(l < r),
                Le => Bool(l <= r),
                Gt => Bool(l > r),
                Ge => Bool(l >= r),
                _ => return Err(EvalError::Unimplemented(format!("unimplemented ptr op: {:?}", bin_op))),
            }
        }

        (l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))),
    };

    Ok((val, false))
}

pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> {
    use rustc::mir::repr::UnOp::*;
    use self::PrimVal::*;
    match (un_op, val) {
        (Not, Bool(b)) => Ok(Bool(!b)),
        (Not, I8(n))  => Ok(I8(!n)),
        (Neg, I8(n))  => Ok(I8(-n)),
        (Not, I16(n)) => Ok(I16(!n)),
        (Neg, I16(n)) => Ok(I16(-n)),
        (Not, I32(n)) => Ok(I32(!n)),
        (Neg, I32(n)) => Ok(I32(-n)),
        (Not, I64(n)) => Ok(I64(!n)),
        (Neg, I64(n)) => Ok(I64(-n)),
        (Not, U8(n))  => Ok(U8(!n)),
        (Not, U16(n)) => Ok(U16(!n)),
        (Not, U32(n)) => Ok(U32(!n)),
        (Not, U64(n)) => Ok(U64(!n)),
        _ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))),
    }
}
<|endoftext|>