<commit_before>
use error::{StoreError, StoreErrorKind};
use std::io::{Seek, SeekFrom};
use std::path::{Path, PathBuf};
use std::fs::{File, OpenOptions};

pub enum LazyFile {
    Absent(PathBuf),
    File(File)
}

fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
    OpenOptions::new().write(true).read(true).open(p)
}

fn create_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
    OpenOptions::new().write(true).read(true).create(true).open(p)
}

impl LazyFile {
    pub fn new(p: PathBuf) -> LazyFile {
        LazyFile::Absent(p)
    }
    pub fn new_with_file(f: File) -> LazyFile {
        LazyFile::File(f)
    }

    pub fn get_file(&mut self) -> Result<&File, StoreError> {
        match self.get_file_mut() {
            Ok(file) => Ok(&*file),
            Err(e) => Err(e)
        }
    }

    pub fn get_file_mut(&mut self) -> Result<&mut File, StoreError> {
        let file = match *self {
            LazyFile::File(ref mut f) => return {
                // We seek to the beginning of the file since we expect each
                // access to the file to be in a different context
                f.seek(SeekFrom::Start(0)).map_err(|e|
                    StoreError::new(
                        StoreErrorKind::FileNotCreated, Some(Box::new(e))));
                Ok(f)
            },
            LazyFile::Absent(ref p) => {
                try!(open_file(p).map_err(|e| {
                    StoreError::new(StoreErrorKind::FileNotFound,
                                    Some(Box::new(e)))
                }))
            }
        };
        *self = LazyFile::File(file);
        if let LazyFile::File(ref mut f) = *self {
            return Ok(f);
        }
        unreachable!()
    }

    pub fn create_file(&mut self) -> Result<&mut File, StoreError> {
        let file = match *self {
            LazyFile::File(ref mut f) => return Ok(f),
            LazyFile::Absent(ref p) => {
                try!(create_file(p).map_err(|e| {
                    StoreError::new(StoreErrorKind::FileNotFound,
                                    Some(Box::new(e)))
                }))
            }
        };
        *self = LazyFile::File(file);
        if let LazyFile::File(ref mut f) = *self {
            return Ok(f);
        }
        unreachable!()
    }
}

#[cfg(test)]
mod test {
    use super::LazyFile;
    use std::io::{Read, Write, Seek, SeekFrom};
    use std::path::PathBuf;
    use std::fs::File;
    use tempdir::TempDir;

    fn get_dir() -> TempDir {
        TempDir::new("test-image").unwrap()
    }

    #[test]
    fn lazy_file() {
        let dir = get_dir();
        let mut path = PathBuf::from(dir.path());
        path.set_file_name("test1");
        let mut lf = LazyFile::new(path);

        write!(lf.create_file().unwrap(), "Hello World").unwrap();
        dir.close().unwrap();
    }

    #[test]
    fn lazy_file_with_file() {
        let dir = get_dir();
        let mut path = PathBuf::from(dir.path());
        path.set_file_name("test2");
        let mut lf = LazyFile::new(path.clone());

        {
            let mut file = lf.create_file().unwrap();

            file.write(b"Hello World").unwrap();
            file.sync_all().unwrap();
        }

        {
            let mut file = lf.get_file().unwrap();
            let mut s = Vec::new();
            file.read_to_end(&mut s).unwrap();
            assert_eq!(s, "Hello World".to_string().into_bytes());
        }

        dir.close().unwrap();
    }
}
<commit_msg>lazyfile.rs: Add documentation<commit_after>
use error::{StoreError, StoreErrorKind};
use std::io::{Seek, SeekFrom};
use std::path::{Path, PathBuf};
use std::fs::{File, OpenOptions};

/**
 * LazyFile type
 *
 * A lazy file is either absent, but a path to it is available, or it is present.
 */
pub enum LazyFile {
    Absent(PathBuf),
    File(File)
}

fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
    OpenOptions::new().write(true).read(true).open(p)
}

fn create_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
    OpenOptions::new().write(true).read(true).create(true).open(p)
}

impl LazyFile {

    /**
     * Create a new LazyFile instance from a Path
     */
    pub fn new(p: PathBuf) -> LazyFile {
        LazyFile::Absent(p)
    }

    /**
     * Create a new LazyFile instance from an already existing file
     */
    pub fn new_with_file(f: File) -> LazyFile {
        LazyFile::File(f)
    }

    /**
     * Get the file behind a LazyFile object
     */
    pub fn get_file(&mut self) -> Result<&File, StoreError> {
        match self.get_file_mut() {
            Ok(file) => Ok(&*file),
            Err(e) => Err(e)
        }
    }

    /**
     * Get the mutable file behind a LazyFile object
     */
    pub fn get_file_mut(&mut self) -> Result<&mut File, StoreError> {
        let file = match *self {
            LazyFile::File(ref mut f) => return {
                // We seek to the beginning of the file since we expect each
                // access to the file to be in a different context
                f.seek(SeekFrom::Start(0)).map_err(|e|
                    StoreError::new(
                        StoreErrorKind::FileNotCreated, Some(Box::new(e))));
                Ok(f)
            },
            LazyFile::Absent(ref p) => {
                try!(open_file(p).map_err(|e| {
                    StoreError::new(StoreErrorKind::FileNotFound,
                                    Some(Box::new(e)))
                }))
            }
        };
        *self = LazyFile::File(file);
        if let LazyFile::File(ref mut f) = *self {
            return Ok(f);
        }
        unreachable!()
    }

    /**
     * Create a file out of this LazyFile object
     */
    pub fn create_file(&mut self) -> Result<&mut File, StoreError> {
        let file = match *self {
            LazyFile::File(ref mut f) => return Ok(f),
            LazyFile::Absent(ref p) => {
                try!(create_file(p).map_err(|e| {
                    StoreError::new(StoreErrorKind::FileNotFound,
                                    Some(Box::new(e)))
                }))
            }
        };
        *self = LazyFile::File(file);
        if let LazyFile::File(ref mut f) = *self {
            return Ok(f);
        }
        unreachable!()
    }
}

#[cfg(test)]
mod test {
    use super::LazyFile;
    use std::io::{Read, Write, Seek, SeekFrom};
    use std::path::PathBuf;
    use std::fs::File;
    use tempdir::TempDir;

    fn get_dir() -> TempDir {
        TempDir::new("test-image").unwrap()
    }

    #[test]
    fn lazy_file() {
        let dir = get_dir();
        let mut path = PathBuf::from(dir.path());
        path.set_file_name("test1");
        let mut lf = LazyFile::new(path);

        write!(lf.create_file().unwrap(), "Hello World").unwrap();
        dir.close().unwrap();
    }

