<commit_before>use std::io::{self, Write, ErrorKind, Seek, SeekFrom, Read};
use std::fmt::{self, Formatter, Display};
use std::fs::{self, File, OpenOptions, Metadata};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::{Arc, RwLock};
use std::path::{Path, PathBuf};

use crc::crc32::{self, Digest, Hasher32};
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
use protobuf::Message;

use kvproto::eraftpb::Snapshot;
use kvproto::raft_serverpb::RaftSnapshotData;
use raftstore::store::Msg;
use util::transport::SendCh;

#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct SnapKey {
    pub region_id: u64,
    pub term: u64,
    pub idx: u64,
}

impl SnapKey {
    #[inline]
    pub fn new(region_id: u64, term: u64, idx: u64) -> SnapKey {
        SnapKey {
            region_id: region_id,
            term: term,
            idx: idx,
        }
    }

    #[inline]
    pub fn from_region_snap(region_id: u64, snap: &Snapshot) -> SnapKey {
        let index = snap.get_metadata().get_index();
        let term = snap.get_metadata().get_term();
        SnapKey::new(region_id, term, index)
    }

    pub fn from_snap(snap: &Snapshot) -> io::Result<SnapKey> {
        let mut snap_data = RaftSnapshotData::new();
        if let Err(e) = snap_data.merge_from_bytes(snap.get_data()) {
            return Err(io::Error::new(ErrorKind::Other, e));
        }

        Ok(SnapKey::from_region_snap(snap_data.get_region().get_id(), snap))
    }
}

impl Display for SnapKey {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}_{}_{}", self.region_id, self.term, self.idx)
    }
}

/// Name prefix for the self-generated snapshot file.
const SNAP_GEN_PREFIX: &'static str = "gen";
/// Name prefix for the received snapshot file.
const SNAP_REV_PREFIX: &'static str = "rev";

/// A structure represents the snapshot file.
///
/// All changes to the file will be written to `tmp_file` first, and use
/// `save` method to make them persistent. When saving a crc32 checksum
/// will be appended to the file end automatically.
pub struct SnapFile {
    file: PathBuf,
    digest: Digest,
    // File is the file obj represent the tmpfile, string is the actual path to
    // tmpfile.
    tmp_file: Option<(File, String)>,
}

impl SnapFile {
    fn new<T: Into<PathBuf>>(snap_dir: T, is_sending: bool, key: &SnapKey) -> io::Result<SnapFile> {
        let mut file_path = snap_dir.into();
        if !file_path.exists() {
            try!(fs::create_dir_all(file_path.as_path()));
        }
        let prefix = if is_sending {
            SNAP_GEN_PREFIX
        } else {
            SNAP_REV_PREFIX
        };
        let file_name = format!("{}_{}.snap", prefix, key);
        file_path.push(&file_name);

        let mut f = SnapFile {
            file: file_path,
            digest: Digest::new(crc32::IEEE),
            tmp_file: None,
        };
        try!(f.init());
        Ok(f)
    }

    pub fn init(&mut self) -> io::Result<()> {
        if self.exists() || self.tmp_file.is_some() {
            return Ok(());
        }

        let tmp_path = format!("{}.tmp", self.path().display());
        let tmp_f = try!(OpenOptions::new().write(true).create_new(true).open(&tmp_path));
        self.tmp_file = Some((tmp_f, tmp_path));
        Ok(())
    }

    pub fn meta(&self) -> io::Result<Metadata> {
        self.file.metadata()
    }

    /// Validate whether current file is broken.
    pub fn validate(&self) -> io::Result<()> {
        let mut reader = try!(File::open(self.path()));
        let mut digest = Digest::new(crc32::IEEE);
        let len = try!(reader.metadata()).len();
        if len < 4 {
            return Err(io::Error::new(ErrorKind::InvalidInput, format!("file length {} < 4", len)));
        }
        let to_read = len as usize - 4;
        let mut total_read = 0;
        let mut buffer = vec![0; 4098];
        loop {
            let read = try!(reader.read(&mut buffer));
            if total_read + read >= to_read {
                digest.write(&buffer[..to_read - total_read]);
                try!(reader.seek(SeekFrom::End(-4)));
                break;
            }
            digest.write(&buffer);
            total_read += read;
        }
        let sum = try!(reader.read_u32::<BigEndian>());
        if sum != digest.sum32() {
            return Err(io::Error::new(ErrorKind::InvalidData,
                                      format!("crc not correct: {} != {}", sum, digest.sum32())));
        }
        Ok(())
    }

    pub fn exists(&self) -> bool {
        self.file.exists() && self.file.is_file()
    }

    pub fn delete(&self) {
        if let Err(e) = self.try_delete() {
            error!("failed to delete {}: {:?}", self.path().display(), e);
        }
    }

    pub fn try_delete(&self) -> io::Result<()> {
        debug!("deleting {}", self.path().display());
        fs::remove_file(self.path())
    }

    /// Use the content in temporary files replace the target file.
    ///
    /// Please note that this method can only be called once.
    pub fn save(&mut self) -> io::Result<()> {
        debug!("saving to {}", self.file.as_path().display());
        if let Some((mut f, path)) = self.tmp_file.take() {
            try!(f.write_u32::<BigEndian>(self.digest.sum32()));
            try!(f.flush());
            try!(fs::rename(path, self.file.as_path()));
        }
        Ok(())
    }

    pub fn path(&self) -> &Path {
        self.file.as_path()
    }
}

impl Write for SnapFile {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if self.tmp_file.is_none() {
            return Ok(0);
        }
        let written = try!(self.tmp_file.as_mut().unwrap().0.write(buf));
        self.digest.write(&buf[..written]);
        Ok(written)
    }

    fn flush(&mut self) -> io::Result<()> {
        if self.tmp_file.is_none() {
            return Ok(());
        }
        self.tmp_file.as_mut().unwrap().0.flush()
    }
}

impl Drop for SnapFile {
    fn drop(&mut self) {
        if let Some((_, path)) = self.tmp_file.take() {
            debug!("deleting {}", path);
            if let Err(e) = fs::remove_file(&path) {
                warn!("failed to delete temporary file {}: {:?}", path, e);
            }
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum SnapEntry {
    Generating = 1,
    Sending = 2,
    Receiving = 3,
    Applying = 4,
}

/// `SnapStats` is for snapshot statistics.
pub struct SnapStats {
    pub sending_count: usize,
    pub receiving_count: usize,
}

/// `SnapManagerCore` trace all current processing snapshots.
pub struct SnapManagerCore {
    // directory to store snapfile.
    base: String,
    registry: HashMap<SnapKey, Vec<SnapEntry>>,
    ch: Option<SendCh<Msg>>,
}

impl SnapManagerCore {
    pub fn new<T: Into<String>>(path: T, ch: Option<SendCh<Msg>>) -> SnapManagerCore {
        SnapManagerCore {
            base: path.into(),
            registry: map![],
            ch: ch,
        }
    }

    pub fn init(&self) -> io::Result<()> {
        let path = Path::new(&self.base);
        if !path.exists() {
            try!(fs::create_dir_all(path));
            return Ok(());
        }
        if !path.is_dir() {
            return Err(io::Error::new(ErrorKind::Other,
                                      format!("{} should be a directory", path.display())));
        }
        Ok(())
    }

    pub fn list_snap(&self) -> io::Result<Vec<(SnapKey, bool)>> {
        let path = Path::new(&self.base);
        let read_dir = try!(fs::read_dir(path));
        Ok(read_dir.filter_map(|p| {
                let p = match p {
                    Err(e) => {
                        error!("failed to list content of {}: {:?}", self.base, e);
                        return None;
                    }
                    Ok(p) => p,
                };
                match p.file_type() {
                    Ok(t) if t.is_file() => {}
                    _ => return None,
                }
                let file_name = p.file_name();
                let name = match file_name.to_str() {
                    None => return None,
                    Some(n) => n,
                };
                let is_sending = name.starts_with(SNAP_GEN_PREFIX);
                let numbers: Vec<u64> = name.split('.')
                    .next()
                    .map_or_else(|| vec![], |s| {
                        s.split('_')
                            .skip(1)
                            .filter_map(|s| s.parse().ok())
                            .collect()
                    });
                if numbers.len() != 3 {
                    error!("failed to parse snapkey from {}", name);
                    return None;
                }
                Some((SnapKey::new(numbers[0], numbers[1], numbers[2]), is_sending))
            })
            .collect())
    }

    #[inline]
    pub fn has_registered(&self, key: &SnapKey) -> bool {
        self.registry.contains_key(key)
    }

    #[inline]
    pub fn get_snap_file(&self, key: &SnapKey, is_sending: bool) -> io::Result<SnapFile> {
        SnapFile::new(&self.base, is_sending, key)
    }

    pub fn register(&mut self, key: SnapKey, entry: SnapEntry) {
        debug!("register [key: {}, entry: {:?}]", key, entry);
        match self.registry.entry(key) {
            Entry::Occupied(mut e) => {
                if e.get().contains(&entry) {
                    warn!("{} is registered more than 1 time!!!", e.key());
                    return;
                }
                e.get_mut().push(entry);
            }
            Entry::Vacant(e) => {
                e.insert(vec![entry]);
            }
        }

        self.notify_stats();
    }

    pub fn deregister(&mut self, key: &SnapKey, entry: &SnapEntry) {
        debug!("deregister [key: {}, entry: {:?}]", key, entry);
        let mut need_clean = false;
        let mut handled = false;
        if let Some(e) = self.registry.get_mut(key) {
            let last_len = e.len();
            e.retain(|e| e != entry);
            need_clean = e.is_empty();
            handled = last_len > e.len();
        }
        if need_clean {
            self.registry.remove(key);
        }
        if handled {
            self.notify_stats();
            return;
        }
        warn!("stale deregister key: {} {:?}", key, entry);
    }

    fn notify_stats(&self) {
        if let Some(ref ch) = self.ch {
            if let Err(e) = ch.try_send(Msg::SnapshotStats) {
                error!("notify snapshot stats failed {:?}", e)
            }
        }
    }

    pub fn stats(&self) -> SnapStats {
        // send_count, generating_count, receiving_count, applying_count
        let (mut sending_cnt, mut receiving_cnt) = (0, 0);
        for v in self.registry.values() {
            let (mut is_sending, mut is_receiving) = (false, false);
            for s in v {
                match *s {
                    SnapEntry::Sending | SnapEntry::Generating => is_sending = true,
                    SnapEntry::Receiving | SnapEntry::Applying => is_receiving = true,
                }
            }
            if is_sending {
                sending_cnt += 1;
            }
            if is_receiving {
                receiving_cnt += 1;
            }
        }

        SnapStats {
            sending_count: sending_cnt,
            receiving_count: receiving_cnt,
        }
    }
}

pub type SnapManager = Arc<RwLock<SnapManagerCore>>;

pub fn new_snap_mgr<T: Into<String>>(path: T, ch: Option<SendCh<Msg>>) -> SnapManager {
    Arc::new(RwLock::new(SnapManagerCore::new(path, ch)))
}
<commit_msg>raftstore: clean temporary file when startup (#1197)<commit_after>use std::io::{self, Write, ErrorKind, Seek, SeekFrom, Read};
use std::fmt::{self, Formatter, Display};
use std::fs::{self, File, OpenOptions, Metadata};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::{Arc, RwLock};
use std::path::{Path, PathBuf};

use crc::crc32::{self, Digest, Hasher32};
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
use protobuf::Message;

use kvproto::eraftpb::Snapshot;
use kvproto::raft_serverpb::RaftSnapshotData;
use raftstore::store::Msg;
use util::transport::SendCh;

const TMP_FILE_SUFFIX: &'static str = ".tmp";

#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct SnapKey {
    pub region_id: u64,
    pub term: u64,
    pub idx: u64,
}

impl SnapKey {
    #[inline]
    pub fn new(region_id: u64, term: u64, idx: u64) -> SnapKey {
        SnapKey {
            region_id: region_id,
            term: term,
            idx: idx,
        }
    }

    #[inline]
    pub fn from_region_snap(region_id: u64, snap: &Snapshot) -> SnapKey {
        let index = snap.get_metadata().get_index();
        let term = snap.get_metadata().get_term();
        SnapKey::new(region_id, term, index)
    }

    pub fn from_snap(snap: &Snapshot) -> io::Result<SnapKey> {
        let mut snap_data = RaftSnapshotData::new();
        if let Err(e) = snap_data.merge_from_bytes(snap.get_data()) {
            return Err(io::Error::new(ErrorKind::Other, e));
        }

        Ok(SnapKey::from_region_snap(snap_data.get_region().get_id(), snap))
    }
}

impl Display for SnapKey {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}_{}_{}", self.region_id, self.term, self.idx)
    }
}

/// Name prefix for the self-generated snapshot file.
const SNAP_GEN_PREFIX: &'static str = "gen";
/// Name prefix for the received snapshot file.
const SNAP_REV_PREFIX: &'static str = "rev";

/// A structure represents the snapshot file.
///
/// All changes to the file will be written to `tmp_file` first, and use
/// `save` method to make them persistent. When saving a crc32 checksum
/// will be appended to the file end automatically.
pub struct SnapFile {
    file: PathBuf,
    digest: Digest,
    // File is the file obj represent the tmpfile, string is the actual path to
    // tmpfile.
    tmp_file: Option<(File, String)>,
}

impl SnapFile {
    fn new<T: Into<PathBuf>>(snap_dir: T, is_sending: bool, key: &SnapKey) -> io::Result<SnapFile> {
        let mut file_path = snap_dir.into();
        if !file_path.exists() {
            try!(fs::create_dir_all(file_path.as_path()));
        }
        let prefix = if is_sending {
            SNAP_GEN_PREFIX
        } else {
            SNAP_REV_PREFIX
        };
        let file_name = format!("{}_{}.snap", prefix, key);
        file_path.push(&file_name);

        let mut f = SnapFile {
            file: file_path,
            digest: Digest::new(crc32::IEEE),
            tmp_file: None,
        };
        try!(f.init());
        Ok(f)
    }

    pub fn init(&mut self) -> io::Result<()> {
        if self.exists() || self.tmp_file.is_some() {
            return Ok(());
        }

        let tmp_path = format!("{}{}", self.path().display(), TMP_FILE_SUFFIX);
        let tmp_f = try!(OpenOptions::new().write(true).create_new(true).open(&tmp_path));
        self.tmp_file = Some((tmp_f, tmp_path));
        Ok(())
    }

    pub fn meta(&self) -> io::Result<Metadata> {
        self.file.metadata()
    }

    /// Validate whether current file is broken.
    pub fn validate(&self) -> io::Result<()> {
        let mut reader = try!(File::open(self.path()));
        let mut digest = Digest::new(crc32::IEEE);
        let len = try!(reader.metadata()).len();
        if len < 4 {
            return Err(io::Error::new(ErrorKind::InvalidInput, format!("file length {} < 4", len)));
        }
        let to_read = len as usize - 4;
        let mut total_read = 0;
        let mut buffer = vec![0; 4098];
        loop {
            let read = try!(reader.read(&mut buffer));
            if total_read + read >= to_read {
                digest.write(&buffer[..to_read - total_read]);
                try!(reader.seek(SeekFrom::End(-4)));
                break;
            }
            digest.write(&buffer);
            total_read += read;
        }
        let sum = try!(reader.read_u32::<BigEndian>());
        if sum != digest.sum32() {
            return Err(io::Error::new(ErrorKind::InvalidData,
                                      format!("crc not correct: {} != {}", sum, digest.sum32())));
        }
        Ok(())
    }

    pub fn exists(&self) -> bool {
        self.file.exists() && self.file.is_file()
    }

    pub fn delete(&self) {
        if let Err(e) = self.try_delete() {
            error!("failed to delete {}: {:?}", self.path().display(), e);
        }
    }

    pub fn try_delete(&self) -> io::Result<()> {
        debug!("deleting {}", self.path().display());
        fs::remove_file(self.path())
    }

    /// Use the content in temporary files replace the target file.
    ///
    /// Please note that this method can only be called once.
    pub fn save(&mut self) -> io::Result<()> {
        debug!("saving to {}", self.file.as_path().display());
        if let Some((mut f, path)) = self.tmp_file.take() {
            try!(f.write_u32::<BigEndian>(self.digest.sum32()));
            try!(f.flush());
            try!(fs::rename(path, self.file.as_path()));
        }
        Ok(())
    }

    pub fn path(&self) -> &Path {
        self.file.as_path()
    }
}

impl Write for SnapFile {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if self.tmp_file.is_none() {
            return Ok(0);
        }
        let written = try!(self.tmp_file.as_mut().unwrap().0.write(buf));
        self.digest.write(&buf[..written]);
        Ok(written)
    }

    fn flush(&mut self) -> io::Result<()> {
        if self.tmp_file.is_none() {
            return Ok(());
        }
        self.tmp_file.as_mut().unwrap().0.flush()
    }
}