    #[test]
    fn lazy_file_with_file() {
        let dir = get_dir();
        let mut path = PathBuf::from(dir.path());
        path.set_file_name("test2");
        let mut lf = LazyFile::new(path.clone());

        {
            let mut file = lf.create_file().unwrap();

            file.write(b"Hello World").unwrap();
            file.sync_all().unwrap();
        }

        {
            let mut file = lf.get_file().unwrap();
            let mut s = Vec::new();
            file.read_to_end(&mut s).unwrap();
            assert_eq!(s, "Hello World".to_string().into_bytes());
        }

        dir.close().unwrap();
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use toml::Value;

use store::Result;
use error::{StoreError as SE, StoreErrorKind as SEK};

pub trait TomlValueExt {
    fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool>;
    fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>>;
    fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>>;
    fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>>;

    #[inline]
    fn insert(&mut self, spec: &str, v: Value) -> Result<bool> {
        self.insert_with_sep(spec, '.', v)
    }

    #[inline]
    fn set(&mut self, spec: &str, v: Value) -> Result<Option<Value>> {
        self.set_with_sep(spec, '.', v)
    }

    #[inline]
    fn read(&self, spec: &str) -> Result<Option<Value>> {
        self.read_with_sep(spec, '.')
    }

    #[inline]
    fn delete(&mut self, spec: &str) -> Result<Option<Value>> {
        self.delete_with_sep(spec, '.')
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
enum Token {
    Key(String),
    Index(usize),
}

impl TomlValueExt for Value {
    /**
     * Insert a header field by a string-spec
     *
     * ```ignore
     *  insert("something.in.a.field", Boolean(true));
     * ```
     *
     * If an array field was accessed which is _out of bounds_ of the array available, the element
     * is appended to the array.
     *
     * Inserts a Boolean in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * Returns true if header field was set, false if there is already a value
     */
    fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool> {
        let tokens = match tokenize(spec, sep) {
            Err(e) => return Err(e),
            Ok(t) => t
        };

        let destination = match tokens.iter().last() {
            None => return Err(SE::new(SEK::HeaderPathSyntaxError, None)),
            Some(d) => d,
        };

        let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens

        // walk N-1 tokens
        let value = match walk_header(self, path_to_dest) {
            Err(e) => return Err(e),
            Ok(v) => v
        };

        // There is already an value at this place
        if extract(value, destination).is_ok() {
            return Ok(false);
        }

        match *destination {
            Token::Key(ref s) => { // if the destination shall be an map key
                match *value {
                    /*
                     * Put it in there if we have a map
                     */
                    Value::Table(ref mut t) => {
                        t.insert(s.clone(), v);
                    }

                    /*
                     * Fail if there is no map here
                     */
                    _ => return Err(SE::new(SEK::HeaderPathTypeFailure, None)),
                }
            },

            Token::Index(i) => { // if the destination shall be an array
                match *value {

                    /*
                     * Put it in there if we have an array
                     */
                    Value::Array(ref mut a) => {
                        a.push(v); // push to the end of the array

                        // if the index is inside the array, we swap-remove the element at this
                        // index
                        if a.len() < i {
                            a.swap_remove(i);
                        }
                    },

                    /*
                     * Fail if there is no array here
                     */
                    _ => return Err(SE::new(SEK::HeaderPathTypeFailure, None)),
                }
            },
        }

        Ok(true)
    }

    /**
     * Set a header field by a string-spec
     *
     * ```ignore
     *  set("something.in.a.field", Boolean(true));
     * ```
     *
     * Sets a Boolean in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * If there is already a value at this place, this value will be overridden and the old value
     * will be returned
     */
    fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>> {
        let tokens = match tokenize(spec, sep) {
            Err(e) => return Err(e),
            Ok(t) => t,
        };
        debug!("tokens = {:?}", tokens);

        let destination = match tokens.iter().last() {
            None => return Err(SE::new(SEK::HeaderPathSyntaxError, None)),
            Some(d) => d
        };
        debug!("destination = {:?}", destination);

        let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
        // walk N-1 tokens
        let value = match walk_header(self, path_to_dest) {
            Err(e) => return Err(e),
            Ok(v) => v
        };
        debug!("walked value = {:?}", value);

        match *destination {
            Token::Key(ref s) => { // if the destination shall be an map key->value
                match *value {
                    /*
                     * Put it in there if we have a map
                     */
                    Value::Table(ref mut t) => {
                        debug!("Matched Key->Table");
                        return Ok(t.insert(s.clone(), v));
                    }

                    /*
                     * Fail if there is no map here
                     */
                    _ => {
                        debug!("Matched Key->NON-Table");
                        return Err(SE::new(SEK::HeaderPathTypeFailure, None));
                    }
                }
            },

            Token::Index(i) => { // if the destination shall be an array
                match *value {

                    /*
                     * Put it in there if we have an array
                     */
                    Value::Array(ref mut a) => {
                        debug!("Matched Index->Array");
                        a.push(v); // push to the end of the array

                        // if the index is inside the array, we swap-remove the element at this
                        // index
                        if a.len() > i {
                            debug!("Swap-Removing in Array {:?}[{:?}] <- {:?}", a, i, a[a.len()-1]);
                            return Ok(Some(a.swap_remove(i)));
                        }

                        debug!("Appended");
                        return Ok(None);
                    },

                    /*
                     * Fail if there is no array here
                     */
                    _ => {
                        debug!("Matched Index->NON-Array");
                        return Err(SE::new(SEK::HeaderPathTypeFailure, None));
                    },
                }
            },
        }

        Ok(None)
    }

    /**
     * Read a header field by a string-spec
     *
     * ```ignore
     *  let value = read("something.in.a.field");
     * ```
     *
     * Reads a Value in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * If there is no a value at this place, None will be returned. This also holds true for Arrays
     * which are accessed at an index which is not yet there, even if the accessed index is much
     * larger than the array length.
     */
    fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>> {
        let tokens = match tokenize(spec, splitchr) {
            Err(e) => return Err(e),
            Ok(t) => t,
        };

        let mut header_clone = self.clone(); // we clone as READing is simpler this way
        // walk N-1 tokens
        match walk_header(&mut header_clone, tokens) {
            Err(e) => match e.err_type() {
                // We cannot find the header key, as there is no path to it
                SEK::HeaderKeyNotFound => Ok(None),
                _ => Err(e),
            },
            Ok(v) => Ok(Some(v.clone())),
        }
    }

    fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>> {
        let tokens = match tokenize(spec, splitchr) {
            Err(e) => return Err(e),
            Ok(t) => t
        };

        let destination = match tokens.iter().last() {
            None => return Err(SE::new(SEK::HeaderPathSyntaxError, None)),
            Some(d) => d
        };
        debug!("destination = {:?}", destination);

        let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
        // walk N-1 tokens
        let mut value = match walk_header(self, path_to_dest) {
            Err(e) => return Err(e),
            Ok(v) => v
        };
        debug!("walked value = {:?}", value);

        match *destination {
            Token::Key(ref s) => { // if the destination shall be an map key->value
                match *value {
                    Value::Table(ref mut t) => {
                        debug!("Matched Key->Table, removing {:?}", s);
                        return Ok(t.remove(s));
                    },
                    _ => {
                        debug!("Matched Key->NON-Table");
                        return Err(SE::new(SEK::HeaderPathTypeFailure, None));
                    }
                }
            },

            Token::Index(i) => { // if the destination shall be an array
                match *value {
                    Value::Array(ref mut a) => {
                        // if the index is inside the array, we swap-remove the element at this
                        // index
                        if a.len() > i {
                            debug!("Removing in Array {:?}[{:?}]", a, i);
                            return Ok(Some(a.remove(i)));
                        } else {
                            return Ok(None);
                        }
                    },
                    _ => {
                        debug!("Matched Index->NON-Array");
                        return Err(SE::new(SEK::HeaderPathTypeFailure, None));
                    },
                }
            },
        }

        Ok(None)
    }

}

fn tokenize(spec: &str, splitchr: char) -> Result<Vec<Token>> {
    use std::str::FromStr;

    spec.split(splitchr)
        .map(|s| {
            usize::from_str(s)
                .map(Token::Index)
                .or_else(|_| Ok(Token::Key(String::from(s))))
        })
        .collect()
}

fn walk_header(v: &mut Value, tokens: Vec<Token>) -> Result<&mut Value> {
    use std::vec::IntoIter;

    fn walk_iter<'a>(v: Result<&'a mut Value>, i: &mut IntoIter<Token>) -> Result<&'a mut Value> {
        let next = i.next();
        v.and_then(move |value| {
            if let Some(token) = next {
                walk_iter(extract(value, &token), i)
            } else {
                Ok(value)
            }
        })
    }

    walk_iter(Ok(v), &mut tokens.into_iter())
}

fn extract_from_table<'a>(v: &'a mut Value, s: &str) -> Result<&'a mut Value> {
    match *v {
        Value::Table(ref mut t) => {
            t.get_mut(&s[..])
                .ok_or(SE::new(SEK::HeaderKeyNotFound, None))
        },
        _ => Err(SE::new(SEK::HeaderPathTypeFailure, None)),
    }
}

fn extract_from_array(v: &mut Value, i: usize) -> Result<&mut Value> {
    match *v {
        Value::Array(ref mut a) => {
            if a.len() < i {
                Err(SE::new(SEK::HeaderKeyNotFound, None))
            } else {
                Ok(&mut a[i])
            }
        },
        _ => Err(SE::new(SEK::HeaderPathTypeFailure, None)),
    }
}

fn extract<'a>(v: &'a mut Value, token: &Token) -> Result<&'a mut Value> {
    match *token {
        Token::Key(ref s)  => extract_from_table(v, s),
        Token::Index(i)    => extract_from_array(v, i),
    }
}

<commit_msg>Use helper fns to prettify error building<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use toml::Value;

use store::Result;
use error::{StoreError as SE, StoreErrorKind as SEK};
use libimagerror::into::IntoError;

pub trait TomlValueExt {
    fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool>;
    fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>>;
    fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>>;
    fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>>;

    #[inline]
    fn insert(&mut self, spec: &str, v: Value) -> Result<bool> {
        self.insert_with_sep(spec, '.', v)
    }

    #[inline]
    fn set(&mut self, spec: &str, v: Value) -> Result<Option<Value>> {
        self.set_with_sep(spec, '.', v)
    }

    #[inline]
    fn read(&self, spec: &str) -> Result<Option<Value>> {
        self.read_with_sep(spec, '.')
    }