impl Drop for SnapFile {
    fn drop(&mut self) {
        if let Some((_, path)) = self.tmp_file.take() {
            debug!("deleting {}", path);
            if let Err(e) = fs::remove_file(&path) {
                warn!("failed to delete temporary file {}: {:?}", path, e);
            }
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum SnapEntry {
    Generating = 1,
    Sending = 2,
    Receiving = 3,
    Applying = 4,
}

/// `SnapStats` is for snapshot statistics.
pub struct SnapStats {
    pub sending_count: usize,
    pub receiving_count: usize,
}

/// `SnapManagerCore` trace all current processing snapshots.
pub struct SnapManagerCore {
    // directory to store snapfile.
    base: String,
    registry: HashMap<SnapKey, Vec<SnapEntry>>,
    ch: Option<SendCh<Msg>>,
}

impl SnapManagerCore {
    pub fn new<T: Into<String>>(path: T, ch: Option<SendCh<Msg>>) -> SnapManagerCore {
        SnapManagerCore {
            base: path.into(),
            registry: map![],
            ch: ch,
        }
    }

    pub fn init(&self) -> io::Result<()> {
        let path = Path::new(&self.base);
        if !path.exists() {
            try!(fs::create_dir_all(path));
            return Ok(());
        }
        if !path.is_dir() {
            return Err(io::Error::new(ErrorKind::Other,
                                      format!("{} should be a directory", path.display())));
        }
        for f in try!(fs::read_dir(path)) {
            let p = try!(f);
            if try!(p.file_type()).is_file() {
                if let Some(s) = p.file_name().to_str() {
                    if s.ends_with(TMP_FILE_SUFFIX) {
                        try!(fs::remove_file(p.path()));
                    }
                }
            }
        }
        Ok(())
    }

    pub fn list_snap(&self) -> io::Result<Vec<(SnapKey, bool)>> {
        let path = Path::new(&self.base);
        let read_dir = try!(fs::read_dir(path));
        Ok(read_dir.filter_map(|p| {
                let p = match p {
                    Err(e) => {
                        error!("failed to list content of {}: {:?}", self.base, e);
                        return None;
                    }
                    Ok(p) => p,
                };
                match p.file_type() {
                    Ok(t) if t.is_file() => {}
                    _ => return None,
                }
                let file_name = p.file_name();
                let name = match file_name.to_str() {
                    None => return None,
                    Some(n) => n,
                };
                let is_sending = name.starts_with(SNAP_GEN_PREFIX);
                let numbers: Vec<u64> = name.split('.')
                    .next()
                    .map_or_else(|| vec![], |s| {
                        s.split('_')
                            .skip(1)
                            .filter_map(|s| s.parse().ok())
                            .collect()
                    });
                if numbers.len() != 3 {
                    error!("failed to parse snapkey from {}", name);
                    return None;
                }
                Some((SnapKey::new(numbers[0], numbers[1], numbers[2]), is_sending))
            })
            .collect())
    }

    #[inline]
    pub fn has_registered(&self, key: &SnapKey) -> bool {
        self.registry.contains_key(key)
    }

    #[inline]
    pub fn get_snap_file(&self, key: &SnapKey, is_sending: bool) -> io::Result<SnapFile> {
        SnapFile::new(&self.base, is_sending, key)
    }

    pub fn register(&mut self, key: SnapKey, entry: SnapEntry) {
        debug!("register [key: {}, entry: {:?}]", key, entry);
        match self.registry.entry(key) {
            Entry::Occupied(mut e) => {
                if e.get().contains(&entry) {
                    warn!("{} is registered more than 1 time!!!", e.key());
                    return;
                }
                e.get_mut().push(entry);
            }
            Entry::Vacant(e) => {
                e.insert(vec![entry]);
            }
        }

        self.notify_stats();
    }

    pub fn deregister(&mut self, key: &SnapKey, entry: &SnapEntry) {
        debug!("deregister [key: {}, entry: {:?}]", key, entry);
        let mut need_clean = false;
        let mut handled = false;
        if let Some(e) = self.registry.get_mut(key) {
            let last_len = e.len();
            e.retain(|e| e != entry);
            need_clean = e.is_empty();
            handled = last_len > e.len();
        }
        if need_clean {
            self.registry.remove(key);
        }
        if handled {
            self.notify_stats();
            return;
        }
        warn!("stale deregister key: {} {:?}", key, entry);
    }

    fn notify_stats(&self) {
        if let Some(ref ch) = self.ch {
            if let Err(e) = ch.try_send(Msg::SnapshotStats) {
                error!("notify snapshot stats failed {:?}", e)
            }
        }
    }

    pub fn stats(&self) -> SnapStats {
        // send_count, generating_count, receiving_count, applying_count
        let (mut sending_cnt, mut receiving_cnt) = (0, 0);
        for v in self.registry.values() {
            let (mut is_sending, mut is_receiving) = (false, false);
            for s in v {
                match *s {
                    SnapEntry::Sending | SnapEntry::Generating => is_sending = true,
                    SnapEntry::Receiving | SnapEntry::Applying => is_receiving = true,
                }
            }
            if is_sending {
                sending_cnt += 1;
            }
            if is_receiving {
                receiving_cnt += 1;
            }
        }

        SnapStats {
            sending_count: sending_cnt,
            receiving_count: receiving_cnt,
        }
    }
}

pub type SnapManager = Arc<RwLock<SnapManagerCore>>;

pub fn new_snap_mgr<T: Into<String>>(path: T, ch: Option<SendCh<Msg>>) -> SnapManager {
    Arc::new(RwLock::new(SnapManagerCore::new(path, ch)))
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use std::fs::File;

    use tempdir::TempDir;

    use util::HandyRwLock;
    use super::*;

    #[test]
    fn test_snap_mgr() {
        let path = TempDir::new("test-snap-mgr").unwrap();

        // `mgr` should create the specified directory when it does not exist.
        let path1 = path.path().to_str().unwrap().to_owned() + "/snap1";
        let p = Path::new(&path1);
        assert!(!p.exists());
        let mut mgr = new_snap_mgr(path1.clone(), None);
        mgr.wl().init().unwrap();
        assert!(p.exists());

        // if target is a file, an error should be returned.
        let path2 = path.path().to_str().unwrap().to_owned() + "/snap2";
        File::create(&path2).unwrap();
        mgr = new_snap_mgr(path2, None);
        assert!(mgr.wl().init().is_err());

        // if temporary files exist, they should be deleted.
        let path3 = path.path().to_str().unwrap().to_owned() + "/snap3";
        let key1 = SnapKey::new(1, 1, 1);
        let f1 = SnapFile::new(&path3, true, &key1).unwrap();
        let f2 = SnapFile::new(&path3, false, &key1).unwrap();
        let key2 = SnapKey::new(2, 1, 1);
        let mut f3 = SnapFile::new(&path3, true, &key2).unwrap();
        f3.save().unwrap();
        let mut f4 = SnapFile::new(&path3, false, &key2).unwrap();
        f4.save().unwrap();
        assert!(!f1.exists());
        assert!(!f2.exists());
        assert!(f3.exists());
        assert!(f4.exists());
        assert!(Path::new(&f1.tmp_file.as_ref().unwrap().1).exists());
        assert!(Path::new(&f2.tmp_file.as_ref().unwrap().1).exists());
        mgr = new_snap_mgr(path3, None);
        mgr.wl().init().unwrap();
        assert!(!Path::new(&f1.tmp_file.as_ref().unwrap().1).exists());
        assert!(!Path::new(&f2.tmp_file.as_ref().unwrap().1).exists());
        assert!(f3.exists());
        assert!(f4.exists());
    }
}
<|endoftext|>


<commit_before>use latch::Latch;
#[cfg(feature = "nightly")]
use std::any::Any;
#[cfg(feature = "nightly")]
use std::panic::{self, AssertUnwindSafe};
use std::cell::UnsafeCell;
use std::mem;

enum JobResult<T> {
    None,
    Ok(T),
    #[cfg(feature = "nightly")]
    Panic(Box<Any + Send>),
}

/// A `Job` is used to advertise work for other threads that they may
/// want to steal. In accordance with time honored tradition, jobs are
/// arranged in a deque, so that thieves can take from the top of the
/// deque while the main worker manages the bottom of the deque. This
/// deque is managed by the `thread_pool` module.
pub trait Job {
    unsafe fn execute(&self, mode: JobMode);
}

pub enum JobMode {
    Execute,
    Abort,
}

#[derive(Copy, Clone)]
pub struct JobRef {
    pointer: *const Job
}

unsafe impl Send for JobRef {}
unsafe impl Sync for JobRef {}

impl JobRef {
    #[inline]
    pub unsafe fn execute(&self, mode: JobMode) {
        (*self.pointer).execute(mode);
    }
}

/// A job that will be owned by a stack slot. This means that when it
/// executes it need not free any heap data, the cleanup occurs when
/// the stack frame is later popped.
pub struct StackJob<L: Latch, F, R> {
    pub latch: L,
    func: UnsafeCell<Option<F>>,
    result: UnsafeCell<JobResult<R>>,
}

impl<L: Latch, F, R> StackJob<L, F, R>
    where F: FnOnce() -> R
{
    pub fn new(func: F, latch: L) -> StackJob<L, F, R> {
        StackJob {
            latch: latch,
            func: UnsafeCell::new(Some(func)),
            result: UnsafeCell::new(JobResult::None),
        }
    }

    pub unsafe fn as_job_ref(&self) -> JobRef {
        let job_ref: *const (Job + 'static) = mem::transmute(self as *const Job);
        JobRef { pointer: job_ref }
    }

    pub unsafe fn run_inline(self) -> R {
        self.func.into_inner().unwrap()()
    }

    #[cfg(not(feature = "nightly"))]
    pub unsafe fn into_result(self) -> R {
        match self.result.into_inner() {
            JobResult::None => panic!("job function panicked"),
            JobResult::Ok(x) => x,
        }
    }

    #[cfg(feature = "nightly")]
    pub unsafe fn into_result(self) -> R {
        match self.result.into_inner() {
            JobResult::None => unreachable!(),
            JobResult::Ok(x) => x,
            JobResult::Panic(x) => panic::resume_unwind(x),
        }
    }

    #[cfg(not(feature = "nightly"))]
    fn run_result(func: F) -> JobResult<R> {
        JobResult::Ok(func())
    }

    #[cfg(feature = "nightly")]
    fn run_result(func: F) -> JobResult<R> {
        // We assert that func is unwind-safe since it doesn't touch any of
        // our data and we will be propagating the panic back to the user at
        // the join() call.
        match panic::catch_unwind(AssertUnwindSafe(func)) {
            Ok(x) => JobResult::Ok(x),
            Err(x) => JobResult::Panic(x),
        }
    }
}

impl<L: Latch, F, R> Job for StackJob<L, F, R>
    where F: FnOnce() -> R
{
    unsafe fn execute(&self, mode: JobMode) {
        match mode {
            JobMode::Execute => {
                // Use a guard here to ensure that the latch is always set, even if
                // the function panics.
                struct PanicGuard<'a, L: Latch + 'a>(&'a L);
                impl<'a, L: Latch> Drop for PanicGuard<'a, L> {
                    fn drop(&mut self) {
                        self.0.set();
                    }
                }

                let _guard = PanicGuard(&self.latch);
                let func = (*self.func.get()).take().unwrap();
                (*self.result.get()) = Self::run_result(func);
            }
            JobMode::Abort => {
                self.latch.set();
            }
        }
    }
}
<commit_msg>change job to not use a trait object<commit_after>use latch::Latch;
#[cfg(feature = "nightly")]
use std::any::Any;
#[cfg(feature = "nightly")]
use std::panic::{self, AssertUnwindSafe};
use std::cell::UnsafeCell;
use std::mem;

enum JobResult<T> {
    None,
    Ok(T),
    #[cfg(feature = "nightly")]
    Panic(Box<Any + Send>),
}

/// A `Job` is used to advertise work for other threads that they may
/// want to steal. In accordance with time honored tradition, jobs are
/// arranged in a deque, so that thieves can take from the top of the
/// deque while the main worker manages the bottom of the deque. This
/// deque is managed by the `thread_pool` module.
pub trait Job {
    unsafe fn execute(&self, mode: JobMode);
}

pub enum JobMode {
    Execute,
    Abort,
}

/// Effectively a Job trait object. Each JobRef **must** be executed
/// exactly once, or else data may leak.
///
/// Internally, we store the job's data in a `*const ()` pointer.  The
/// true type is something like `*const StackJob<...>`, but we hide
/// it. We also carry the "execute fn" from the `Job` trait.
#[derive(Copy, Clone)]
pub struct JobRef {
    pointer: *const (),
    execute_fn: unsafe fn(*const (), mode: JobMode),
}

unsafe impl Send for JobRef {}
unsafe impl Sync for JobRef {}

impl JobRef {
    unsafe fn new<T>(data: *const T) -> JobRef
        where T: Job
    {
        let fn_ptr: unsafe fn(&T, JobMode) = <T as Job>::execute;

        // erase types:
        let fn_ptr: unsafe fn(*const (), JobMode) = mem::transmute(fn_ptr);
        let pointer = data as *const ();

        JobRef { pointer: pointer, execute_fn: fn_ptr }
    }

    #[inline]
    pub unsafe fn execute(&self, mode: JobMode) {
        (self.execute_fn)(self.pointer, mode)
    }
}

/// A job that will be owned by a stack slot. This means that when it
/// executes it need not free any heap data, the cleanup occurs when
/// the stack frame is later popped.
pub struct StackJob<L: Latch, F, R> {
    pub latch: L,
    func: UnsafeCell<Option<F>>,
    result: UnsafeCell<JobResult<R>>,
}

impl<L: Latch, F, R> StackJob<L, F, R>
    where F: FnOnce() -> R
{
    pub fn new(func: F, latch: L) -> StackJob<L, F, R> {
        StackJob {
            latch: latch,
            func: UnsafeCell::new(Some(func)),
            result: UnsafeCell::new(JobResult::None),
        }
    }

    pub unsafe fn as_job_ref(&self) -> JobRef {
        JobRef::new(self)
    }

    pub unsafe fn run_inline(self) -> R {
        self.func.into_inner().unwrap()()
    }

    #[cfg(not(feature = "nightly"))]
    pub unsafe fn into_result(self) -> R {
        match self.result.into_inner() {
            JobResult::None => panic!("job function panicked"),
            JobResult::Ok(x) => x,
        }
    }

    #[cfg(feature = "nightly")]
    pub unsafe fn into_result(self) -> R {
        match self.result.into_inner() {
            JobResult::None => unreachable!(),
            JobResult::Ok(x) => x,
            JobResult::Panic(x) => panic::resume_unwind(x),
        }
    }

    #[cfg(not(feature = "nightly"))]
    fn run_result(func: F) -> JobResult<R> {
        JobResult::Ok(func())
    }

    #[cfg(feature = "nightly")]
    fn run_result(func: F) -> JobResult<R> {
        // We assert that func is unwind-safe since it doesn't touch any of
        // our data and we will be propagating the panic back to the user at
        // the join() call.
        match panic::catch_unwind(AssertUnwindSafe(func)) {
            Ok(x) => JobResult::Ok(x),
            Err(x) => JobResult::Panic(x),
        }
    }
}

impl<L: Latch, F, R> Job for StackJob<L, F, R>
    where F: FnOnce() -> R
{
    unsafe fn execute(&self, mode: JobMode) {
        match mode {
            JobMode::Execute => {
                // Use a guard here to ensure that the latch is always set, even if
                // the function panics.
                struct PanicGuard<'a, L: Latch + 'a>(&'a L);
                impl<'a, L: Latch> Drop for PanicGuard<'a, L> {
                    fn drop(&mut self) {
                        self.0.set();
                    }
                }

                let _guard = PanicGuard(&self.latch);
                let func = (*self.func.get()).take().unwrap();
                (*self.result.get()) = Self::run_result(func);
            }
            JobMode::Abort => {
                self.latch.set();
            }
        }
    }
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

use ::DiskExt;
use ::utils;
use super::system::get_all_data;

use libc::statvfs;
use std::mem;
use std::fmt::{Debug, Error, Formatter};
use std::path::{Path, PathBuf};
use std::ffi::{OsStr, OsString};
use std::os::unix::ffi::OsStrExt;

/// Enum containing the different handled disks types.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum DiskType {
    /// HDD type.
    HDD,
    /// SSD type.
    SSD,
    /// Unknown type.
    Unknown(isize),
}

impl From<isize> for DiskType {
    fn from(t: isize) -> DiskType {
        match t {
            0 => DiskType::SSD,
            1 => DiskType::HDD,
            id => DiskType::Unknown(id),
        }
    }
}

fn find_type_for_name(name: &OsStr) -> DiskType {
    #![allow(or_fun_call)]
    /* turn "sda1" into "sda": */
    let mut trimmed: &[u8] = name.as_bytes();
    while trimmed.len() > 1 && trimmed[trimmed.len()-1] >= b'0' && trimmed[trimmed.len()-1] <= b'9' {
        trimmed = &trimmed[..trimmed.len()-1]
    }
    let trimmed: &OsStr = OsStrExt::from_bytes(trimmed);

    let path = Path::new("/sys/block/").to_owned()
        .join(trimmed)
        .join("queue/rotational");
    let rotational_int = get_all_data(path).unwrap_or(String::new()).trim().parse();
    DiskType::from(rotational_int.unwrap_or(-1))
}

#[cfg(target_pointer_width = "32")]
macro_rules! cast {
    ($x:expr) => { $x as u64 }
}

#[cfg(not(target_pointer_width = "32"))]
macro_rules! cast {
    ($x:expr) => { $x }
}

pub fn new(name: &OsStr, mount_point: &Path, file_system: &[u8]) -> Disk {
    let mount_point_cpath = utils::to_cpath(mount_point);
    let type_ = find_type_for_name(name);
    let mut total = 0;
    let mut available = 0;
    unsafe {
        let mut stat: statvfs = mem::zeroed();
        if statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat) == 0 {
            total = cast!(stat.f_bsize) * stat.f_blocks;
            available = cast!(stat.f_bsize) * stat.f_bavail;
        }
    }
    Disk {
        type_,
        name: name.to_owned(),
        file_system: file_system.to_owned(),
        mount_point: mount_point.to_owned(),
        total_space: cast!(total),
        available_space: cast!(available),
    }
}

/// Struct containing a disk information.
pub struct Disk {
    type_: DiskType,
    name: OsString,
    file_system: Vec<u8>,
    mount_point: PathBuf,
    total_space: u64,
    available_space: u64,
}

impl Debug for Disk {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        write!(fmt,
               "Disk({:?})[FS: {:?}][Type: {:?}] mounted on {:?}: {}/{} B",
               self.get_name(), self.get_file_system(), self.get_type(), self.get_mount_point(),
               self.get_available_space(), self.get_total_space())
    }
}

impl DiskExt for Disk {
    fn get_type(&self) -> DiskType {
        self.type_
    }

    fn get_name(&self) -> &OsStr {
        &self.name
    }

    fn get_file_system(&self) -> &[u8] {
        &self.file_system
    }

    fn get_mount_point(&self) -> &Path {
        &self.mount_point
    }

    fn get_total_space(&self) -> u64 {
        self.total_space
    }

    fn get_available_space(&self) -> u64 {
        self.available_space
    }

    fn update(&mut self) -> bool {
        unsafe {
            let mut stat: statvfs = mem::zeroed();
            let mount_point_cpath = utils::to_cpath(&self.mount_point);
            if statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat) == 0 {
                let tmp = cast!(stat.f_bsize) * stat.f_bavail;
                self.available_space = cast!(tmp);
                true
            } else {
                false
            }
        }
    }
}
<commit_msg>improve cast macro<commit_after>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

use ::DiskExt;
use ::utils;
use super::system::get_all_data;

use libc::statvfs;
use std::mem;
use std::fmt::{Debug, Error, Formatter};
use std::path::{Path, PathBuf};
use std::ffi::{OsStr, OsString};
use std::os::unix::ffi::OsStrExt;

/// Enum containing the different handled disks types.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum DiskType {
    /// HDD type.
    HDD,
    /// SSD type.
    SSD,
    /// Unknown type.
    Unknown(isize),
}

impl From<isize> for DiskType {
    fn from(t: isize) -> DiskType {
        match t {
            0 => DiskType::SSD,
            1 => DiskType::HDD,
            id => DiskType::Unknown(id),
        }
    }
}

fn find_type_for_name(name: &OsStr) -> DiskType {
    #![allow(or_fun_call)]
    /* turn "sda1" into "sda": */
    let mut trimmed: &[u8] = name.as_bytes();
    while trimmed.len() > 1 && trimmed[trimmed.len()-1] >= b'0' && trimmed[trimmed.len()-1] <= b'9' {
        trimmed = &trimmed[..trimmed.len()-1]
    }
    let trimmed: &OsStr = OsStrExt::from_bytes(trimmed);

    let path = Path::new("/sys/block/").to_owned()
        .join(trimmed)
        .join("queue/rotational");
    let rotational_int = get_all_data(path).unwrap_or(String::new()).trim().parse();
    DiskType::from(rotational_int.unwrap_or(-1))
}

macro_rules! cast {
    ($x:expr) => { u64::from($x) }
}

pub fn new(name: &OsStr, mount_point: &Path, file_system: &[u8]) -> Disk {
    let mount_point_cpath = utils::to_cpath(mount_point);
    let type_ = find_type_for_name(name);
    let mut total = 0;
    let mut available = 0;
    unsafe {
        let mut stat: statvfs = mem::zeroed();
        if statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat) == 0 {
            total = cast!(stat.f_bsize) * cast!(stat.f_blocks);
            available = cast!(stat.f_bsize) * cast!(stat.f_bavail);
        }
    }
    Disk {
        type_,
        name: name.to_owned(),
        file_system: file_system.to_owned(),
        mount_point: mount_point.to_owned(),
        total_space: cast!(total),
        available_space: cast!(available),
    }
}

/// Struct containing a disk information.
pub struct Disk {
    type_: DiskType,
    name: OsString,
    file_system: Vec<u8>,
    mount_point: PathBuf,
    total_space: u64,
    available_space: u64,
}

impl Debug for Disk {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        write!(fmt,
               "Disk({:?})[FS: {:?}][Type: {:?}] mounted on {:?}: {}/{} B",
               self.get_name(), self.get_file_system(), self.get_type(), self.get_mount_point(),
               self.get_available_space(), self.get_total_space())
    }
}

impl DiskExt for Disk {
    fn get_type(&self) -> DiskType {
        self.type_
    }

    fn get_name(&self) -> &OsStr {
        &self.name
    }

    fn get_file_system(&self) -> &[u8] {
        &self.file_system
    }

    fn get_mount_point(&self) -> &Path {
        &self.mount_point
    }

    fn get_total_space(&self) -> u64 {
        self.total_space
    }

    fn get_available_space(&self) -> u64 {
        self.available_space
    }

    fn update(&mut self) -> bool {
        unsafe {
            let mut stat: statvfs = mem::zeroed();
            let mount_point_cpath = utils::to_cpath(&self.mount_point);
            if statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat) == 0 {
                let tmp = cast!(stat.f_bsize) * cast!(stat.f_bavail);
                self.available_space = cast!(tmp);
                true
            } else {
                false
            }
        }
    }
}
<|endoftext|>


<commit_before>use bytecount::naive_count_32;
use regex::Regex;

lazy_static! {
    static ref PAT_TOKENS: Regex = Regex::new(r#"(?xm)
          (?:\(:)
        | (?:=>)
        | "
        | (?:\n\x20*)
        | =
        | \(
        | \)
        | \s+
        | [^\(\)=\s"]+"#).expect("Failed to compile lexer regex");
}

#[derive(Debug, PartialEq)]
pub enum Token<'a> {
    StartBlock(i32),
    RightParen,
    Rocket,
    Indent,
    Dedent,
    Text(i32, &'a str),
    Character(i32, char),
    Quote(i32),
}

pub fn lex(data: &str) -> Vec<Token> {
    let data_bytes = data.as_bytes();
    let mut lineno: i32 = 0;
    let mut last_match_start: usize = 0;
    let mut tokens: Vec<Token> = vec![];
    let mut indent: Vec<usize> = vec![0];
    let mut start_rocket = false;

    for pat_match in PAT_TOKENS.find_iter(data) {
        lineno += naive_count_32(&data_bytes[last_match_start..pat_match.start()], b'\n') as i32;
        last_match_start = pat_match.start();
        let token_text = pat_match.as_str();
        let bytes = token_text.as_bytes();
        let token = match bytes[0] {
            b')' => Token::RightParen,
            b'"' => Token::Quote(lineno),
            b'\n' => {
                tokens.push(Token::Character(lineno, '\n'));

                // If the line is empty, ignore it.
                if data_bytes.get(pat_match.end()) == Some(&b'\n') {
                    continue;
                }

                // Subtract one for the leading newline
                let new_indentation_level = token_text.len() - 1;

                let mut current_indentation_level =
                    *(indent.last().expect("Indentation stack is empty"));
                while new_indentation_level < current_indentation_level {
                    indent.pop();
                    current_indentation_level =
                        *(indent.last().expect("Indentation stack is empty"));
                    tokens.push(Token::Dedent);
                }

                if start_rocket {
                    indent.push(new_indentation_level);
                    tokens.push(Token::Indent);
                    start_rocket = false;
                    Token::Text(lineno, &token_text[new_indentation_level..])
                } else {
                    continue;
                }
            }
            b'(' => match bytes.get(1) {
                Some(&b':') => Token::StartBlock(lineno),
                None => Token::Character(lineno, '('),
                _ => panic!("Bad character matched: Expected ':' or nothing"),
            },
            b'=' => match bytes.get(1) {
                Some(&b'>') => {
                    start_rocket = true;
                    Token::Rocket
                }
                None => Token::Character(lineno, '='),
                _ => panic!("Bad character matched: Expected '>' or nothing"),
            },
            _ => Token::Text(lineno, token_text),
        };

        tokens.push(token)
    }

    while indent.len() > 1 {
        tokens.push(Token::Dedent);
        indent.pop();
    }

    tokens
}

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

    #[test]
    fn test_empty() {
        assert_eq!(lex(""), vec![]);
    }

    #[test]
    fn test_expression() {
        assert_eq!(
            lex(r#"(:foo bar (:a "b c") "baz" )"#),
            vec![
                Token::StartBlock(0),
                Token::Text(0, "foo"),
                Token::Text(0, " "),
                Token::Text(0, "bar"),
                Token::Text(0, " "),
                Token::StartBlock(0),
                Token::Text(0, "a"),
                Token::Text(0, " "),
                Token::Quote(0),
                Token::Text(0, "b"),
                Token::Text(0, " "),
                Token::Text(0, "c"),
                Token::Quote(0),
                Token::RightParen,
                Token::Text(0, " "),
                Token::Quote(0),
                Token::Text(0, "baz"),
                Token::Quote(0),
                Token::Text(0, " "),
                Token::RightParen,
            ]
        );
    }

    #[test]
    fn test_rocket() {
        assert_eq!(
            lex(
                r#"
(:note "a title" =>
  stuff 1

  stuff 2

  (:note =>
    more stuff

    second paragraph

  closing nested
"#
            ),
            vec![
                Token::Character(0, '\n'),
                Token::StartBlock(1),
                Token::Text(1, "note"),
                Token::Text(1, " "),
                Token::Quote(1),
                Token::Text(1, "a"),
                Token::Text(1, " "),
                Token::Text(1, "title"),
                Token::Quote(1),
                Token::Text(1, " "),
                Token::Rocket,
                Token::Character(1, '\n'),
                Token::Indent,
                Token::Text(1, " "),
                Token::Text(2, "stuff"),
                Token::Text(2, " "),
                Token::Text(2, "1"),
                Token::Character(2, '\n'),
                Token::Character(3, '\n'),
                Token::Text(4, "stuff"),
                Token::Text(4, " "),
                Token::Text(4, "2"),
                Token::Character(4, '\n'),
                Token::Character(5, '\n'),
                Token::StartBlock(6),
                Token::Text(6, "note"),
                Token::Text(6, " "),
                Token::Rocket,
                Token::Character(6, '\n'),
                Token::Indent,
                Token::Text(6, " "),
                Token::Text(7, "more"),
                Token::Text(7, " "),
                Token::Text(7, "stuff"),
                Token::Character(7, '\n'),
                Token::Character(8, '\n'),
                Token::Text(9, "second"),
                Token::Text(9, " "),
                Token::Text(9, "paragraph"),
                Token::Character(9, '\n'),
                Token::Character(10, '\n'),
                Token::Dedent,
                Token::Text(11, "closing"),
                Token::Text(11, " "),
                Token::Text(11, "nested"),
                Token::Character(11, '\n'),
                Token::Dedent,
            ]
        );
    }
}
<commit_msg>Fix extra indentation created by lexer<commit_after>use bytecount::naive_count_32;
use regex::Regex;

lazy_static! {
    static ref PAT_TOKENS: Regex = Regex::new(r#"(?xm)
          (?:\(:)
        | (?:=>)
        | "
        | (?:\n\x20*)
        | =
        | \(
        | \)
        | \s+
        | [^\(\)=\s"]+"#).expect("Failed to compile lexer regex");
}

#[derive(Debug, PartialEq)]
pub enum Token<'a> {
    StartBlock(i32),
    RightParen,
    Rocket,
    Indent,
    Dedent,
    Text(i32, &'a str),
    Character(i32, char),
    Quote(i32),
}

pub fn lex(data: &str) -> Vec<Token> {
    let data_bytes = data.as_bytes();
    let mut lineno: i32 = 0;
    let mut last_match_start: usize = 0;
    let mut tokens: Vec<Token> = vec![];
    let mut indent: Vec<usize> = vec![0];
    let mut start_rocket = false;

    for pat_match in PAT_TOKENS.find_iter(data) {
        lineno += naive_count_32(&data_bytes[last_match_start..pat_match.start()], b'\n') as i32;
        last_match_start = pat_match.start();
        let token_text = pat_match.as_str();
        let bytes = token_text.as_bytes();
        let token = match bytes[0] {
            b')' => Token::RightParen,
            b'"' => Token::Quote(lineno),
            b'\n' => {
                tokens.push(Token::Character(lineno, '\n'));

                // If the line is empty, ignore it.
                if data_bytes.get(pat_match.end()) == Some(&b'\n') {
                    continue;
                }

                // Subtract one for the leading newline
                let new_indentation_level = token_text.len() - 1;

                let mut current_indentation_level =
                    *(indent.last().expect("Indentation stack is empty"));
                while new_indentation_level < current_indentation_level {
                    indent.pop();
                    current_indentation_level =
                        *(indent.last().expect("Indentation stack is empty"));
                    tokens.push(Token::Dedent);
                }

                if start_rocket {
                    indent.push(new_indentation_level);
                    tokens.push(Token::Indent);
                    start_rocket = false;
                }

                continue;
            }
            b'(' => match bytes.get(1) {
                Some(&b':') => Token::StartBlock(lineno),
                None => Token::Character(lineno, '('),
                _ => panic!("Bad character matched: Expected ':' or nothing"),
            },
            b'=' => match bytes.get(1) {
                Some(&b'>') => {
                    start_rocket = true;
                    Token::Rocket
                }
                None => Token::Character(lineno, '='),
                _ => panic!("Bad character matched: Expected '>' or nothing"),
            },
            _ => Token::Text(lineno, token_text),
        };

        tokens.push(token)
    }

    while indent.len() > 1 {
        tokens.push(Token::Dedent);
        indent.pop();
    }

    tokens
}

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

    #[test]
    fn test_empty() {
        assert_eq!(lex(""), vec![]);
    }

    #[test]
    fn test_expression() {
        assert_eq!(
            lex(r#"(:foo bar (:a "b c") "baz" )"#),
            vec![
                Token::StartBlock(0),
                Token::Text(0, "foo"),
                Token::Text(0, " "),
                Token::Text(0, "bar"),
                Token::Text(0, " "),
                Token::StartBlock(0),
                Token::Text(0, "a"),
                Token::Text(0, " "),
                Token::Quote(0),
                Token::Text(0, "b"),
                Token::Text(0, " "),
                Token::Text(0, "c"),
                Token::Quote(0),
                Token::RightParen,
                Token::Text(0, " "),
                Token::Quote(0),
                Token::Text(0, "baz"),
                Token::Quote(0),
                Token::Text(0, " "),
                Token::RightParen,
            ]
        );
    }

    #[test]
    fn test_rocket() {
        assert_eq!(
            lex(
                r#"
(:note "a title" =>
  stuff 1

  stuff 2

  (:note =>
    more stuff

    second paragraph

  closing nested
"#
            ),
            vec![
                Token::Character(0, '\n'),
                Token::StartBlock(1),
                Token::Text(1, "note"),
                Token::Text(1, " "),
                Token::Quote(1),
                Token::Text(1, "a"),
                Token::Text(1, " "),
                Token::Text(1, "title"),
                Token::Quote(1),
                Token::Text(1, " "),
                Token::Rocket,
                Token::Character(1, '\n'),
                Token::Indent,
                Token::Text(2, "stuff"),
                Token::Text(2, " "),
                Token::Text(2, "1"),
                Token::Character(2, '\n'),
                Token::Character(3, '\n'),
                Token::Text(4, "stuff"),
                Token::Text(4, " "),
                Token::Text(4, "2"),
                Token::Character(4, '\n'),
                Token::Character(5, '\n'),
                Token::StartBlock(6),
                Token::Text(6, "note"),
                Token::Text(6, " "),
                Token::Rocket,
                Token::Character(6, '\n'),
                Token::Indent,
                Token::Text(7, "more"),
                Token::Text(7, " "),
                Token::Text(7, "stuff"),
                Token::Character(7, '\n'),
                Token::Character(8, '\n'),
                Token::Text(9, "second"),
                Token::Text(9, " "),
                Token::Text(9, "paragraph"),
                Token::Character(9, '\n'),
                Token::Character(10, '\n'),
                Token::Dedent,
                Token::Text(11, "closing"),
                Token::Text(11, " "),
                Token::Text(11, "nested"),
                Token::Character(11, '\n'),
                Token::Dedent,
            ]
        );
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;

pub enum Infinitable<T> {
	Finite(T),
	Infinity,
	NegativeInfinity,
}

impl<T> Infinitable<T> {
	pub fn is_finite(&self) -> bool {
		match *self {
			Infinitable::Finite(_) => true,
			_ => false,
		}
	}
}

impl<T> PartialEq for Infinitable<T> where T: PartialEq {
	fn eq(&self, other: &Self) -> bool {
		match (self, other) {
			(&Infinitable::Infinity, &Infinitable::Infinity)
			| (&Infinitable::NegativeInfinity, &Infinitable::NegativeInfinity)
				=> true,
			(&Infinitable::Finite(ref x), &Infinitable::Finite(ref y))
				=> x == y,
			(..)
				=> false,
		}
	}
}

impl<T> Eq for Infinitable<T> where T: Eq {
}

impl<T> PartialOrd for Infinitable<T> where T: PartialOrd {
	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
		match (self, other) {
			(&Infinitable::Infinity, &Infinitable::Infinity)
			| (&Infinitable::NegativeInfinity, &Infinitable::NegativeInfinity)
				=> Some(Ordering::Equal),
			(&Infinitable::Infinity, _)
			| (_, &Infinitable::NegativeInfinity)
				=> Some(Ordering::Greater),
			(&Infinitable::NegativeInfinity, _)
			| (_, &Infinitable::Infinity)
				=> Some(Ordering::Less),
			(&Infinitable::Finite(ref x), &Infinitable::Finite(ref y))
				=> x.partial_cmp(y),
		}
	}
}

impl<T> Ord for Infinitable<T> where T: Ord {
	fn cmp(&self, other: &Self) -> Ordering {
		match (self, other) {
			(&Infinitable::Finite(ref x), &Infinitable::Finite(ref y))
				=> x.cmp(y),
			(..)
				=> self.partial_cmp(other).unwrap(),
		}
	}
}
<commit_msg>Adjust match expression for consistency<commit_after>use std::cmp::Ordering;

pub enum Infinitable<T> {
	Finite(T),
	Infinity,
	NegativeInfinity,
}

impl<T> Infinitable<T> {
	pub fn is_finite(&self) -> bool {
		match self {
			&Infinitable::Finite(_) => true,
			_ => false,
		}
	}
}

impl<T> PartialEq for Infinitable<T> where T: PartialEq {
	fn eq(&self, other: &Self) -> bool {
		match (self, other) {
			(&Infinitable::Infinity, &Infinitable::Infinity)
			| (&Infinitable::NegativeInfinity, &Infinitable::NegativeInfinity)
				=> true,
			(&Infinitable::Finite(ref x), &Infinitable::Finite(ref y))
				=> x == y,
			(..)
				=> false,
		}
	}
}

impl<T> Eq for Infinitable<T> where T: Eq {
}

impl<T> PartialOrd for Infinitable<T> where T: PartialOrd {
	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
		match (self, other) {
			(&Infinitable::Infinity, &Infinitable::Infinity)
			| (&Infinitable::NegativeInfinity, &Infinitable::NegativeInfinity)
				=> Some(Ordering::Equal),
			(&Infinitable::Infinity, _)
			| (_, &Infinitable::NegativeInfinity)
				=> Some(Ordering::Greater),
			(&Infinitable::NegativeInfinity, _)
			| (_, &Infinitable::Infinity)
				=> Some(Ordering::Less),
			(&Infinitable::Finite(ref x), &Infinitable::Finite(ref y))
				=> x.partial_cmp(y),
		}
	}
}

impl<T> Ord for Infinitable<T> where T: Ord {
	fn cmp(&self, other: &Self) -> Ordering {
		match (self, other) {
			(&Infinitable::Finite(ref x), &Infinitable::Finite(ref y))
				=> x.cmp(y),
			(..)
				=> self.partial_cmp(other).unwrap(),
		}
	}
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

use std::str::{self, FromStr};
use nom::{IResult, ErrorKind, digit};

#[derive(Debug, PartialEq)]
pub enum LiteralValue {
    Null,
    Number(f32),
    String(String),
    Boolean(bool)
}

#[derive(Debug, PartialEq)]
pub enum Expression {
    ThisExpression,
    Literal {
        value: LiteralValue
    }
}

#[derive(Debug, PartialEq)]
pub struct VariableDeclarator {
    pub id: String,
    pub init: Option<Expression>
}

#[derive(Debug, PartialEq)]
pub enum Statement {
    VariableDeclaration {
        declarations: Vec<VariableDeclarator>,
        kind: String
    },
    If {
        test: Expression,
        consequent: Box<Statement>,
        alternate: Option<Box<Statement>>
    },
    Block {
        body: Vec<Statement>
    },
    Empty,
    Expression(Expression)
}

#[derive(Debug, PartialEq)]
pub struct Program {
    pub body: Vec<Statement>
}

fn var_name_char(c: char) -> bool {
    c.is_alphanumeric() || c == '_' || c == '$'
}

named!(variable_declarator< &str, VariableDeclarator >, do_parse!(
    return_error!(ErrorKind::Custom(42), peek!(none_of!("0123456789"))) >>
    // return_error!(ErrorKind::Custom(42), not!(tag!("1"))) >>
    // peek!(verify!(nom::anychar, |c: char| (c.is_alphabetic() || c == '_' || c == '$'))) >>
    id: take_while1_s!(var_name_char) >>
    init: opt!(
        do_parse!(
            ws!(tag!("=")) >>
            res: expression >>
            (res)
        )
    ) >>
    (VariableDeclarator {
        id: id.to_string(),
        init: init
    })
));

named!(if_statement< &str, Statement >, do_parse!(
    ws!(tag!("if")) >>
    test: delimited!(tag!("("), expression, tag!(")")) >>
    consequent: call!(block_statement) >>
    // opt!(complete!(…)) so it doesn't return an incomplete state
    // FIXME: isn't there a smarter way to handle this?
    alternate_opt: opt!(complete!(preceded!(ws!(tag!("else")), block_statement))) >>
    (Statement::If {
        test: test,
        consequent: Box::new(consequent),
        alternate: match alternate_opt {
            Some(alternate) => Some(Box::new(alternate)),
            None => None
        }
    })
));

named!(empty_statement< &str, Statement >, do_parse!(
    tag!(";") >>
    (Statement::Empty)
));

named!(block_statement< &str, Statement >, map!(
    ws!(delimited!(
        tag!("{"),
        call!(body),
        tag!("}")
    )), |body| (Statement::Block { body: body })
));

named!(variable_declaration_statement< &str, Statement >, do_parse!(
    var_type: tag!("var") >>
    take_while1_s!(char::is_whitespace) >>
    declarators: ws!(separated_list!(tag!(","), variable_declarator)) >>
    (Statement::VariableDeclaration {
        declarations: declarators,
        kind: var_type.to_string()
    })
));

named!(null_literal_value< &str, LiteralValue >, do_parse!(
    tag!("null") >>
    (LiteralValue::Null)
));

named!(boolean_literal_value< &str, LiteralValue >, alt!(
    tag!("true")  => { |_| LiteralValue::Boolean(true)  } |
    tag!("false") => { |_| LiteralValue::Boolean(false) }
));

// Largely inspired from nom (thanks to geal)
// https://github.com/Geal/nom/blob/66128e5ccf316f60fdd55a7ae8d266f42955b00c/benches/json.rs#L23-L48
// FIXME support hexadecimal, octal and binary expressions too
named!(number_literal_value< &str, LiteralValue >, map!(
    pair!(
        opt!(alt!(tag!("+") | tag!("-"))),
        map_res!(
            recognize!(
                alt_complete!(
                    delimited!(digit, tag!("."), opt!(complete!(digit))) |
                    delimited!(opt!(digit), tag!("."), digit)            |
                    digit
                )
            ),
            FromStr::from_str
        )
    ),
    |(sign, abs_value): (Option<&str>, f32)| {
        let value = if sign == Some("-") { -abs_value } else { abs_value };
        LiteralValue::Number(value)
    }
));

fn eat_string(input: &str) -> IResult< &str, &str > {
    if input.len() == 0 {
        return IResult::Incomplete(nom::Needed::Unknown);
    }
    let mut chars = input.chars();

    let separator = match chars.nth(0) {
        sep @ Some('\'') | sep @ Some('"') => sep.unwrap(),
        Some(_) | None => return nom::IResult::Error(error_position!(ErrorKind::Custom(43), input))
    };

    let mut escaped = false;
    let mut idx = 1;
    for item in chars {
        if escaped {
            escaped = false;
        } else {
            match item {
                c if c == separator => return IResult::Done(&input[idx+1..], &input[0..idx+1]),
                '\\' => escaped = true,
                '\n' => return IResult::Incomplete(nom::Needed::Unknown),
                _ => ()
            }
        }
        idx += 1;
    }

    return IResult::Error(error_position!(ErrorKind::Custom(43), input));
}

named!(string_literal_value< &str, LiteralValue >, do_parse!(
    string: call!(eat_string) >>
    (LiteralValue::String(string.to_string()))
));

named!(literal_value< &str, LiteralValue >, alt_complete!(
    number_literal_value |
    null_literal_value |
    boolean_literal_value |
    string_literal_value
));

named!(literal_expression< &str, Expression >, do_parse!(
    literal_value: call!(literal_value) >>
    (Expression::Literal {
        value: literal_value
    })
));

named!(this_expression< &str, Expression >, do_parse!(
    tag!("this") >>
    (Expression::ThisExpression)
));

named!(expression< &str, Expression >, ws!(alt_complete!(
    this_expression |
    literal_expression
)));

named!(expression_statement< &str, Statement >, do_parse!(
    expression: call!(expression) >>
    (Statement::Expression(expression))
));

named!(statement< &str, Statement >, alt_complete!(
    empty_statement |
    terminated!(variable_declaration_statement, tag!(";")) |
    terminated!(expression_statement, tag!(";")) |
    block_statement |
    if_statement
));

// TODO support ASI
named!(body< &str, Vec<Statement> >, ws!(
    many0!(statement)
));

named!(pub program< &str, Program >, do_parse!(
    body: call!(body) >>
    (Program {
        body: body
    })
));

#[cfg(test)]
mod tests {
    use super::*;
    use nom::{IResult, Needed};

    #[test]
    fn it_parses_declaration_expression() {
        assert_eq!(program("var test;"), IResult::Done("", Program {
            body: vec![
                Statement::VariableDeclaration {
                    declarations: vec![VariableDeclarator {
                        id: "test".to_string(),
                        init: None
                    }],
                    kind: "var".to_string()
                }
            ]
        }));
    }

    #[test]
    fn it_parses_multi_declaration_expression() {
        assert_eq!(program("var test,\n\t foo;"), IResult::Done("", Program {
            body: vec![
                Statement::VariableDeclaration {
                    declarations: vec![VariableDeclarator {
                        id: "test".to_string(),
                        init: None
                    }, VariableDeclarator {
                        id: "foo".to_string(),
                        init: None
                    }],
                    kind: "var".to_string()
                }
            ]
        }));
    }

    #[test]
    fn it_parses_multi_declaration_expression_with_initialisation() {
        assert_eq!(program("var test = this,\n\t foo = null;"), IResult::Done("", Program {
            body: vec![
                Statement::VariableDeclaration {
                    declarations: vec![VariableDeclarator {
                        id: "test".to_string(),
                        init: Some(Expression::ThisExpression)
                    }, VariableDeclarator {
                        id: "foo".to_string(),
                        init: Some(Expression::Literal {
                            value: LiteralValue::Null
                        })
                    }],
                    kind: "var".to_string()
                }
            ]
        }));
    }

    #[test]
    fn it_parses_statements_with_whitespaces_around() {
        assert_eq!(program(" var test;"), program("var test;"));
        assert_eq!(program(" var   test   ;"), program("var test;"));
        assert_eq!(program(" var   test   ;   ").to_result(), program("var test;").to_result());
    }

    #[test]
    fn it_fails_to_parse_var_names_beginning_with_nums() {
        let res = program("var 1test;");
        println!("FAIL HERE {:?}", res);
        assert!(res.is_err());
        assert!(false, "decris mieux!");
    }

    #[test]
    fn it_parses_this_expression() {
        assert_eq!(program("this;"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(Expression::ThisExpression)
            ]
        }));
    }

    #[test]
    fn it_parses_null_literal_expression() {
        assert_eq!(program("null;"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Null
                    }
                )
            ]
        }));
    }

    #[test]
    fn it_parses_decimal_number_literal_expression() {
        assert_eq!(program("42;   +42.042;   -42.4242;"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Number(42.0f32)
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Number(42.042f32)
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Number(-42.4242f32)
                    }
                )
            ]
        }));
    }

    // FIXME should pass…
    #[test]
    fn it_parses_octal_number_literal_expression() {
        assert_eq!(program("0o10"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Number(8f32)
                    }
                )
            ]
        }));
    }

    #[test]
    fn it_parses_string_literal_expression() {
        let res = program("\"foo\";   'foo'; \"foo$\\n \\\"bar\\\"\";");
        assert_eq!(res, IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::String("\"foo\"".to_string())
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::String("'foo'".to_string())
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::String("\"foo$\\n \\\"bar\\\"\"".to_string())
                    }
                )
            ]
        }));
    }

    #[test]
    fn it_parses_incomplete_string_literal_expression() {
        assert_eq!(program("\"foo   \r\n bar\";"), IResult::Incomplete(Needed::Unknown));
        assert_eq!(program("\"foo   \n bar\";"), IResult::Incomplete(Needed::Unknown));
    }

    #[test]
    fn it_parses_boolean_literal_expression() {
        assert_eq!(program("true; false;"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Boolean(true)
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Boolean(false)
                    }
                )
            ]
        }));
    }

    #[test]
    fn it_parses_if_statement() {
        // FIXME test with newlines in the test. ASI shouldn't work in it.
        // TODO

        assert_eq!(program("if ( true ) {\n 42; \n 43; \n}"), IResult::Done("", Program {
            body: vec![
                Statement::If {
                    test: Expression::Literal {
                        value: LiteralValue::Boolean(true)
                    },
                    consequent: Box::new(Statement::Block {
                        body: vec![
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(42f32)
                                }
                            ),
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(43f32)
                                }
                            )
                        ]
                    }),
                    alternate: None
                }
            ]
        }));
    }

    #[test]
    fn it_parses_if_else_statement() {
        // FIXME test with newlines in the test. ASI shouldn't work in it.
        assert_eq!(program("if (true) {\n 42; 43; \n} else { 44; 45; }"), IResult::Done("", Program {
            body: vec![
                Statement::If {
                    test: Expression::Literal {
                        value: LiteralValue::Boolean(true)
                    },
                    consequent: Box::new(Statement::Block {
                        body: vec![
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(42f32)
                                }
                            ),
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(43f32)
                                }
                            )
                        ]
                    }),
                    alternate: Some(Box::new(Statement::Block{
                        body: vec![
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(44f32)
                                }
                            ),
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(45f32)
                                }
                            )
                        ]
                    }))
                }
            ]
        }));
    }

    #[test]
    fn it_parses_block_statement() {
        assert_eq!(program("{ true; var test; }"), IResult::Done("", Program {
            body: vec![
                Statement::Block {
                    body: vec![
                        Statement::Expression(
                            Expression::Literal {
                                value: LiteralValue::Boolean(true)
                            }
                        ),
                        Statement::VariableDeclaration {
                            declarations: vec![VariableDeclarator {
                                id: "test".to_string(),
                                init: None
                            }],
                            kind: "var".to_string()
                        }
                    ]
                }
            ]
        }));
    }

    #[test]
    fn it_parses_empty_statement() {
        let res = program(";");
        let with_space = program("  ;");
        assert_eq!(res, IResult::Done("", Program {
            body: vec![
                Statement::Empty
            ]
        }));
        assert_eq!(with_space, res);
    }

}
<commit_msg>little improvements<commit_after>#[macro_use]
extern crate nom;