    #[inline]
    fn delete(&mut self, spec: &str) -> Result<Option<Value>> {
        self.delete_with_sep(spec, '.')
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
enum Token {
    Key(String),
    Index(usize),
}

impl TomlValueExt for Value {
    /**
     * Insert a header field by a string-spec
     *
     * ```ignore
     *  insert("something.in.a.field", Boolean(true));
     * ```
     *
     * If an array field was accessed which is _out of bounds_ of the array available, the element
     * is appended to the array.
     *
     * Inserts a Boolean in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * Returns true if header field was set, false if there is already a value
     */
    fn insert_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<bool> {
        let tokens = match tokenize(spec, sep) {
            Err(e) => return Err(e),
            Ok(t) => t
        };

        let destination = match tokens.iter().last() {
            None => return Err(SE::new(SEK::HeaderPathSyntaxError, None)),
            Some(d) => d,
        };

        let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens

        // walk N-1 tokens
        let value = match walk_header(self, path_to_dest) {
            Err(e) => return Err(e),
            Ok(v) => v
        };

        // There is already an value at this place
        if extract(value, destination).is_ok() {
            return Ok(false);
        }

        match *destination {
            Token::Key(ref s) => { // if the destination shall be an map key
                match *value {
                    /*
                     * Put it in there if we have a map
                     */
                    Value::Table(ref mut t) => {
                        t.insert(s.clone(), v);
                    }

                    /*
                     * Fail if there is no map here
                     */
                    _ => return Err(SEK::HeaderPathTypeFailure.into_error()),
                }
            },

            Token::Index(i) => { // if the destination shall be an array
                match *value {

                    /*
                     * Put it in there if we have an array
                     */
                    Value::Array(ref mut a) => {
                        a.push(v); // push to the end of the array

                        // if the index is inside the array, we swap-remove the element at this
                        // index
                        if a.len() < i {
                            a.swap_remove(i);
                        }
                    },

                    /*
                     * Fail if there is no array here
                     */
                    _ => return Err(SEK::HeaderPathTypeFailure.into_error()),
                }
            },
        }

        Ok(true)
    }

    /**
     * Set a header field by a string-spec
     *
     * ```ignore
     *  set("something.in.a.field", Boolean(true));
     * ```
     *
     * Sets a Boolean in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * If there is already a value at this place, this value will be overridden and the old value
     * will be returned
     */
    fn set_with_sep(&mut self, spec: &str, sep: char, v: Value) -> Result<Option<Value>> {
        let tokens = match tokenize(spec, sep) {
            Err(e) => return Err(e),
            Ok(t) => t,
        };
        debug!("tokens = {:?}", tokens);

        let destination = match tokens.iter().last() {
            None => return Err(SEK::HeaderPathSyntaxError.into_error()),
            Some(d) => d
        };
        debug!("destination = {:?}", destination);

        let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
        // walk N-1 tokens
        let value = match walk_header(self, path_to_dest) {
            Err(e) => return Err(e),
            Ok(v) => v
        };
        debug!("walked value = {:?}", value);

        match *destination {
            Token::Key(ref s) => { // if the destination shall be an map key->value
                match *value {
                    /*
                     * Put it in there if we have a map
                     */
                    Value::Table(ref mut t) => {
                        debug!("Matched Key->Table");
                        return Ok(t.insert(s.clone(), v));
                    }

                    /*
                     * Fail if there is no map here
                     */
                    _ => {
                        debug!("Matched Key->NON-Table");
                        return Err(SEK::HeaderPathTypeFailure.into_error());
                    }
                }
            },

            Token::Index(i) => { // if the destination shall be an array
                match *value {

                    /*
                     * Put it in there if we have an array
                     */
                    Value::Array(ref mut a) => {
                        debug!("Matched Index->Array");
                        a.push(v); // push to the end of the array

                        // if the index is inside the array, we swap-remove the element at this
                        // index
                        if a.len() > i {
                            debug!("Swap-Removing in Array {:?}[{:?}] <- {:?}", a, i, a[a.len()-1]);
                            return Ok(Some(a.swap_remove(i)));
                        }

                        debug!("Appended");
                        return Ok(None);
                    },

                    /*
                     * Fail if there is no array here
                     */
                    _ => {
                        debug!("Matched Index->NON-Array");
                        return Err(SEK::HeaderPathTypeFailure.into_error());
                    },
                }
            },
        }

        Ok(None)
    }

    /**
     * Read a header field by a string-spec
     *
     * ```ignore
     *  let value = read("something.in.a.field");
     * ```
     *
     * Reads a Value in the section "something" -> "in" -> "a" -> "field"
     * A JSON equivalent would be
     *
     *  {
     *      something: {
     *          in: {
     *              a: {
     *                  field: true
     *              }
     *          }
     *      }
     *  }
     *
     * If there is no a value at this place, None will be returned. This also holds true for Arrays
     * which are accessed at an index which is not yet there, even if the accessed index is much
     * larger than the array length.
     */
    fn read_with_sep(&self, spec: &str, splitchr: char) -> Result<Option<Value>> {
        let tokens = match tokenize(spec, splitchr) {
            Err(e) => return Err(e),
            Ok(t) => t,
        };

        let mut header_clone = self.clone(); // we clone as READing is simpler this way
        // walk N-1 tokens
        match walk_header(&mut header_clone, tokens) {
            Err(e) => match e.err_type() {
                // We cannot find the header key, as there is no path to it
                SEK::HeaderKeyNotFound => Ok(None),
                _ => Err(e),
            },
            Ok(v) => Ok(Some(v.clone())),
        }
    }

    fn delete_with_sep(&mut self, spec: &str, splitchr: char) -> Result<Option<Value>> {
        let tokens = match tokenize(spec, splitchr) {
            Err(e) => return Err(e),
            Ok(t) => t
        };

        let destination = match tokens.iter().last() {
            None => return Err(SEK::HeaderPathSyntaxError.into_error()),
            Some(d) => d
        };
        debug!("destination = {:?}", destination);

        let path_to_dest = tokens[..(tokens.len() - 1)].into(); // N - 1 tokens
        // walk N-1 tokens
        let mut value = match walk_header(self, path_to_dest) {
            Err(e) => return Err(e),
            Ok(v) => v
        };
        debug!("walked value = {:?}", value);

        match *destination {
            Token::Key(ref s) => { // if the destination shall be an map key->value
                match *value {
                    Value::Table(ref mut t) => {
                        debug!("Matched Key->Table, removing {:?}", s);
                        return Ok(t.remove(s));
                    },
                    _ => {
                        debug!("Matched Key->NON-Table");
                        return Err(SEK::HeaderPathTypeFailure.into_error());
                    }
                }
            },

            Token::Index(i) => { // if the destination shall be an array
                match *value {
                    Value::Array(ref mut a) => {
                        // if the index is inside the array, we swap-remove the element at this
                        // index
                        if a.len() > i {
                            debug!("Removing in Array {:?}[{:?}]", a, i);
                            return Ok(Some(a.remove(i)));
                        } else {
                            return Ok(None);
                        }
                    },
                    _ => {
                        debug!("Matched Index->NON-Array");
                        return Err(SEK::HeaderPathTypeFailure.into_error());
                    },
                }
            },
        }

        Ok(None)
    }

}

fn tokenize(spec: &str, splitchr: char) -> Result<Vec<Token>> {
    use std::str::FromStr;

    spec.split(splitchr)
        .map(|s| {
            usize::from_str(s)
                .map(Token::Index)
                .or_else(|_| Ok(Token::Key(String::from(s))))
        })
        .collect()
}

fn walk_header(v: &mut Value, tokens: Vec<Token>) -> Result<&mut Value> {
    use std::vec::IntoIter;

    fn walk_iter<'a>(v: Result<&'a mut Value>, i: &mut IntoIter<Token>) -> Result<&'a mut Value> {
        let next = i.next();
        v.and_then(move |value| {
            if let Some(token) = next {
                walk_iter(extract(value, &token), i)
            } else {
                Ok(value)
            }
        })
    }

    walk_iter(Ok(v), &mut tokens.into_iter())
}

fn extract_from_table<'a>(v: &'a mut Value, s: &str) -> Result<&'a mut Value> {
    match *v {
        Value::Table(ref mut t) => {
            t.get_mut(&s[..])
                .ok_or(SEK::HeaderKeyNotFound.into_error())
        },
        _ => Err(SEK::HeaderPathTypeFailure.into_error()),
    }
}

fn extract_from_array(v: &mut Value, i: usize) -> Result<&mut Value> {
    match *v {
        Value::Array(ref mut a) => {
            if a.len() < i {
                Err(SEK::HeaderKeyNotFound.into_error())
            } else {
                Ok(&mut a[i])
            }
        },
        _ => Err(SEK::HeaderPathTypeFailure.into_error()),
    }
}