use std::str::{self, FromStr};
use nom::{IResult, ErrorKind, digit};

#[derive(Debug, PartialEq)]
pub enum LiteralValue {
    Null,
    Number(f32),
    String(String),
    Boolean(bool)
}

#[derive(Debug, PartialEq)]
pub enum Expression {
    ThisExpression,
    Literal {
        value: LiteralValue
    }
}

#[derive(Debug, PartialEq)]
pub struct VariableDeclarator {
    pub id: String,
    pub init: Option<Expression>
}

#[derive(Debug, PartialEq)]
pub enum Statement {
    VariableDeclaration {
        declarations: Vec<VariableDeclarator>,
        kind: String
    },
    If {
        test: Expression,
        consequent: Box<Statement>,
        alternate: Option<Box<Statement>>
    },
    Block {
        body: Vec<Statement>
    },
    Empty,
    Expression(Expression)
}

#[derive(Debug, PartialEq)]
pub struct Program {
    pub body: Vec<Statement>
}

fn var_name_char(c: char) -> bool {
    c.is_alphanumeric() || c == '_' || c == '$'
}

named!(variable_declarator< &str, VariableDeclarator >, do_parse!(
    return_error!(ErrorKind::Custom(42), peek!(none_of!("0123456789"))) >>
    // return_error!(ErrorKind::Custom(42), not!(tag!("1"))) >>
    // peek!(verify!(nom::anychar, |c: char| (c.is_alphabetic() || c == '_' || c == '$'))) >>
    id: take_while1_s!(var_name_char) >>
    init: opt!(
        do_parse!(
            ws!(tag!("=")) >>
            res: expression >>
            (res)
        )
    ) >>
    (VariableDeclarator {
        id: id.to_string(),
        init: init
    })
));

named!(if_statement< &str, Statement >, do_parse!(
    ws!(tag!("if")) >>
    test: delimited!(tag!("("), expression, tag!(")")) >>
    consequent: call!(block_statement) >>
    // opt!(complete!(…)) so it doesn't return an incomplete state
    // FIXME: isn't there a smarter way to handle this?
    alternate_opt: opt!(complete!(preceded!(ws!(tag!("else")), block_statement))) >>
    (Statement::If {
        test: test,
        consequent: Box::new(consequent),
        alternate: match alternate_opt {
            Some(alternate) => Some(Box::new(alternate)),
            None => None
        }
    })
));

named!(empty_statement< &str, Statement >, do_parse!(
    tag!(";") >>
    (Statement::Empty)
));

named!(block_statement< &str, Statement >, map!(
    ws!(delimited!(
        tag!("{"),
        call!(body),
        tag!("}")
    )), |body| (Statement::Block { body: body })
));

named!(variable_declaration_statement< &str, Statement >, do_parse!(
    var_type: tag!("var") >>
    take_while1_s!(char::is_whitespace) >>
    declarators: ws!(separated_list!(tag!(","), variable_declarator)) >>
    (Statement::VariableDeclaration {
        declarations: declarators,
        kind: var_type.to_string()
    })
));