fn extract<'a>(v: &'a mut Value, token: &Token) -> Result<&'a mut Value> {
    match *token {
        Token::Key(ref s)  => extract_from_table(v, s),
        Token::Index(i)    => extract_from_array(v, i),
    }
}

<|endoftext|>


<commit_before>//! Test command for testing the preopt pass.
//!
//! The resulting function is sent to `filecheck`.

use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
use cranelift_codegen;
use cranelift_codegen::ir::Function;
use cranelift_codegen::print_errors::pretty_error;
use cranelift_reader::TestCommand;
use std::borrow::Cow;

struct TestSimplePreopt;

pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<SubTest>> {
    assert_eq!(parsed.command, "simple_preopt");
    if !parsed.options.is_empty() {
        Err(format!("No options allowed on {}", parsed))
    } else {
        Ok(Box::new(TestSimplePreopt))
    }
}

impl SubTest for TestSimplePreopt {
    fn name(&self) -> &'static str {
        "simple_preopt"
    }

    fn is_mutating(&self) -> bool {
        true
    }

    fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
        let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
        let isa = context.isa.expect("preopt needs an ISA");

        comp_ctx.flowgraph();
        comp_ctx
            .preopt(isa)
            .map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;

        let text = &comp_ctx.func.display(isa).to_string();
        run_filecheck(&text, context)
    }
}
<commit_msg>Questionable change: Remove domtree generation from simple_preopt testing.<commit_after>//! Test command for testing the preopt pass.
//!
//! The resulting function is sent to `filecheck`.

use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
use cranelift_codegen;
use cranelift_codegen::ir::Function;
use cranelift_codegen::print_errors::pretty_error;
use cranelift_reader::TestCommand;
use std::borrow::Cow;

struct TestSimplePreopt;

pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<SubTest>> {
    assert_eq!(parsed.command, "simple_preopt");
    if !parsed.options.is_empty() {
        Err(format!("No options allowed on {}", parsed))
    } else {
        Ok(Box::new(TestSimplePreopt))
    }
}

impl SubTest for TestSimplePreopt {
    fn name(&self) -> &'static str {
        "simple_preopt"
    }

    fn is_mutating(&self) -> bool {
        true
    }

    fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
        let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
        let isa = context.isa.expect("preopt needs an ISA");

        comp_ctx.compute_cfg();
        comp_ctx
            .preopt(isa)
            .map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
        let text = &comp_ctx.func.display(isa).to_string();
        run_filecheck(&text, context)
    }
}
<|endoftext|>


<commit_before>use syntax::{
    ast::{self, AstNode},
    match_ast, SyntaxElement, TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     $0dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
    let new_contents = adjusted_macro_contents(&macro_call)?;

    let macro_text_range = macro_call.syntax().text_range();
    let macro_end = if macro_call.semicolon_token().is_some() {
        macro_text_range.end() - TextSize::of(';')
    } else {
        macro_text_range.end()
    };

    acc.add(
        AssistId("remove_dbg", AssistKind::Refactor),
        "Remove dbg!()",
        macro_text_range,
        |builder| {
            builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents);
        },
    )
}

fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
    let contents = get_valid_macrocall_contents(&macro_call, "dbg")?;
    let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
    let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
        TextSize::of('('),
        macro_text_with_brackets.len() - TextSize::of(')'),
    ));

    Some(
        if !is_leaf_or_control_flow_expr(macro_call)
            && needs_parentheses_around_macro_contents(contents)
        {
            format!("({})", macro_text_in_brackets)
        } else {
            macro_text_in_brackets.to_string()
        },
    )
}

fn is_leaf_or_control_flow_expr(macro_call: &ast::MacroCall) -> bool {
    macro_call.syntax().next_sibling().is_none()
        || match macro_call.syntax().parent() {
            Some(parent) => match_ast! {
                match parent {
                    ast::Condition(_it) => true,
                    ast::MatchExpr(_it) => true,
                    _ => false,
                }
            },
            None => false,
        }
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens, then returns the contents between the ending tokens
fn get_valid_macrocall_contents(
    macro_call: &ast::MacroCall,
    macro_name: &str,
) -> Option<Vec<SyntaxElement>> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;
    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let mut children_with_tokens = macro_call.token_tree()?.syntax().children_with_tokens();
    let first_child = children_with_tokens.next()?;
    let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>();
    let last_child = contents_between_brackets.pop()?;

    if contents_between_brackets.is_empty() {
        None
    } else {
        match (first_child.kind(), last_child.kind()) {
            (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => {
                Some(contents_between_brackets)
            }
            _ => None,
        }
    }
}

fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -> bool {
    if macro_contents.len() < 2 {
        return false;
    }
    let mut macro_contents = macro_contents.into_iter().peekable();
    let mut unpaired_brackets_in_contents = Vec::new();
    while let Some(element) = macro_contents.next() {
        match element.kind() {
            T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element),
            T![')'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['('])
                {
                    return true;
                }
            }
            T![']'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['['])
                {
                    return true;
                }
            }
            T!['}'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['{'])
                {
                    return true;
                }
            }
            symbol_kind => {
                let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
                if symbol_not_in_bracket
                    && symbol_kind != T![:] // paths
                    && (symbol_kind != T![.] // field/method access
                        || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations
                            .peek()
                            .map(|element| element.kind() == T![.])
                            .unwrap_or(false))
                    && symbol_kind != T![?] // try operator
                    && (symbol_kind.is_punct() || symbol_kind == T![as])
                {
                    return true;
                }
            }
        }
    }
    !unpaired_brackets_in_contents.is_empty()
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "$0dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!$0((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 $0+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = $0dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.$0checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );

        check_assist(remove_dbg, "$0dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![$01 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{$01 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "$0vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "$0dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "$0dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.$0checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
    }

    #[test]
    fn remove_dbg_from_non_leaf_simple_expression() {
        check_assist(
            remove_dbg,
            "
fn main() {
    let mut a = 1;
    while dbg!$0(a) < 10000 {
        a += 1;
    }
}
",
            "
fn main() {
    let mut a = 1;
    while a < 10000 {
        a += 1;
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(a + b).foo();"#,
            r#"let res = (a + b).foo();"#,
        );

        check_assist(remove_dbg, r#"let res = $0dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#);
        check_assist(remove_dbg, r#"let res = $0dbg![2 + 2] * 5"#, r#"let res = (2 + 2) * 5"#);
    }

    #[test]
    fn test_remove_dbg_method_chaining() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(foo().bar()).baz();"#,
            r#"let res = foo().bar().baz();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(foo.bar()).baz();"#,
            r#"let res = foo.bar().baz();"#,
        );
    }

    #[test]
    fn test_remove_dbg_field_chaining() {
        check_assist(remove_dbg, r#"let res = $0dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        check_assist_target(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg$0!(5 + 10));
    println!("{}", x);
}"#,
            "dbg!(5 + 10)",
        );

        check_assist(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg$0!(5 + 10));
    println!("{}", x);
}"#,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!("{}", x);
}"#,
        );
    }

    #[test]
    fn test_remove_dbg_try_expr() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(result?).foo();"#,
            r#"let res = result?.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_await_expr() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(fut.await).foo();"#,
            r#"let res = fut.await.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_as_cast() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(3 as usize).foo();"#,
            r#"let res = (3 as usize).foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_index_expr() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(array[3]).foo();"#,
            r#"let res = array[3].foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(tuple.3).foo();"#,
            r#"let res = tuple.3.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_range_expr() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(foo..bar).foo();"#,
            r#"let res = (foo..bar).foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(foo..=bar).foo();"#,
            r#"let res = (foo..=bar).foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_followed_by_block() {
        check_assist(
            remove_dbg,
            r#"fn foo() {
    if $0dbg!(x || y) {}
}"#,
            r#"fn foo() {
    if x || y {}
}"#,
        );
        check_assist(
            remove_dbg,
            r#"fn foo() {
    while let foo = $0dbg!(&x) {}
}"#,
            r#"fn foo() {
    while let foo = &x {}
}"#,
        );
        check_assist(
            remove_dbg,
            r#"fn foo() {
    if let foo = $0dbg!(&x) {}
}"#,
            r#"fn foo() {
    if let foo = &x {}
}"#,
        );
        check_assist(
            remove_dbg,
            r#"fn foo() {
    match $0dbg!(&x) {}
}"#,
            r#"fn foo() {
    match &x {}
}"#,
        );
    }
}
<commit_msg>Show dbg remove assist on empty contents<commit_after>use syntax::{
    ast::{self, AstNode},
    match_ast, SyntaxElement, TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     $0dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
    let new_contents = adjusted_macro_contents(&macro_call)?;

    let macro_text_range = macro_call.syntax().text_range();
    let macro_end = if macro_call.semicolon_token().is_some() {
        macro_text_range.end() - TextSize::of(';')
    } else {
        macro_text_range.end()
    };

    acc.add(
        AssistId("remove_dbg", AssistKind::Refactor),
        "Remove dbg!()",
        macro_text_range,
        |builder| {
            builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents);
        },
    )
}

fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
    let contents = get_valid_macrocall_contents(&macro_call, "dbg")?;
    let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
    let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
        TextSize::of('('),
        macro_text_with_brackets.len() - TextSize::of(')'),
    ));

    Some(
        if !is_leaf_or_control_flow_expr(macro_call)
            && needs_parentheses_around_macro_contents(contents)
        {
            format!("({})", macro_text_in_brackets)
        } else {
            macro_text_in_brackets.to_string()
        },
    )
}

fn is_leaf_or_control_flow_expr(macro_call: &ast::MacroCall) -> bool {
    macro_call.syntax().next_sibling().is_none()
        || match macro_call.syntax().parent() {
            Some(parent) => match_ast! {
                match parent {
                    ast::Condition(_it) => true,
                    ast::MatchExpr(_it) => true,
                    _ => false,
                }
            },
            None => false,
        }
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens, then returns the contents between the ending tokens
fn get_valid_macrocall_contents(
    macro_call: &ast::MacroCall,
    macro_name: &str,
) -> Option<Vec<SyntaxElement>> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;
    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let mut children_with_tokens = macro_call.token_tree()?.syntax().children_with_tokens();
    let first_child = children_with_tokens.next()?;
    let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>();
    let last_child = contents_between_brackets.pop()?;

    match (first_child.kind(), last_child.kind()) {
        (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => {
            Some(contents_between_brackets)
        }
        _ => None,
    }
}

fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -> bool {
    if macro_contents.len() < 2 {
        return false;
    }
    let mut macro_contents = macro_contents.into_iter().peekable();
    let mut unpaired_brackets_in_contents = Vec::new();
    while let Some(element) = macro_contents.next() {
        match element.kind() {
            T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element),
            T![')'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['('])
                {
                    return true;
                }
            }
            T![']'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['['])
                {
                    return true;
                }
            }
            T!['}'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['{'])
                {
                    return true;
                }
            }
            symbol_kind => {
                let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
                if symbol_not_in_bracket
                    && symbol_kind != T![:] // paths
                    && (symbol_kind != T![.] // field/method access
                        || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations
                            .peek()
                            .map(|element| element.kind() == T![.])
                            .unwrap_or(false))
                    && symbol_kind != T![?] // try operator
                    && (symbol_kind.is_punct() || symbol_kind == T![as])
                {
                    return true;
                }
            }
        }
    }
    !unpaired_brackets_in_contents.is_empty()
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "$0dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!$0((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 $0+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = $0dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.$0checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );

        check_assist(remove_dbg, "$0dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![$01 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{$01 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "$0vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "$0dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "$0dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.$0checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
    }

    #[test]
    fn remove_dbg_from_non_leaf_simple_expression() {
        check_assist(
            remove_dbg,
            "
fn main() {
    let mut a = 1;
    while dbg!$0(a) < 10000 {
        a += 1;
    }
}
",
            "
fn main() {
    let mut a = 1;
    while a < 10000 {
        a += 1;
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(a + b).foo();"#,
            r#"let res = (a + b).foo();"#,
        );

        check_assist(remove_dbg, r#"let res = $0dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#);
        check_assist(remove_dbg, r#"let res = $0dbg![2 + 2] * 5"#, r#"let res = (2 + 2) * 5"#);
    }

    #[test]
    fn test_remove_dbg_method_chaining() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(foo().bar()).baz();"#,
            r#"let res = foo().bar().baz();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(foo.bar()).baz();"#,
            r#"let res = foo.bar().baz();"#,
        );
    }

    #[test]
    fn test_remove_dbg_field_chaining() {
        check_assist(remove_dbg, r#"let res = $0dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        check_assist_target(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg$0!(5 + 10));
    println!("{}", x);
}"#,
            "dbg!(5 + 10)",
        );

        check_assist(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg$0!(5 + 10));
    println!("{}", x);
}"#,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!("{}", x);
}"#,
        );
    }

    #[test]
    fn test_remove_dbg_try_expr() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(result?).foo();"#,
            r#"let res = result?.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_await_expr() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(fut.await).foo();"#,
            r#"let res = fut.await.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_as_cast() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(3 as usize).foo();"#,
            r#"let res = (3 as usize).foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_index_expr() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(array[3]).foo();"#,
            r#"let res = array[3].foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(tuple.3).foo();"#,
            r#"let res = tuple.3.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_range_expr() {
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(foo..bar).foo();"#,
            r#"let res = (foo..bar).foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = $0dbg!(foo..=bar).foo();"#,
            r#"let res = (foo..=bar).foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_followed_by_block() {
        check_assist(
            remove_dbg,
            r#"fn foo() {
    if $0dbg!(x || y) {}
}"#,
            r#"fn foo() {
    if x || y {}
}"#,
        );
        check_assist(
            remove_dbg,
            r#"fn foo() {
    while let foo = $0dbg!(&x) {}
}"#,
            r#"fn foo() {
    while let foo = &x {}
}"#,
        );
        check_assist(
            remove_dbg,
            r#"fn foo() {
    if let foo = $0dbg!(&x) {}
}"#,
            r#"fn foo() {
    if let foo = &x {}
}"#,
        );
        check_assist(
            remove_dbg,
            r#"fn foo() {
    match $0dbg!(&x) {}
}"#,
            r#"fn foo() {
    match &x {}
}"#,
        );
    }

    #[test]
    fn test_remove_empty_dbg() {
        check_assist(remove_dbg, r#"$0dbg!()"#, r#""#);
    }
}
<|endoftext|>


<commit_before>//! Implementation of the `#[simd_test]` macro
//!
//! This macro expands to a `#[test]` function which tests the local machine
//! for the appropriate cfg before calling the inner test function.

extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;

use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::env;

fn string(s: &str) -> TokenTree {
    Literal::string(s).into()
}