named!(null_literal_value< &str, LiteralValue >, do_parse!(
    tag!("null") >>
    (LiteralValue::Null)
));

named!(boolean_literal_value< &str, LiteralValue >, alt!(
    tag!("true")  => { |_| LiteralValue::Boolean(true)  } |
    tag!("false") => { |_| LiteralValue::Boolean(false) }
));

// Largely inspired from nom (thanks to geal)
// https://github.com/Geal/nom/blob/66128e5ccf316f60fdd55a7ae8d266f42955b00c/benches/json.rs#L23-L48
// FIXME support hexadecimal, octal and binary expressions too
named!(number_literal_value< &str, LiteralValue >, map!(
    pair!(
        opt!(alt!(tag!("+") | tag!("-"))),
        map_res!(
            recognize!(
                alt_complete!(
                    delimited!(digit, tag!("."), opt!(complete!(digit))) |
                    delimited!(opt!(digit), tag!("."), digit)            |
                    digit
                )
            ),
            FromStr::from_str
        )
    ),
    |(sign, abs_value): (Option<&str>, f32)| {
        let value = if sign == Some("-") { -abs_value } else { abs_value };
        LiteralValue::Number(value)
    }
));

fn eat_string(input: &str) -> IResult< &str, &str > {
    if input.len() == 0 {
        return IResult::Incomplete(nom::Needed::Unknown);
    }
    let mut chars = input.char_indices();

    let separator = match chars.nth(0) {
        sep @ Some((_, '\'')) | sep @ Some((_, '"')) => sep.unwrap().1,
        // FIXME meaningfull error codes
        Some(_) | None => return nom::IResult::Error(error_position!(ErrorKind::Custom(43), input))
    };

    let mut escaped = false;
    for (idx, item) in chars {
        if escaped {
            escaped = false;
        } else {
            match item {
                c if c == separator => return IResult::Done(&input[idx+1..], &input[0..idx+1]),
                '\\' => escaped = true,
                '\n' => return IResult::Incomplete(nom::Needed::Unknown),
                _ => ()
            }
        }
    }

    return IResult::Error(error_position!(ErrorKind::Custom(43), input));
}

named!(string_literal_value< &str, LiteralValue >, do_parse!(
    string: call!(eat_string) >>
    (LiteralValue::String(string.to_string()))
));

named!(literal_value< &str, LiteralValue >, alt_complete!(
    number_literal_value |
    null_literal_value |
    boolean_literal_value |
    string_literal_value
));

named!(literal_expression< &str, Expression >, do_parse!(
    literal_value: call!(literal_value) >>
    (Expression::Literal {
        value: literal_value
    })
));

named!(this_expression< &str, Expression >, do_parse!(
    tag!("this") >>
    (Expression::ThisExpression)
));

named!(expression< &str, Expression >, ws!(alt_complete!(
    this_expression |
    literal_expression
)));

named!(expression_statement< &str, Statement >, do_parse!(
    expression: call!(expression) >>
    (Statement::Expression(expression))
));

named!(statement< &str, Statement >, alt_complete!(
    empty_statement |
    terminated!(variable_declaration_statement, tag!(";")) |
    terminated!(expression_statement, tag!(";")) |
    block_statement |
    if_statement
));

// TODO support ASI
named!(body< &str, Vec<Statement> >, ws!(
    many0!(statement)
));

named!(pub program< &str, Program >, do_parse!(
    body: call!(body) >>
    (Program {
        body: body
    })
));

#[cfg(test)]
mod tests {
    use super::*;
    use nom::{IResult, Needed};

    #[test]
    fn it_parses_declaration_expression() {
        assert_eq!(program("var test;"), IResult::Done("", Program {
            body: vec![
                Statement::VariableDeclaration {
                    declarations: vec![VariableDeclarator {
                        id: "test".to_string(),
                        init: None
                    }],
                    kind: "var".to_string()
                }
            ]
        }));
    }

    #[test]
    fn it_parses_multi_declaration_expression() {
        assert_eq!(program("var test,\n\t foo;"), IResult::Done("", Program {
            body: vec![
                Statement::VariableDeclaration {
                    declarations: vec![VariableDeclarator {
                        id: "test".to_string(),
                        init: None
                    }, VariableDeclarator {
                        id: "foo".to_string(),
                        init: None
                    }],
                    kind: "var".to_string()
                }
            ]
        }));
    }

    #[test]
    fn it_parses_multi_declaration_expression_with_initialisation() {
        assert_eq!(program("var test = this,\n\t foo = null;"), IResult::Done("", Program {
            body: vec![
                Statement::VariableDeclaration {
                    declarations: vec![VariableDeclarator {
                        id: "test".to_string(),
                        init: Some(Expression::ThisExpression)
                    }, VariableDeclarator {
                        id: "foo".to_string(),
                        init: Some(Expression::Literal {
                            value: LiteralValue::Null
                        })
                    }],
                    kind: "var".to_string()
                }
            ]
        }));
    }

    #[test]
    fn it_parses_statements_with_whitespaces_around() {
        assert_eq!(program(" var test;"), program("var test;"));
        assert_eq!(program(" var   test   ;"), program("var test;"));
        assert_eq!(program(" var   test   ;   ").to_result(), program("var test;").to_result());
    }

    #[test]
    fn it_fails_to_parse_var_names_beginning_with_nums() {
        let res = program("var 1test;");
        println!("FAIL HERE {:?}", res);
        assert!(res.is_err());
        assert!(false, "decris mieux!");
    }