#[proc_macro_attribute]
pub fn simd_test(
    attr: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let tokens = TokenStream::from(attr).into_iter().collect::<Vec<_>>();
    if tokens.len() != 3 {
        panic!("expected #[simd_test(enable = \"feature\")]");
    }
    match &tokens[0] {
        TokenTree::Ident(tt) if *tt == "enable" => {}
        _ => panic!("expected #[simd_test(enable = \"feature\")]"),
    }
    match &tokens[1] {
        TokenTree::Punct(tt) if tt.as_char() == '=' => {}
        _ => panic!("expected #[simd_test(enable = \"feature\")]"),
    }
    let enable_feature = match &tokens[2] {
        TokenTree::Literal(tt) => tt.to_string(),
        _ => panic!("expected #[simd_test(enable = \"feature\")]"),
    };
    let enable_feature = enable_feature
        .trim_left_matches('"')
        .trim_right_matches('"');
    let target_features: Vec<String> = enable_feature
        .replace('+', "")
        .split(',')
        .map(String::from)
        .collect();

    let enable_feature = string(enable_feature);
    let item = TokenStream::from(item);
    let name = find_name(item.clone());

    let name: TokenStream = name
        .to_string()
        .parse()
        .unwrap_or_else(|_| panic!("failed to parse name: {}", name.to_string()));

    let target = env::var("TARGET").expect("TARGET environment variable should be set for rustc");
    let mut force_test = false;
    let macro_test = match target
        .split('-')
        .next()
        .unwrap_or_else(|| panic!("target triple contained no \"-\": {}", target))
    {
        "i686" | "x86_64" | "i586" => "is_x86_feature_detected",
        "arm" | "armv7" => "is_arm_feature_detected",
        "aarch64" => "is_aarch64_feature_detected",
        "powerpc" | "powerpcle" => "is_powerpc_feature_detected",
        "powerpc64" | "powerpc64le" => "is_powerpc64_feature_detected",
        "mips" | "mipsel" => {
            // FIXME:
            // On MIPS CI run-time feature detection always returns false due
            // to this qemu bug: https://bugs.launchpad.net/qemu/+bug/1754372
            //
            // This is a workaround to force the MIPS tests to always run on
            // CI.
            force_test = true;
            "is_mips_feature_detected"
        }
        "mips64" | "mips64el" => {
            // FIXME: see above
            force_test = true;
            "is_mips64_feature_detected"
        }
        t => panic!("unknown target: {}", t),
    };
    let macro_test = Ident::new(macro_test, Span::call_site());

    let mut cfg_target_features = TokenStream::new();
    for feature in target_features {
        let q = quote_spanned! {
            proc_macro2::Span::call_site() =>
            #macro_test!(#feature) &&
        };
        q.to_tokens(&mut cfg_target_features);
    }
    let q = quote! { true };
    q.to_tokens(&mut cfg_target_features);

    let test_norun = std::env::var("STDSIMD_TEST_NORUN").is_ok();
    let maybe_ignore = if test_norun {
        quote! { #[ignore] }
    } else {
        TokenStream::new()
    };

    let ret: TokenStream = quote_spanned! {
        proc_macro2::Span::call_site() =>
        #[allow(non_snake_case)]
        #[test]
        #maybe_ignore
        fn #name() {
            if #force_test | (#cfg_target_features) {
                return unsafe { #name() };
            } else {
                ::stdsimd_test::assert_skip_test_ok(stringify!(#name));
            }

            #[target_feature(enable = #enable_feature)]
            #item
        }
    };
    ret.into()
}

fn find_name(item: TokenStream) -> Ident {
    let mut tokens = item.into_iter();
    while let Some(tok) = tokens.next() {
        if let TokenTree::Ident(word) = tok {
            if word == "fn" {
                break;
            }
        }
    }

    match tokens.next() {
        Some(TokenTree::Ident(word)) => word,
        _ => panic!("failed to find function name"),
    }
}
<commit_msg>Remove usage of deprecated functions<commit_after>//! Implementation of the `#[simd_test]` macro
//!
//! This macro expands to a `#[test]` function which tests the local machine
//! for the appropriate cfg before calling the inner test function.

extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;

use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::env;

fn string(s: &str) -> TokenTree {
    Literal::string(s).into()
}

#[proc_macro_attribute]
pub fn simd_test(
    attr: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let tokens = TokenStream::from(attr).into_iter().collect::<Vec<_>>();
    if tokens.len() != 3 {
        panic!("expected #[simd_test(enable = \"feature\")]");
    }
    match &tokens[0] {
        TokenTree::Ident(tt) if *tt == "enable" => {}
        _ => panic!("expected #[simd_test(enable = \"feature\")]"),
    }
    match &tokens[1] {
        TokenTree::Punct(tt) if tt.as_char() == '=' => {}
        _ => panic!("expected #[simd_test(enable = \"feature\")]"),
    }
    let enable_feature = match &tokens[2] {
        TokenTree::Literal(tt) => tt.to_string(),
        _ => panic!("expected #[simd_test(enable = \"feature\")]"),
    };
    let enable_feature = enable_feature
        .trim_start_matches('"')
        .trim_end_matches('"');
    let target_features: Vec<String> = enable_feature
        .replace('+', "")
        .split(',')
        .map(String::from)
        .collect();

    let enable_feature = string(enable_feature);
    let item = TokenStream::from(item);
    let name = find_name(item.clone());

    let name: TokenStream = name
        .to_string()
        .parse()
        .unwrap_or_else(|_| panic!("failed to parse name: {}", name.to_string()));

    let target = env::var("TARGET").expect("TARGET environment variable should be set for rustc");
    let mut force_test = false;
    let macro_test = match target
        .split('-')
        .next()
        .unwrap_or_else(|| panic!("target triple contained no \"-\": {}", target))
    {
        "i686" | "x86_64" | "i586" => "is_x86_feature_detected",
        "arm" | "armv7" => "is_arm_feature_detected",
        "aarch64" => "is_aarch64_feature_detected",
        "powerpc" | "powerpcle" => "is_powerpc_feature_detected",
        "powerpc64" | "powerpc64le" => "is_powerpc64_feature_detected",
        "mips" | "mipsel" => {
            // FIXME:
            // On MIPS CI run-time feature detection always returns false due
            // to this qemu bug: https://bugs.launchpad.net/qemu/+bug/1754372
            //
            // This is a workaround to force the MIPS tests to always run on
            // CI.
            force_test = true;
            "is_mips_feature_detected"
        }
        "mips64" | "mips64el" => {
            // FIXME: see above
            force_test = true;
            "is_mips64_feature_detected"
        }
        t => panic!("unknown target: {}", t),
    };
    let macro_test = Ident::new(macro_test, Span::call_site());

    let mut cfg_target_features = TokenStream::new();
    for feature in target_features {
        let q = quote_spanned! {
            proc_macro2::Span::call_site() =>
            #macro_test!(#feature) &&
        };
        q.to_tokens(&mut cfg_target_features);
    }
    let q = quote! { true };
    q.to_tokens(&mut cfg_target_features);

    let test_norun = std::env::var("STDSIMD_TEST_NORUN").is_ok();
    let maybe_ignore = if test_norun {
        quote! { #[ignore] }
    } else {
        TokenStream::new()
    };

    let ret: TokenStream = quote_spanned! {
        proc_macro2::Span::call_site() =>
        #[allow(non_snake_case)]
        #[test]
        #maybe_ignore
        fn #name() {
            if #force_test | (#cfg_target_features) {
                return unsafe { #name() };
            } else {
                ::stdsimd_test::assert_skip_test_ok(stringify!(#name));
            }

            #[target_feature(enable = #enable_feature)]
            #item
        }
    };
    ret.into()
}

fn find_name(item: TokenStream) -> Ident {
    let mut tokens = item.into_iter();
    while let Some(tok) = tokens.next() {
        if let TokenTree::Ident(word) = tok {
            if word == "fn" {
                break;
            }
        }
    }

    match tokens.next() {
        Some(TokenTree::Ident(word)) => word,
        _ => panic!("failed to find function name"),
    }
}
<|endoftext|>


<commit_before>use super::allocator::Allocator;

use alloc::allocator::{Alloc, Layout};

// Bunch of different heaps as the tests run concurrently
const HEAP_SIZE: usize = 1000;

// Alloc tests

#[test]
fn alloc_0() {
    let mut heap = [0; HEAP_SIZE];
    let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

    unsafe {
        let layout = Layout::from_size_align_unchecked(26, 0);
        let result = allocator.alloc(layout);
        assert!(result.is_ok());
    }
}

#[test]
fn alloc_1() {
    let mut heap = [0; HEAP_SIZE];
    let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

    unsafe {
        // Allocate the entire heap
        for _ in 0..10 {
            let size = 100 - ::core::mem::size_of::<::block::Block>();
            let layout = Layout::from_size_align_unchecked(size, 0);
            let result = allocator.alloc(layout);
            assert!(result.is_ok());
        }
    }
}

#[test]
fn returns_err_on_oom_01() {
    let mut heap = [0; HEAP_SIZE];
    let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

    unsafe {
        let layout = Layout::from_size_align_unchecked(HEAP_SIZE + 1, 0);
        let result = allocator.alloc(layout);
        assert!(result.is_err());
    }
}

#[test]
fn returns_err_on_oom_02() {
    let mut heap = [0; HEAP_SIZE];
    let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

    unsafe {
        // Allocate the entire heap
        for _ in 0..10 {
            let size = 100 - ::core::mem::size_of::<::block::Block>();
            let layout = Layout::from_size_align_unchecked(size, 0);
            let result = allocator.alloc(layout);
            assert!(result.is_ok());
        }

        // And OOM it...
        let layout = Layout::from_size_align_unchecked(1, 0);
        let result = allocator.alloc(layout);
        assert!(result.is_err());
    }
}

// Dealloc tests

#[test]
fn dealloc_0() {
    let mut heap = [0; HEAP_SIZE];
    let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

    let size = 100 - ::core::mem::size_of::<::block::Block>();

    unsafe {
        // Allocate some memory
        let layout_a = Layout::from_size_align_unchecked(size, 0);
        let p1 = allocator.alloc(layout_a);

        // Deallocate it...
        let layout_d = Layout::from_size_align_unchecked(size, 0);
        allocator.dealloc(p1.unwrap(), layout_d);
    }
}

#[test]
fn dealloc_1() {
    let mut heap = [0; HEAP_SIZE];
    let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

    let size = 500 - ::core::mem::size_of::<::block::Block>();

    unsafe {
        // Allocate the entire heap in two allocs
        let layout_a1 = Layout::from_size_align_unchecked(size, 0);
        let p1 = allocator.alloc(layout_a1);
        assert!(p1.is_ok());

        let layout_a2 = Layout::from_size_align_unchecked(size, 0);
        let p2 = allocator.alloc(layout_a2);
        assert!(p2.is_ok());

        // Deallocate the first
        let layout_d = Layout::from_size_align_unchecked(size, 0);
        allocator.dealloc(p1.unwrap(), layout_d);

        // Try to reallocate the same memory freed from p1
        let layout_a3 = Layout::from_size_align_unchecked(size, 0);
        let p3 = allocator.alloc(layout_a3);
        assert!(p3.is_ok());
    }
}

// #[test]
// fn usable_size_0() {
//     // This will change in the future but for now just test that it returns the size
//     // we asked for
//     let mut heap = [0; HEAP_SIZE];
//     let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);
//
//     let size = 100;
//     let align = 0;
//
//     let _ = allocator.alloc(size, 0);
//     assert!(allocator.usable_size(size, align) == size);
// }
//
// #[test]
// fn realloc_0() {
//     // Should be able to allocate one thing, then reallocate it an unlimited number of times
//     let mut heap = [0; HEAP_SIZE];
//     let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);
//
//     let size = 100;
//     let mut ptr = allocator.alloc(size, 0).unwrap();
//
//     for _ in 1..500 {
//         let new_ptr = allocator.realloc(ptr, size, size, 0).unwrap();
//         ptr = new_ptr;
//     }
// }
//
// #[test]
// #[should_panic(expected = "Out Of Memory")]
// fn realloc_1() {
//     // This test should currently panic because the memory will become fragmented into lots of
//     // small blocks. When mereging is implemented this should be fixed.
//     let mut heap = [0; HEAP_SIZE];
//     let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);
//
//     let size = 100;
//     let mut ptr = allocator.alloc(size, 0).unwrap();
//
//     for i in 1..500 {
//         let new_ptr = allocator.realloc(ptr, size + i - 1, size + i, 0).unwrap();
//         ptr = new_ptr;
//     }
// }
//
// #[test]
// fn realloc_inplace_0() {
//     // For now this do nothing...
//     let mut heap = [0; HEAP_SIZE];
//     let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);
//
//     let size = 10;
//     let ptr = allocator.alloc(size, 0).unwrap();
//     assert!(allocator.realloc_inplace(ptr, size, 100, 0) == size);
// }
<commit_msg>Fix new allocator tests<commit_after>use super::allocator::Allocator;

use alloc::allocator::{Alloc, Layout};

// Bunch of different heaps as the tests run concurrently
const HEAP_SIZE: usize = 1000;

// Alloc tests

#[test]
fn alloc_0() {
    let mut heap = [0; HEAP_SIZE];

    unsafe {
        let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

        let layout = Layout::from_size_align_unchecked(26, 0);
        let result = allocator.alloc(layout);
        assert!(result.is_ok());
    }
}

#[test]
fn alloc_1() {
    let mut heap = [0; HEAP_SIZE];

    unsafe {
        let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

        // Allocate the entire heap
        for _ in 0..10 {
            let size = 100 - ::core::mem::size_of::<::block::Block>();
            let layout = Layout::from_size_align_unchecked(size, 0);
            let result = allocator.alloc(layout);
            assert!(result.is_ok());
        }
    }
}

#[test]
fn returns_err_on_oom_01() {
    let mut heap = [0; HEAP_SIZE];

    unsafe {
        let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

        let layout = Layout::from_size_align_unchecked(HEAP_SIZE + 1, 0);
        let result = allocator.alloc(layout);
        assert!(result.is_err());
    }
}

#[test]
fn returns_err_on_oom_02() {
    let mut heap = [0; HEAP_SIZE];

    unsafe {
        let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

        // Allocate the entire heap
        for _ in 0..10 {
            let size = 100 - ::core::mem::size_of::<::block::Block>();
            let layout = Layout::from_size_align_unchecked(size, 0);
            let result = allocator.alloc(layout);
            assert!(result.is_ok());
        }

        // And OOM it...
        let layout = Layout::from_size_align_unchecked(1, 0);
        let result = allocator.alloc(layout);
        assert!(result.is_err());
    }
}

// Dealloc tests

#[test]
fn dealloc_0() {
    let mut heap = [0; HEAP_SIZE];
    let size = 100 - ::core::mem::size_of::<::block::Block>();

    unsafe {
        let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

        // Allocate some memory
        let layout_a = Layout::from_size_align_unchecked(size, 0);
        let p1 = allocator.alloc(layout_a);

        // Deallocate it...
        let layout_d = Layout::from_size_align_unchecked(size, 0);
        allocator.dealloc(p1.unwrap(), layout_d);
    }
}

#[test]
fn dealloc_1() {
    let mut heap = [0; HEAP_SIZE];
    let size = 500 - ::core::mem::size_of::<::block::Block>();

    unsafe {
        let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);

        // Allocate the entire heap in two allocs
        let layout_a1 = Layout::from_size_align_unchecked(size, 0);
        let p1 = allocator.alloc(layout_a1);
        assert!(p1.is_ok());

        let layout_a2 = Layout::from_size_align_unchecked(size, 0);
        let p2 = allocator.alloc(layout_a2);
        assert!(p2.is_ok());

        // Deallocate the first
        let layout_d = Layout::from_size_align_unchecked(size, 0);
        allocator.dealloc(p1.unwrap(), layout_d);

        // Try to reallocate the same memory freed from p1
        let layout_a3 = Layout::from_size_align_unchecked(size, 0);
        let p3 = allocator.alloc(layout_a3);
        assert!(p3.is_ok());
    }
}

// #[test]
// fn usable_size_0() {
//     // This will change in the future but for now just test that it returns the size
//     // we asked for
//     let mut heap = [0; HEAP_SIZE];
//     let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);
//
//     let size = 100;
//     let align = 0;
//
//     let _ = allocator.alloc(size, 0);
//     assert!(allocator.usable_size(size, align) == size);
// }
//
// #[test]
// fn realloc_0() {
//     // Should be able to allocate one thing, then reallocate it an unlimited number of times
//     let mut heap = [0; HEAP_SIZE];
//     let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);
//
//     let size = 100;
//     let mut ptr = allocator.alloc(size, 0).unwrap();
//
//     for _ in 1..500 {
//         let new_ptr = allocator.realloc(ptr, size, size, 0).unwrap();
//         ptr = new_ptr;
//     }
// }
//
// #[test]
// #[should_panic(expected = "Out Of Memory")]
// fn realloc_1() {
//     // This test should currently panic because the memory will become fragmented into lots of
//     // small blocks. When mereging is implemented this should be fixed.
//     let mut heap = [0; HEAP_SIZE];
//     let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);
//
//     let size = 100;
//     let mut ptr = allocator.alloc(size, 0).unwrap();
//
//     for i in 1..500 {
//         let new_ptr = allocator.realloc(ptr, size + i - 1, size + i, 0).unwrap();
//         ptr = new_ptr;
//     }
// }
//
// #[test]
// fn realloc_inplace_0() {
//     // For now this do nothing...
//     let mut heap = [0; HEAP_SIZE];
//     let mut allocator = Allocator::new(&mut heap, HEAP_SIZE);
//
//     let size = 10;
//     let ptr = allocator.alloc(size, 0).unwrap();
//     assert!(allocator.realloc_inplace(ptr, size, 100, 0) == size);
// }
<|endoftext|>


<commit_before>use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::Command;

use flate2::read::GzDecoder;
use tar::Archive;

fn main() {
	// TODO: Replace with is_some_with once stabilized
	// https://github.com/rust-lang/rust/issues/93050
	let targets_hermit =
		matches!(env::var_os("CARGO_CFG_TARGET_OS"), Some(os) if os == OsStr::new("hermit"));
	let runs_clippy =
		matches!(env::var_os("CARGO_CFG_FEATURE"), Some(os) if os == OsStr::new("cargo-clippy"));
	if !targets_hermit || runs_clippy {
		return;
	}

	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let version = "0.3.54";
		let out_dir = out_dir();
		let src_dir = out_dir.join(format!("libhermit-rs-{version}"));

		if !src_dir.exists() {
			let url = format!(
				"https://github.com/hermitcore/libhermit-rs/archive/refs/tags/v{version}.tar.gz"
			);
			let response = ureq::get(url.as_str()).call().unwrap().into_reader();
			let tar = GzDecoder::new(response);
			let mut archive = Archive::new(tar);
			archive.unpack(src_dir.parent().unwrap()).unwrap();
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = target_dir();
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");

		let mut cmd = Command::new("cargo");

		// Remove rust-toolchain-specific environment variables from kernel cargo
		cmd.env_remove("LD_LIBRARY_PATH");
		env::vars()
			.filter(|(key, _value)| key.starts_with("CARGO") || key.starts_with("RUST"))
			.for_each(|(key, _value)| {
				cmd.env_remove(&key);
			});

		cmd.current_dir(&self.src_dir)
			.arg("xtask")
			.arg("build")
			.arg("--arch")
			.arg(&arch)
			.args(&[
				"--profile",
				match profile.as_str() {
					"debug" => "dev",
					profile => profile,
				},
			])
			.arg("--target-dir")
			.arg(&target_dir);

		if has_feature("instrument") {
			cmd.arg("--instrument-mcount");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(&arch)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn out_dir() -> PathBuf {
	env::var_os("OUT_DIR").unwrap().into()
}

fn target_dir() -> PathBuf {
	let mut target_dir = out_dir();
	target_dir.push("target");
	target_dir
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.args(&["--features", &features.join(" ")]);
	}
}
<commit_msg>Sys: Build xtask in target dir<commit_after>use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::Command;

use flate2::read::GzDecoder;
use tar::Archive;

fn main() {
	// TODO: Replace with is_some_with once stabilized
	// https://github.com/rust-lang/rust/issues/93050
	let targets_hermit =
		matches!(env::var_os("CARGO_CFG_TARGET_OS"), Some(os) if os == OsStr::new("hermit"));
	let runs_clippy =
		matches!(env::var_os("CARGO_CFG_FEATURE"), Some(os) if os == OsStr::new("cargo-clippy"));
	if !targets_hermit || runs_clippy {
		return;
	}

	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let version = "0.3.54";
		let out_dir = out_dir();
		let src_dir = out_dir.join(format!("libhermit-rs-{version}"));

		if !src_dir.exists() {
			let url = format!(
				"https://github.com/hermitcore/libhermit-rs/archive/refs/tags/v{version}.tar.gz"
			);
			let response = ureq::get(url.as_str()).call().unwrap().into_reader();
			let tar = GzDecoder::new(response);
			let mut archive = Archive::new(tar);
			archive.unpack(src_dir.parent().unwrap()).unwrap();
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = target_dir();
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");

		let mut cmd = Command::new("cargo");

		// Remove rust-toolchain-specific environment variables from kernel cargo
		cmd.env_remove("LD_LIBRARY_PATH");
		env::vars()
			.filter(|(key, _value)| key.starts_with("CARGO") || key.starts_with("RUST"))
			.for_each(|(key, _value)| {
				cmd.env_remove(&key);
			});

		cmd.current_dir(&self.src_dir)
			.arg("run")
			.arg("--package=xtask")
			.arg("--target-dir")
			.arg(&target_dir)
			.arg("--")
			.arg("build")
			.arg("--arch")
			.arg(&arch)
			.args(&[
				"--profile",
				match profile.as_str() {
					"debug" => "dev",
					profile => profile,
				},
			])
			.arg("--target-dir")
			.arg(&target_dir);

		if has_feature("instrument") {
			cmd.arg("--instrument-mcount");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(&arch)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn out_dir() -> PathBuf {
	env::var_os("OUT_DIR").unwrap().into()
}

fn target_dir() -> PathBuf {
	let mut target_dir = out_dir();
	target_dir.push("target");
	target_dir
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.args(&["--features", &features.join(" ")]);
	}
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::path::Path;
use std::borrow::Borrow;
use std::ops::Deref;

use semver::Version;
use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;

use error::StoreErrorKind as SEK;
use store::Result;
use store::Store;

/// The Index into the Store
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
    base: Option<PathBuf>,
    id:   PathBuf,
}

impl StoreId {

    pub fn storified(self, store: &Store) -> StoreId {
        if self.starts_with(store.path()) {
            debug!("Not storifying {:?}, because it is already.", self);
            self
        } else {
            debug!("Create new store id out of: {:?} and {:?}", store.path(), self.id);

            let new_id = StoreId { base: Some(store.path().clone()), self.id };

            debug!("Created: '{:?}'", new_id);
            new_id
        }
    }

    pub fn exists(&self) -> bool {
        let pb : PathBuf = self.clone().into();
        pb.exists()
    }

    pub fn is_file(&self) -> bool {
        true
    }

    pub fn is_dir(&self) -> bool {
        false
    }

}

impl Into<PathBuf> for StoreId {

    fn into(self) -> PathBuf {
        let mut base = self.base.unwrap_or(PathBuf::from("/"));
        base.push(self.id);
        base
    }

}

impl Display for StoreId {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        match self.id.to_str() {
            Some(s) => write!(fmt, "{}", s),
            None    => write!(fmt, "{}", self.id.to_string_lossy()),
        }
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> StoreId;
}

impl IntoStoreId for StoreId {
    fn into_storeid(self) -> StoreId {
        self
    }
}

pub fn build_entry_path(store: &Store, path_elem: &str) -> Result<PathBuf> {
    debug!("Checking path element for version");
    if path_elem.split('~').last().map_or(false, |v| Version::parse(v).is_err()) {
        debug!("Version cannot be parsed from {:?}", path_elem);
        debug!("Path does not contain version!");
        return Err(SEK::StorePathLacksVersion.into());
    }
    debug!("Version checking succeeded");

    debug!("Building path from {:?}", path_elem);
    let mut path = store.path().clone();

    if path_elem.starts_with('/') {
        path.push(&path_elem[1..]);
    } else {
        path.push(path_elem);
    }

    Ok(path)
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr, $version:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use semver::Version;
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let version = Version::parse($version).unwrap();
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(format!("{}~{}",
                                               name,
                                               version));
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> $crate::storeid::StoreId {
                    StoreId::from(self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    iter: Box<Iterator<Item = StoreId>>,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
        StoreIdIterator {
            iter: iter,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.iter.next()
    }

}

#[cfg(test)]
mod test {

    use storeid::IntoStoreId;

    module_entry_path_mod!("test", "0.2.0-alpha+leet1337");

    #[test]
    fn correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().to_str().unwrap(), "test/test~0.2.0-alpha+leet1337");
    }

}
<commit_msg>Add StoreId::new()<commit_after>use std::path::PathBuf;
use std::path::Path;
use std::borrow::Borrow;
use std::ops::Deref;

use semver::Version;
use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;

use error::StoreErrorKind as SEK;
use store::Result;
use store::Store;

/// The Index into the Store
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
    base: Option<PathBuf>,
    id:   PathBuf,
}

impl StoreId {

    pub fn new(base: Option<PathBuf>, id: PathBuf) -> StoreId {
        StoreId { base: base, id: id }
    }

    pub fn storified(self, store: &Store) -> StoreId {
        if self.starts_with(store.path()) {
            debug!("Not storifying {:?}, because it is already.", self);
            self
        } else {
            debug!("Create new store id out of: {:?} and {:?}", store.path(), self.id);

            let new_id = StoreId { base: Some(store.path().clone()), self.id };

            debug!("Created: '{:?}'", new_id);
            new_id
        }
    }

    pub fn exists(&self) -> bool {
        let pb : PathBuf = self.clone().into();
        pb.exists()
    }

    pub fn is_file(&self) -> bool {
        true
    }

    pub fn is_dir(&self) -> bool {
        false
    }

}

impl Into<PathBuf> for StoreId {

    fn into(self) -> PathBuf {
        let mut base = self.base.unwrap_or(PathBuf::from("/"));
        base.push(self.id);
        base
    }

}

impl Display for StoreId {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        match self.id.to_str() {
            Some(s) => write!(fmt, "{}", s),
            None    => write!(fmt, "{}", self.id.to_string_lossy()),
        }
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> StoreId;
}

impl IntoStoreId for StoreId {
    fn into_storeid(self) -> StoreId {
        self
    }
}

pub fn build_entry_path(store: &Store, path_elem: &str) -> Result<PathBuf> {
    debug!("Checking path element for version");
    if path_elem.split('~').last().map_or(false, |v| Version::parse(v).is_err()) {
        debug!("Version cannot be parsed from {:?}", path_elem);
        debug!("Path does not contain version!");
        return Err(SEK::StorePathLacksVersion.into());
    }
    debug!("Version checking succeeded");

    debug!("Building path from {:?}", path_elem);
    let mut path = store.path().clone();

    if path_elem.starts_with('/') {
        path.push(&path_elem[1..]);
    } else {
        path.push(path_elem);
    }

    Ok(path)
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr, $version:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use semver::Version;
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let version = Version::parse($version).unwrap();
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(format!("{}~{}",
                                               name,
                                               version));
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> $crate::storeid::StoreId {
                    StoreId::from(self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    iter: Box<Iterator<Item = StoreId>>,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
        StoreIdIterator {
            iter: iter,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.iter.next()
    }

}

#[cfg(test)]
mod test {

    use storeid::IntoStoreId;

    module_entry_path_mod!("test", "0.2.0-alpha+leet1337");

    #[test]
    fn correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().to_str().unwrap(), "test/test~0.2.0-alpha+leet1337");
    }

}
<|endoftext|>