    #[test]
    fn it_parses_this_expression() {
        assert_eq!(program("this;"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(Expression::ThisExpression)
            ]
        }));
    }

    #[test]
    fn it_parses_null_literal_expression() {
        assert_eq!(program("null;"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Null
                    }
                )
            ]
        }));
    }

    #[test]
    fn it_parses_decimal_number_literal_expression() {
        assert_eq!(program("42;   +42.042;   -42.4242;"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Number(42.0f32)
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Number(42.042f32)
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Number(-42.4242f32)
                    }
                )
            ]
        }));
    }

    // FIXME should pass…
    #[test]
    fn it_parses_octal_number_literal_expression() {
        assert_eq!(program("0o10"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Number(8f32)
                    }
                )
            ]
        }));
    }

    #[test]
    fn it_parses_string_literal_expression() {
        let res = program("\"foo\";   'foo'; \"foo$\\n \\\"bar\\\"\";");
        assert_eq!(res, IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::String("\"foo\"".to_string())
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::String("'foo'".to_string())
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::String("\"foo$\\n \\\"bar\\\"\"".to_string())
                    }
                )
            ]
        }));
    }

    #[test]
    fn it_parses_incomplete_string_literal_expression() {
        assert_eq!(program("\"foo   \r\n bar\";"), IResult::Incomplete(Needed::Unknown));
        assert_eq!(program("\"foo   \n bar\";"), IResult::Incomplete(Needed::Unknown));
    }

    #[test]
    fn it_parses_boolean_literal_expression() {
        assert_eq!(program("true; false;"), IResult::Done("", Program {
            body: vec![
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Boolean(true)
                    }
                ),
                Statement::Expression(
                    Expression::Literal {
                        value: LiteralValue::Boolean(false)
                    }
                )
            ]
        }));
    }

    #[test]
    fn it_parses_if_statement() {
        // FIXME test with newlines in the test. ASI shouldn't work in it.
        // TODO

        assert_eq!(program("if ( true ) {\n 42; \n 43; \n}"), IResult::Done("", Program {
            body: vec![
                Statement::If {
                    test: Expression::Literal {
                        value: LiteralValue::Boolean(true)
                    },
                    consequent: Box::new(Statement::Block {
                        body: vec![
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(42f32)
                                }
                            ),
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(43f32)
                                }
                            )
                        ]
                    }),
                    alternate: None
                }
            ]
        }));
    }

    #[test]
    fn it_parses_if_else_statement() {
        // FIXME test with newlines in the test. ASI shouldn't work in it.
        assert_eq!(program("if (true) {\n 42; 43; \n} else { 44; 45; }"), IResult::Done("", Program {
            body: vec![
                Statement::If {
                    test: Expression::Literal {
                        value: LiteralValue::Boolean(true)
                    },
                    consequent: Box::new(Statement::Block {
                        body: vec![
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(42f32)
                                }
                            ),
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(43f32)
                                }
                            )
                        ]
                    }),
                    alternate: Some(Box::new(Statement::Block{
                        body: vec![
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(44f32)
                                }
                            ),
                            Statement::Expression(
                                Expression::Literal {
                                    value: LiteralValue::Number(45f32)
                                }
                            )
                        ]
                    }))
                }
            ]
        }));
    }

    #[test]
    fn it_parses_block_statement() {
        assert_eq!(program("{ true; var test; }"), IResult::Done("", Program {
            body: vec![
                Statement::Block {
                    body: vec![
                        Statement::Expression(
                            Expression::Literal {
                                value: LiteralValue::Boolean(true)
                            }
                        ),
                        Statement::VariableDeclaration {
                            declarations: vec![VariableDeclarator {
                                id: "test".to_string(),
                                init: None
                            }],
                            kind: "var".to_string()
                        }
                    ]
                }
            ]
        }));
    }

    #[test]
    fn it_parses_empty_block_statement() {
        assert_eq!(program("{}"), IResult::Done("", Program {
            body: vec![
                Statement::Block {
                    body: vec![]
                }
            ]
        }));
    }

    #[test]
    fn it_parses_empty_statement() {
        let res = program(";");
        let with_space = program("  ;");
        assert_eq!(res, IResult::Done("", Program {
            body: vec![
                Statement::Empty
            ]
        }));
        assert_eq!(with_space, res);
    }

//    fn it_parses_assignment_expression() {
//        let assignment_operators = ["=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "|=", "^=", "&="];
//        for assigment_operator in assignment_operators {
//            assert_eq!(program("a " + assignment_operators + " 42"
//    }
}
<|endoftext|>


<commit_before>#![feature(no_std)]
#![feature(asm, core)]
#![feature(libc, page_size)]
#![no_std]

#[macro_use]
extern crate core;

#[cfg(test)]
#[macro_use]
extern crate std;

pub use context::Context;
pub use stack::{Stack, StackSource};

#[cfg(not(test))]
mod std { pub use core::*; }

pub mod context;
pub mod stack;

mod arch;

#[cfg(feature = "os")]
pub mod os;
<commit_msg>limit compilation of arch to x86_64<commit_after>#![feature(no_std)]
#![feature(asm, core)]
#![feature(libc, page_size)]
#![no_std]

#[macro_use]
extern crate core;

#[cfg(test)]
#[macro_use]
extern crate std;

pub use context::Context;
pub use stack::{Stack, StackSource};

#[cfg(not(test))]
mod std { pub use core::*; }

pub mod context;
pub mod stack;

#[cfg(target_arch = "x86_64")]
mod arch;

#[cfg(feature = "os")]
pub mod os;
<|endoftext|>


<commit_before>#![feature(
    btree_range,
    collections,
    collections_bound,
    question_mark,
    rustc_private,
    pub_restricted,
)]

// From rustc.
#[macro_use] extern crate rustc;
extern crate rustc_borrowck;
extern crate rustc_data_structures;
extern crate rustc_mir;
extern crate rustc_trans;
extern crate rustc_const_math;
extern crate syntax;
#[macro_use] extern crate log;
extern crate log_settings;

// From crates.io.
extern crate byteorder;

mod error;
mod interpreter;
mod memory;
mod primval;

pub use error::{
    EvalError,
    EvalResult,
};

pub use interpreter::{
    CachedMir,
    EvalContext,
    Frame,
    eval_main,
    run_mir_passes,
};

pub use memory::{
    Memory,
    Pointer,
    AllocId,
};
<commit_msg>Remove unused extern crate rustc_trans.<commit_after>#![feature(
    btree_range,
    collections,
    collections_bound,
    question_mark,
    rustc_private,
    pub_restricted,
)]

// From rustc.
#[macro_use] extern crate rustc;
extern crate rustc_borrowck;
extern crate rustc_data_structures;
extern crate rustc_mir;
extern crate rustc_const_math;
extern crate syntax;
#[macro_use] extern crate log;
extern crate log_settings;

// From crates.io.
extern crate byteorder;

mod error;
mod interpreter;
mod memory;
mod primval;

pub use error::{
    EvalError,
    EvalResult,
};

pub use interpreter::{
    CachedMir,
    EvalContext,
    Frame,
    eval_main,
    run_mir_passes,
};

pub use memory::{
    Memory,
    Pointer,
    AllocId,
};
<|endoftext|>


<commit_before>//! Cookie factory
#![feature(trace_macros)]
//macros:
//* write_vec!(vec, ...) -> Result(nb of bytes)
//* write_slice!(s, ...) -> Result(nb of bytes)
//* write_iterator!(it, ...) -> Result(nb of bytes)
//
//* config can be endianness
// combinator: c!(writer, config, conbinator/serializer)

#[macro_export]
macro_rules! write_vec (
  ($vec: expr,  $submac:ident!($($args:tt)*)) => (
    {
      $submac!(write_vec_impl!($vec), $($args)*)
    }
  )
);

#[macro_export]
macro_rules! write_vec_impl (
  ($vec:expr, $config:expr, Slice, $value: expr) => (
    {
      ($vec).extend(($value).iter().cloned());
      Ok(($value).len())
    }
  );
  ($vec:expr, $config:expr, Byte, $value: expr) => (
    {
      ($vec).push($value);
      let res:Result<usize,()> = Ok(1usize);
      res
    }
  );
);

#[macro_export]
macro_rules! write_slice (
  ($sl:expr, $submac:ident!($($args:tt)*)) => (
    {
      let mut pos = 0;
      $submac!(write_slice_impl!(($sl, pos)), $($args)*)
    }
  );
);

#[macro_export]
macro_rules! write_slice_impl (
  (($sl:expr, $pos:expr), $config:expr, Slice, $value: expr) => (
    {
      if ($value).len() < $sl.len() - $pos {
        std::slice::bytes::copy_memory($value, &mut ($sl)[$pos..($value.length)]);
        $pos += value.length;
        Ok(($value).len())
      } else {
        Err(())
      }
    }
  );
  (($sl:expr, $pos:expr), $config:expr, Byte, $value: expr) => (
    {
      if $sl.len() - $pos > 0 {
        $sl[$pos] = $value as u8;
        $pos += 1;
        Ok(1)
      } else {
        Err(())
      }
    }
  );
);

#[macro_export]
macro_rules! opt (
  ($writer:ident!($($wargs:tt)*), $config:expr, $submac:ident!( $($args:tt)* )) => (
    {
      match $submac!($writer!($($wargs)*), $config, $($args)*) {
        Ok(nb) => Ok(nb),
        Err(_) => Ok(0)
      }
    }
  )
);

#[macro_export]
macro_rules! s_many (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr, $submac:ident!( $($args:tt)* )) => (
    {
      for el in $value {
        $submac:ident!( $writer!($($wargs)*), $config, $($args:tt)*, el )
      }
      Ok(42usize)
    }
  );
);

#[macro_export]
macro_rules! s_chain (
  ($writer:ident!($($wargs:tt)*), $config:expr, $($rest:tt)* ) => (
    s_chain_impl!($writer!($($wargs)*), $config, 0, $($rest)* )
  );
);

#[macro_export]
macro_rules! s_chain_impl (
  ($writer:ident!($($wargs:tt)*), $config:expr, $acc:expr, $submac:ident!( $($args:tt)* ) ~ $($rest:tt)*) => (
    {
      match $submac!( $writer!($($wargs)*), $config, $($args)*) {
        Err(e) => Err(e),
        Ok(i)  => s_chain_impl!($writer!($($wargs)*), $config, $acc + i, $($rest)*)
      }
    }
  );
  ($writer:ident!($($wargs:tt)*), $config:expr, $acc:expr, $submac:ident!( $($args:tt)* )) => (
    {
      match $submac!( $writer!($($wargs)*), $config, $($args)*) {
        Err(e) => Err(e),
        Ok(i)  => Ok($acc + i)
      }
    }
  );
);

#[macro_export]
macro_rules! s_apply (
  ($writer:ident!($($wargs:tt)*), $config:expr, $($args:tt)*) => (
    {
      $writer!($($wargs)*, $config, $($args)*)
    }
  );
);

#[macro_export]
macro_rules! s_u8 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, $value)
    }
  );
);

#[macro_export]
macro_rules! s_u16 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      s_chain!($writer!($($wargs)*), $config,
        s_apply!(Byte, ($value >> 8) as u8)  ~
        s_apply!(Byte, $value as u8)
      )
    }
  );
);

#[macro_export]
macro_rules! s_u32 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      s_chain!($writer!($($wargs)*), $config,
        s_apply!(Byte, ($value >> 24) as u8) ~
        s_apply!(Byte, ($value >> 16) as u8) ~
        s_apply!(Byte, ($value >> 8) as u8)  ~
        s_apply!(Byte, $value as u8)
      )
    }
  );
);

#[cfg(test)]
mod tests {
  use std::iter;

  #[test]
  fn writer_vec() {
    trace_macros!(true);
    let mut v = Vec::new();
    let res:Result<usize,()> = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_slice() {
    trace_macros!(true);
    let mut v   = Vec::with_capacity(4);
    v.extend(iter::repeat(0).take(4));
    let res = write_slice!(&mut v[..], s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_array() {
    trace_macros!(true);
    let mut v = Vec::new();
    let input = vec![0x7f, 0xff, 0xff, 0xff];

    let res = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(res, Ok(4));
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
    //assert!(false);
  }
}
<commit_msg>Remove previous macros<commit_after>//! Cookie factory
#![feature(trace_macros)]
<|endoftext|>