<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use remove_dir_all::remove_dir_all;
use std::path::{self, Path, PathBuf};
use std::{fmt, fs, io};

use crate::error::IoResultExt;
use crate::Builder;

/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir() -> io::Result<TempDir> {
    TempDir::new()
}

/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`,
/// let dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
    TempDir::new_in(dir)
}

/// A directory in the filesystem that is automatically deleted when
/// it goes out of scope.
///
/// The [`TempDir`] type creates a directory on the file system that
/// is deleted once it goes out of scope. At construction, the
/// `TempDir` creates a new directory with a randomly generated name.
///
/// The default constructor, [`TempDir::new()`], creates directories in
/// the location returned by [`std::env::temp_dir()`], but `TempDir`
/// can be configured to manage a temporary directory in any location
/// by constructing with a [`Builder`].
///
/// After creating a `TempDir`, work with the file system by doing
/// standard [`std::fs`] file system operations on its [`Path`],
/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
/// value is dropped, the directory at the path will be deleted, along
/// with any files and directories it contains. It is your responsibility
/// to ensure that no further file system operations are attempted
/// inside the temporary directory once it has been deleted.
///
/// # Resource Leaking
///
/// Various platform-specific conditions may cause `TempDir` to fail
/// to delete the underlying directory. It's important to ensure that
/// handles (like [`File`] and [`ReadDir`]) to files inside the
/// directory are dropped before the `TempDir` goes out of scope. The
/// `TempDir` destructor will silently ignore any errors in deleting
/// the directory; to instead handle errors call [`TempDir::close()`].
///
/// Note that if the program exits before the `TempDir` destructor is
/// run, such as via [`std::process::exit()`], by segfaulting, or by
/// receiving a signal like `SIGINT`, then the temporary directory
/// will not be deleted.
///
/// # Examples
///
/// Create a temporary directory with a generated name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let tmp_dir = TempDir::new()?;
/// # Ok(())
/// # }
/// ```
///
/// Create a temporary directory with a prefix in its name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::Builder;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`,
/// // whose name will begin with 'example'.
/// let tmp_dir = Builder::new().prefix("example").tempdir()?;
/// # Ok(())
/// # }
/// ```
///
/// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html
/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
/// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html
/// [`Builder`]: struct.Builder.html
/// [`TempDir::close()`]: struct.TempDir.html#method.close
/// [`TempDir::new()`]: struct.TempDir.html#method.new
/// [`TempDir::path()`]: struct.TempDir.html#method.path
/// [`TempDir`]: struct.TempDir.html
/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
/// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html
/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
pub struct TempDir {
    path: Option<Box<Path>>,
}

impl TempDir {
    /// Attempts to make a temporary directory inside of `env::temp_dir()`.
    ///
    /// See [`Builder`] for more configuration.
    ///
    /// The directory and everything inside it will be automatically deleted
    /// once the returned `TempDir` is destroyed.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of `std::env::temp_dir()`
    /// let tmp_dir = TempDir::new()?;
    ///
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    ///
    /// // `tmp_dir` goes out of scope, the directory as well as
    /// // `tmp_file` will be deleted here.
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [`Builder`]: struct.Builder.html
    pub fn new() -> io::Result<TempDir> {
        Builder::new().tempdir()
    }

    /// Attempts to make a temporary directory inside of `dir`.
    /// The directory and everything inside it will be automatically
    /// deleted once the returned `TempDir` is destroyed.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::{self, File};
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of the current directory
    /// let tmp_dir = TempDir::new_in(".")?;
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
        Builder::new().tempdir_in(dir)
    }

    /// Accesses the [`Path`] to the temporary directory.
    ///
    /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
    ///
    /// # Examples
    ///
    /// ```
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_path;
    ///
    /// {
    ///    let tmp_dir = TempDir::new()?;
    ///    tmp_path = tmp_dir.path().to_owned();
    ///
    ///    // Check that the temp directory actually exists.
    ///    assert!(tmp_path.exists());
    ///
    ///    // End of `tmp_dir` scope, directory will be deleted
    /// }
    ///
    /// // Temp directory should be deleted by now
    /// assert_eq!(tmp_path.exists(), false);
    /// # Ok(())
    /// # }
    /// ```
    pub fn path(&self) -> &path::Path {
        self.path.as_ref().unwrap()
    }

    /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
    ///
    /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
    /// the directory will no longer be automatically deleted.
    ///
    /// [`TempDir`]: struct.TempDir.html
    /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = TempDir::new()?;
    ///
    /// // Persist the temporary directory to disk,
    /// // getting the path where it is.
    /// let tmp_path = tmp_dir.into_path();
    ///
    /// // Delete the temporary directory ourselves.
    /// fs::remove_dir_all(tmp_path)?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn into_path(mut self) -> PathBuf {
        self.path.take().unwrap().into()
    }

    /// Closes and removes the temporary directory, returning a `Result`.
    ///
    /// Although `TempDir` removes the directory on drop, in the destructor
    /// any errors are ignored. To detect errors cleaning up the temporary
    /// directory, call `close` instead.
    ///
    /// # Errors
    ///
    /// This function may return a variety of [`std::io::Error`]s that result from deleting
    /// the files and directories contained with the temporary directory,
    /// as well as from deleting the temporary directory itself. These errors
    /// may be platform specific.
    ///
    /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of `std::env::temp_dir()`.
    /// let tmp_dir = TempDir::new()?;
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    ///
    /// // By closing the `TempDir` explicitly we can check that it has
    /// // been deleted successfully. If we don't close it explicitly,
    /// // the directory will still be deleted when `tmp_dir` goes out
    /// // of scope, but we won't know whether deleting the directory
    /// // succeeded.
    /// drop(tmp_file);
    /// tmp_dir.close()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn close(mut self) -> io::Result<()> {
        let result = remove_dir_all(self.path()).with_err_path(|| self.path());

        // Prevent the Drop impl from removing the dir a second time.
        self.path = None;

        result
    }
}

impl AsRef<Path> for TempDir {
    fn as_ref(&self) -> &Path {
        self.path()
    }
}

impl fmt::Debug for TempDir {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TempDir")
            .field("path", &self.path())
            .finish()
    }
}

impl Drop for TempDir {
    fn drop(&mut self) {
        // Path is `None` if `close()` or `into_path()` has been called.
        if let Some(ref p) = self.path {
            let _ = remove_dir_all(p);
        }
    }
}

pub(crate) fn create(path: PathBuf) -> io::Result<TempDir> {
    fs::create_dir(&path)
        .with_err_path(|| &path)
        .map(|_| TempDir {
            path: Some(path.into_boxed_path()),
        })
}
<commit_msg>Reduce size of `TempDir` by removing the `Option`<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use remove_dir_all::remove_dir_all;
use std::mem;
use std::path::{self, Path, PathBuf};
use std::{fmt, fs, io};

use crate::error::IoResultExt;
use crate::Builder;

/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir() -> io::Result<TempDir> {
    TempDir::new()
}

/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`,
/// let dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
    TempDir::new_in(dir)
}

/// A directory in the filesystem that is automatically deleted when
/// it goes out of scope.
///
/// The [`TempDir`] type creates a directory on the file system that
/// is deleted once it goes out of scope. At construction, the
/// `TempDir` creates a new directory with a randomly generated name.
///
/// The default constructor, [`TempDir::new()`], creates directories in
/// the location returned by [`std::env::temp_dir()`], but `TempDir`
/// can be configured to manage a temporary directory in any location
/// by constructing with a [`Builder`].
///
/// After creating a `TempDir`, work with the file system by doing
/// standard [`std::fs`] file system operations on its [`Path`],
/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
/// value is dropped, the directory at the path will be deleted, along
/// with any files and directories it contains. It is your responsibility
/// to ensure that no further file system operations are attempted
/// inside the temporary directory once it has been deleted.
///
/// # Resource Leaking
///
/// Various platform-specific conditions may cause `TempDir` to fail
/// to delete the underlying directory. It's important to ensure that
/// handles (like [`File`] and [`ReadDir`]) to files inside the
/// directory are dropped before the `TempDir` goes out of scope. The
/// `TempDir` destructor will silently ignore any errors in deleting
/// the directory; to instead handle errors call [`TempDir::close()`].
///
/// Note that if the program exits before the `TempDir` destructor is
/// run, such as via [`std::process::exit()`], by segfaulting, or by
/// receiving a signal like `SIGINT`, then the temporary directory
/// will not be deleted.
///
/// # Examples
///
/// Create a temporary directory with a generated name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let tmp_dir = TempDir::new()?;
/// # Ok(())
/// # }
/// ```
///
/// Create a temporary directory with a prefix in its name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::Builder;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`,
/// // whose name will begin with 'example'.
/// let tmp_dir = Builder::new().prefix("example").tempdir()?;
/// # Ok(())
/// # }
/// ```
///
/// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html
/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
/// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html
/// [`Builder`]: struct.Builder.html
/// [`TempDir::close()`]: struct.TempDir.html#method.close
/// [`TempDir::new()`]: struct.TempDir.html#method.new
/// [`TempDir::path()`]: struct.TempDir.html#method.path
/// [`TempDir`]: struct.TempDir.html
/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
/// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html
/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
pub struct TempDir {
    path: Box<Path>,
}

impl TempDir {
    /// Attempts to make a temporary directory inside of `env::temp_dir()`.
    ///
    /// See [`Builder`] for more configuration.
    ///
    /// The directory and everything inside it will be automatically deleted
    /// once the returned `TempDir` is destroyed.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of `std::env::temp_dir()`
    /// let tmp_dir = TempDir::new()?;
    ///
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    ///
    /// // `tmp_dir` goes out of scope, the directory as well as
    /// // `tmp_file` will be deleted here.
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [`Builder`]: struct.Builder.html
    pub fn new() -> io::Result<TempDir> {
        Builder::new().tempdir()
    }

    /// Attempts to make a temporary directory inside of `dir`.
    /// The directory and everything inside it will be automatically
    /// deleted once the returned `TempDir` is destroyed.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::{self, File};
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of the current directory
    /// let tmp_dir = TempDir::new_in(".")?;
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
        Builder::new().tempdir_in(dir)
    }

    /// Accesses the [`Path`] to the temporary directory.
    ///
    /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
    ///
    /// # Examples
    ///
    /// ```
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_path;
    ///
    /// {
    ///    let tmp_dir = TempDir::new()?;
    ///    tmp_path = tmp_dir.path().to_owned();
    ///
    ///    // Check that the temp directory actually exists.
    ///    assert!(tmp_path.exists());
    ///
    ///    // End of `tmp_dir` scope, directory will be deleted
    /// }
    ///
    /// // Temp directory should be deleted by now
    /// assert_eq!(tmp_path.exists(), false);
    /// # Ok(())
    /// # }
    /// ```
    pub fn path(&self) -> &path::Path {
        self.path.as_ref()
    }

    /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
    ///
    /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
    /// the directory will no longer be automatically deleted.
    ///
    /// [`TempDir`]: struct.TempDir.html
    /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = TempDir::new()?;
    ///
    /// // Persist the temporary directory to disk,
    /// // getting the path where it is.
    /// let tmp_path = tmp_dir.into_path();
    ///
    /// // Delete the temporary directory ourselves.
    /// fs::remove_dir_all(tmp_path)?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn into_path(self) -> PathBuf {
        // Prevent the Drop impl from being called.
        let mut this = mem::ManuallyDrop::new(self);

        // replace this.path with an empty Box, since an empty Box does not
        // allocate any heap memory.
        mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into()
    }

    /// Closes and removes the temporary directory, returning a `Result`.
    ///
    /// Although `TempDir` removes the directory on drop, in the destructor
    /// any errors are ignored. To detect errors cleaning up the temporary
    /// directory, call `close` instead.
    ///
    /// # Errors
    ///
    /// This function may return a variety of [`std::io::Error`]s that result from deleting
    /// the files and directories contained with the temporary directory,
    /// as well as from deleting the temporary directory itself. These errors
    /// may be platform specific.
    ///
    /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::TempDir;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// // Create a directory inside of `std::env::temp_dir()`.
    /// let tmp_dir = TempDir::new()?;
    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
    /// let mut tmp_file = File::create(file_path)?;
    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
    ///
    /// // By closing the `TempDir` explicitly we can check that it has
    /// // been deleted successfully. If we don't close it explicitly,
    /// // the directory will still be deleted when `tmp_dir` goes out
    /// // of scope, but we won't know whether deleting the directory
    /// // succeeded.
    /// drop(tmp_file);
    /// tmp_dir.close()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn close(mut self) -> io::Result<()> {
        let result = remove_dir_all(self.path()).with_err_path(|| self.path());

        // Set self.path to empty Box to release the memory, since an empty
        // Box does not allocate any heap memory.
        self.path = PathBuf::new().into_boxed_path();

        // Prevent the Drop impl from being called.
        mem::forget(self);

        result
    }
}

impl AsRef<Path> for TempDir {
    fn as_ref(&self) -> &Path {
        self.path()
    }
}

impl fmt::Debug for TempDir {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TempDir")
            .field("path", &self.path())
            .finish()
    }
}

impl Drop for TempDir {
    fn drop(&mut self) {
        let _ = remove_dir_all(self.path());
    }
}

pub(crate) fn create(path: PathBuf) -> io::Result<TempDir> {
    fs::create_dir(&path)
        .with_err_path(|| &path)
        .map(|_| TempDir {
            path: path.into_boxed_path(),
        })
}
<|endoftext|>


<commit_before>use libc;
use instruction::Insn;
use constants::{CsArch, CsMode, CsErr};
use csh;

#[allow(dead_code)]
#[link(name = "capstone")]
extern "C" {
    pub fn cs_open(arch: CsArch, mode: CsMode, handle: *mut csh) -> CsErr;
    pub fn cs_close(handle: *mut csh) -> CsErr;
    pub fn cs_disasm(handle: csh,
                     code: *const u8,
                     code_size: libc::size_t,
                     address: u64,
                     count: libc::size_t,
                     insn: &mut *const Insn)
                     -> libc::size_t;
    pub fn cs_disasm_ex(handle: csh,
                        code: *const u8,
                        code_size: libc::size_t,
                        address: u64,
                        count: libc::size_t,
                        insn: &mut *const Insn)
                        -> libc::size_t;
    pub fn cs_free(insn: *const Insn, count: libc::size_t);
    pub fn cs_reg_name(handle: csh, reg_id: libc::size_t) -> *const libc::c_char;
    pub fn cs_insn_name(handle: csh, insn_id: libc::size_t) -> *const libc::c_char;
}
<commit_msg>Add cs_err<commit_after>use libc;
use instruction::Insn;
use constants::{CsArch, CsMode, CsErr};
use csh;

#[allow(dead_code)]
#[link(name = "capstone")]
extern "C" {
    pub fn cs_open(arch: CsArch, mode: CsMode, handle: *mut csh) -> CsErr;
    pub fn cs_close(handle: *mut csh) -> CsErr;
    pub fn cs_disasm(handle: csh,
                     code: *const u8,
                     code_size: libc::size_t,
                     address: u64,
                     count: libc::size_t,
                     insn: &mut *const Insn)
                     -> libc::size_t;
    pub fn cs_disasm_ex(handle: csh,
                        code: *const u8,
                        code_size: libc::size_t,
                        address: u64,
                        count: libc::size_t,
                        insn: &mut *const Insn)
                        -> libc::size_t;
    pub fn cs_free(insn: *const Insn, count: libc::size_t);
    pub fn cs_reg_name(handle: csh, reg_id: libc::size_t) -> *const libc::c_char;
    pub fn cs_insn_name(handle: csh, insn_id: libc::size_t) -> *const libc::c_char;
    pub fn cs_errno(handle: csh) -> CsErr;
}
<|endoftext|>


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

use crate::internal::prelude::*;
use crate::json::{from_number, NULL};
use crate::model::prelude::*;

/// A builder to optionally edit certain fields of a [`Guild`]. This is meant
/// for usage with [`Guild::edit`].
///
/// **Note**: Editing a guild requires that the current user have the
/// [Manage Guild] permission.
///
/// [`Guild::edit`]: crate::model::guild::Guild::edit
/// [`Guild`]: crate::model::guild::Guild
/// [Manage Guild]: crate::model::permissions::Permissions::MANAGE_GUILD
#[derive(Clone, Debug, Default)]
pub struct EditGuild(pub HashMap<&'static str, Value>);

impl EditGuild {
    /// Set the "AFK voice channel" that users are to move to if they have been
    /// AFK for an amount of time, configurable by [`Self::afk_timeout`].
    ///
    /// The given channel must be either some valid voice channel, or [`None`] to
    /// not set an AFK channel. The library does not check if a channel is
    /// valid.
    #[inline]
    pub fn afk_channel<C: Into<ChannelId>>(&mut self, channel: Option<C>) -> &mut Self {
        self._afk_channel(channel.map(Into::into));
        self
    }

    fn _afk_channel(&mut self, channel: Option<ChannelId>) {
        self.0.insert("afk_channel_id", match channel {
            Some(channel) => from_number(channel.0),
            None => NULL,
        });
    }

    /// Set the amount of time a user is to be moved to the AFK channel -
    /// configured via [`Self::afk_channel`] - after being AFK.
    pub fn afk_timeout(&mut self, timeout: u64) -> &mut Self {
        self.0.insert("afk_timeout", from_number(timeout));
        self
    }

    /// Set the icon of the guild. Pass [`None`] to remove the icon.
    ///
    /// # Examples
    ///
    /// Using the utility function - [`utils::read_image`] - to read an image
    /// from the cwd and encode it in base64 to send to Discord.
    ///
    /// ```rust,no_run
    /// # use serenity::{http::Http, model::id::GuildId};
    /// #
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// #     let http = Http::new("token");
    /// #     let mut guild = GuildId(0).to_partial_guild(&http).await?;
    /// use serenity::utils;
    ///
    /// // assuming a `guild` has already been bound
    ///
    /// let base64_icon = utils::read_image("./guild_icon.png")?;
    ///
    /// guild.edit(&http, |g| g.icon(Some(&base64_icon))).await?;
    /// #     Ok(())
    /// # }
    /// ```
    ///
    /// [`utils::read_image`]: crate::utils::read_image
    pub fn icon(&mut self, icon: Option<&str>) -> &mut Self {
        self.0.insert("icon", icon.map_or_else(|| NULL, |x| Value::from(x.to_string())));
        self
    }

    /// Set the name of the guild.
    ///
    /// **Note**: Must be between (and including) 2-100 characters.
    pub fn name<S: ToString>(&mut self, name: S) -> &mut Self {
        self.0.insert("name", Value::from(name.to_string()));
        self
    }

    /// Set the description of the guild.
    ///
    /// **Note**: Requires that the guild have the `DISCOVERABLE` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn description<S: ToString>(&mut self, name: S) -> &mut Self {
        self.0.insert("name", Value::from(name.to_string()));
        self
    }

    /// Set the features of the guild.
    ///
    /// **Note**: Requires that the guild have the `DISCOVERABLE` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn features(&mut self, features: Vec<String>) -> &mut Self {
        let mut values: Vec<Value> = vec![];

        for value in features {
            values.push(Value::from(value));
        }

        self.0.insert("features", Value::from(values));
        self
    }

    /// Transfers the ownership of the guild to another user by Id.
    ///
    /// **Note**: The current user must be the owner of the guild.
    #[inline]
    pub fn owner<U: Into<UserId>>(&mut self, user_id: U) -> &mut Self {
        self._owner(user_id.into());
        self
    }

    fn _owner(&mut self, user_id: UserId) {
        let id = from_number(user_id.0);
        self.0.insert("owner_id", id);
    }

    /// Set the splash image of the guild on the invitation page.
    ///
    /// The `splash` must be base64-encoded 1024x1024 png/jpeg/gif image-data.
    ///
    /// Requires that the guild have the `INVITE_SPLASH` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn splash(&mut self, splash: Option<&str>) -> &mut Self {
        let splash = splash.map_or(NULL, |x| Value::from(x.to_string()));
        self.0.insert("splash", splash);
        self
    }

    /// Set the splash image of the guild on the discovery page.
    ///
    /// The `splash` must be base64-encoded 1024x1024 png/jpeg/gif image-data.
    ///
    /// Requires that the guild have the `DISCOVERABLE` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn discovery_splash(&mut self, splash: Option<&str>) -> &mut Self {
        let splash = splash.map_or(NULL, |x| Value::from(x.to_string()));
        self.0.insert("splash", splash);
        self
    }

    /// Set the banner image of the guild, it appears on the left side-bar.
    ///
    /// The `banner` must be base64-encoded 16:9 png/jpeg image data.
    ///
    /// Requires that the guild have the `BANNER` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn banner(&mut self, banner: Option<&str>) -> &mut Self {
        let banner = banner.map_or(NULL, |x| Value::from(x.to_string()));
        self.0.insert("banner", banner);
        self
    }

    /// Set the channel ID where welcome messages and boost events will be
    /// posted.
    pub fn system_channel_id(&mut self, channel_id: Option<ChannelId>) -> &mut Self {
        let channel_id = channel_id.map_or(NULL, |x| Value::from(x.0));
        self.0.insert("system_channel_id", channel_id);
        self
    }

    /// Set the channel ID of the rules and guidelines channel.
    ///
    /// **Note**:
    /// This feature is for Community guilds only.
    pub fn rules_channel_id(&mut self, channel_id: Option<ChannelId>) -> &mut Self {
        let channel_id = channel_id.map_or(NULL, |x| Value::from(x.0));
        self.0.insert("rules_channel_id", channel_id);
        self
    }

    /// Set the channel ID where admins and moderators receive update messages
    /// from Discord.
    ///
    /// **Note**:
    /// This feature is for Community guilds only.
    pub fn public_updates_channel_id(&mut self, channel_id: Option<ChannelId>) -> &mut Self {
        let channel_id = channel_id.map_or(NULL, |x| Value::from(x.0));
        self.0.insert("public_updates_channel_id", channel_id);
        self
    }

    /// Set the preferred locale used in Server Discovery and update messages
    /// from Discord.
    ///
    /// If this is not set, the locale will default to "en-US";
    ///
    /// **Note**:
    /// This feature is for Community guilds only.
    pub fn preferred_locale(&mut self, locale: Option<&str>) -> &mut Self {
        let locale = locale.map_or(NULL, |x| Value::from(x.to_string()));
        self.0.insert("preferred_locale", locale);
        self
    }

    /// Set the content filter level.
    pub fn explicit_content_filter(&mut self, level: Option<ExplicitContentFilter>) -> &mut Self {
        let level = level.map_or(NULL, |x| Value::from(x as u8));
        self.0.insert("explicit_content_filter", level);
        self
    }

    /// Set the default message notification level.
    pub fn default_message_notifications(
        &mut self,
        level: Option<DefaultMessageNotificationLevel>,
    ) -> &mut Self {
        let level = level.map_or(NULL, |x| Value::from(x as u8));
        self.0.insert("default_message_notifications", level);
        self
    }

    /// Set the verification level of the guild. This can restrict what a
    /// user must have prior to being able to send messages in a guild.
    ///
    /// Refer to the documentation for [`VerificationLevel`] for more
    /// information on each variant.
    ///
    ///
    /// # Examples
    ///
    /// Setting the verification level to [`High`][`VerificationLevel::High`]:
    ///
    /// ```rust,no_run
    /// # use serenity::{http::Http, model::id::GuildId};
    /// #
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// #     let http = Http::new("token");
    /// #     let mut guild = GuildId(0).to_partial_guild(&http).await?;
    /// use serenity::model::guild::VerificationLevel;
    ///
    /// // assuming a `guild` has already been bound
    ///
    /// let edit = guild.edit(&http, |g| g.verification_level(VerificationLevel::High)).await;
    ///
    /// if let Err(why) = edit {
    ///     println!("Error setting verification level: {:?}", why);
    /// }
    /// #     Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn verification_level<V>(&mut self, verification_level: V) -> &mut Self
    where
        V: Into<VerificationLevel>,
    {
        self._verification_level(verification_level.into());
        self
    }

    fn _verification_level(&mut self, verification_level: VerificationLevel) {
        let num = from_number(verification_level.num());
        self.0.insert("verification_level", num);
    }

    /// Modifies the notifications that are sent by discord to the configured system channel.
    ///
    /// ```rust,no_run
    /// # use serenity::{http::Http, model::id::GuildId};
    /// #
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// #     let http = Http::new("token");
    /// #     let mut guild = GuildId(0).to_partial_guild(&http).await?;
    /// use serenity::model::guild::SystemChannelFlags;
    ///
    /// // assuming a `guild` has already been bound
    ///
    /// let edit = guild
    ///     .edit(&http, |g| {
    ///         g.system_channel_flags(
    ///             SystemChannelFlags::SUPPRESS_JOIN_NOTIFICATIONS
    ///                 | SystemChannelFlags::SUPPRESS_GUILD_REMINDER_NOTIFICATIONS,
    ///         )
    ///     })
    ///     .await;
    ///
    /// if let Err(why) = edit {
    ///     println!("Error setting verification level: {:?}", why);
    /// }
    /// #     Ok(())
    /// # }
    /// ```
    pub fn system_channel_flags(&mut self, system_channel_flags: SystemChannelFlags) -> &mut Self {
        self.0.insert("system_channel_flags", system_channel_flags.bits().into());
        self
    }
}
<commit_msg>Fix typo in the `EditGuild::discovery_splash` field (#1956)<commit_after>use std::collections::HashMap;

use crate::internal::prelude::*;
use crate::json::{from_number, NULL};
use crate::model::prelude::*;

/// A builder to optionally edit certain fields of a [`Guild`]. This is meant
/// for usage with [`Guild::edit`].
///
/// **Note**: Editing a guild requires that the current user have the
/// [Manage Guild] permission.
///
/// [`Guild::edit`]: crate::model::guild::Guild::edit
/// [`Guild`]: crate::model::guild::Guild
/// [Manage Guild]: crate::model::permissions::Permissions::MANAGE_GUILD
#[derive(Clone, Debug, Default)]
pub struct EditGuild(pub HashMap<&'static str, Value>);

impl EditGuild {
    /// Set the "AFK voice channel" that users are to move to if they have been
    /// AFK for an amount of time, configurable by [`Self::afk_timeout`].
    ///
    /// The given channel must be either some valid voice channel, or [`None`] to
    /// not set an AFK channel. The library does not check if a channel is
    /// valid.
    #[inline]
    pub fn afk_channel<C: Into<ChannelId>>(&mut self, channel: Option<C>) -> &mut Self {
        self._afk_channel(channel.map(Into::into));
        self
    }

    fn _afk_channel(&mut self, channel: Option<ChannelId>) {
        self.0.insert("afk_channel_id", match channel {
            Some(channel) => from_number(channel.0),
            None => NULL,
        });
    }

    /// Set the amount of time a user is to be moved to the AFK channel -
    /// configured via [`Self::afk_channel`] - after being AFK.
    pub fn afk_timeout(&mut self, timeout: u64) -> &mut Self {
        self.0.insert("afk_timeout", from_number(timeout));
        self
    }

    /// Set the icon of the guild. Pass [`None`] to remove the icon.
    ///
    /// # Examples
    ///
    /// Using the utility function - [`utils::read_image`] - to read an image
    /// from the cwd and encode it in base64 to send to Discord.
    ///
    /// ```rust,no_run
    /// # use serenity::{http::Http, model::id::GuildId};
    /// #
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// #     let http = Http::new("token");
    /// #     let mut guild = GuildId(0).to_partial_guild(&http).await?;
    /// use serenity::utils;
    ///
    /// // assuming a `guild` has already been bound
    ///
    /// let base64_icon = utils::read_image("./guild_icon.png")?;
    ///
    /// guild.edit(&http, |g| g.icon(Some(&base64_icon))).await?;
    /// #     Ok(())
    /// # }
    /// ```
    ///
    /// [`utils::read_image`]: crate::utils::read_image
    pub fn icon(&mut self, icon: Option<&str>) -> &mut Self {
        self.0.insert("icon", icon.map_or_else(|| NULL, |x| Value::from(x.to_string())));
        self
    }

    /// Set the name of the guild.
    ///
    /// **Note**: Must be between (and including) 2-100 characters.
    pub fn name<S: ToString>(&mut self, name: S) -> &mut Self {
        self.0.insert("name", Value::from(name.to_string()));
        self
    }

    /// Set the description of the guild.
    ///
    /// **Note**: Requires that the guild have the `DISCOVERABLE` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn description<S: ToString>(&mut self, name: S) -> &mut Self {
        self.0.insert("name", Value::from(name.to_string()));
        self
    }

    /// Set the features of the guild.
    ///
    /// **Note**: Requires that the guild have the `DISCOVERABLE` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn features(&mut self, features: Vec<String>) -> &mut Self {
        let mut values: Vec<Value> = vec![];

        for value in features {
            values.push(Value::from(value));
        }

        self.0.insert("features", Value::from(values));
        self
    }

    /// Transfers the ownership of the guild to another user by Id.
    ///
    /// **Note**: The current user must be the owner of the guild.
    #[inline]
    pub fn owner<U: Into<UserId>>(&mut self, user_id: U) -> &mut Self {
        self._owner(user_id.into());
        self
    }

    fn _owner(&mut self, user_id: UserId) {
        let id = from_number(user_id.0);
        self.0.insert("owner_id", id);
    }

    /// Set the splash image of the guild on the invitation page.
    ///
    /// The `splash` must be base64-encoded 1024x1024 png/jpeg/gif image-data.
    ///
    /// Requires that the guild have the `INVITE_SPLASH` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn splash(&mut self, splash: Option<&str>) -> &mut Self {
        let splash = splash.map_or(NULL, |x| Value::from(x.to_string()));
        self.0.insert("splash", splash);
        self
    }

    /// Set the splash image of the guild on the discovery page.
    ///
    /// The `splash` must be base64-encoded 1024x1024 png/jpeg/gif image-data.
    ///
    /// Requires that the guild have the `DISCOVERABLE` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn discovery_splash(&mut self, splash: Option<&str>) -> &mut Self {
        let splash = splash.map_or(NULL, |x| Value::from(x.to_string()));
        self.0.insert("discovery_splash", splash);
        self
    }

    /// Set the banner image of the guild, it appears on the left side-bar.
    ///
    /// The `banner` must be base64-encoded 16:9 png/jpeg image data.
    ///
    /// Requires that the guild have the `BANNER` feature enabled.
    /// You can check this through a guild's [`features`] list.
    ///
    /// [`features`]: crate::model::guild::Guild::features
    pub fn banner(&mut self, banner: Option<&str>) -> &mut Self {
        let banner = banner.map_or(NULL, |x| Value::from(x.to_string()));
        self.0.insert("banner", banner);
        self
    }

    /// Set the channel ID where welcome messages and boost events will be
    /// posted.
    pub fn system_channel_id(&mut self, channel_id: Option<ChannelId>) -> &mut Self {
        let channel_id = channel_id.map_or(NULL, |x| Value::from(x.0));
        self.0.insert("system_channel_id", channel_id);
        self
    }

    /// Set the channel ID of the rules and guidelines channel.
    ///
    /// **Note**:
    /// This feature is for Community guilds only.
    pub fn rules_channel_id(&mut self, channel_id: Option<ChannelId>) -> &mut Self {
        let channel_id = channel_id.map_or(NULL, |x| Value::from(x.0));
        self.0.insert("rules_channel_id", channel_id);
        self
    }

    /// Set the channel ID where admins and moderators receive update messages
    /// from Discord.
    ///
    /// **Note**:
    /// This feature is for Community guilds only.
    pub fn public_updates_channel_id(&mut self, channel_id: Option<ChannelId>) -> &mut Self {
        let channel_id = channel_id.map_or(NULL, |x| Value::from(x.0));
        self.0.insert("public_updates_channel_id", channel_id);
        self
    }

    /// Set the preferred locale used in Server Discovery and update messages
    /// from Discord.
    ///
    /// If this is not set, the locale will default to "en-US";
    ///
    /// **Note**:
    /// This feature is for Community guilds only.
    pub fn preferred_locale(&mut self, locale: Option<&str>) -> &mut Self {
        let locale = locale.map_or(NULL, |x| Value::from(x.to_string()));
        self.0.insert("preferred_locale", locale);
        self
    }

    /// Set the content filter level.
    pub fn explicit_content_filter(&mut self, level: Option<ExplicitContentFilter>) -> &mut Self {
        let level = level.map_or(NULL, |x| Value::from(x as u8));
        self.0.insert("explicit_content_filter", level);
        self
    }

    /// Set the default message notification level.
    pub fn default_message_notifications(
        &mut self,
        level: Option<DefaultMessageNotificationLevel>,
    ) -> &mut Self {
        let level = level.map_or(NULL, |x| Value::from(x as u8));
        self.0.insert("default_message_notifications", level);
        self
    }

    /// Set the verification level of the guild. This can restrict what a
    /// user must have prior to being able to send messages in a guild.
    ///
    /// Refer to the documentation for [`VerificationLevel`] for more
    /// information on each variant.
    ///
    ///
    /// # Examples
    ///
    /// Setting the verification level to [`High`][`VerificationLevel::High`]:
    ///
    /// ```rust,no_run
    /// # use serenity::{http::Http, model::id::GuildId};
    /// #
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// #     let http = Http::new("token");
    /// #     let mut guild = GuildId(0).to_partial_guild(&http).await?;
    /// use serenity::model::guild::VerificationLevel;
    ///
    /// // assuming a `guild` has already been bound
    ///
    /// let edit = guild.edit(&http, |g| g.verification_level(VerificationLevel::High)).await;
    ///
    /// if let Err(why) = edit {
    ///     println!("Error setting verification level: {:?}", why);
    /// }
    /// #     Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn verification_level<V>(&mut self, verification_level: V) -> &mut Self
    where
        V: Into<VerificationLevel>,
    {
        self._verification_level(verification_level.into());
        self
    }

    fn _verification_level(&mut self, verification_level: VerificationLevel) {
        let num = from_number(verification_level.num());
        self.0.insert("verification_level", num);
    }

    /// Modifies the notifications that are sent by discord to the configured system channel.
    ///
    /// ```rust,no_run
    /// # use serenity::{http::Http, model::id::GuildId};
    /// #
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// #     let http = Http::new("token");
    /// #     let mut guild = GuildId(0).to_partial_guild(&http).await?;
    /// use serenity::model::guild::SystemChannelFlags;
    ///
    /// // assuming a `guild` has already been bound
    ///
    /// let edit = guild
    ///     .edit(&http, |g| {
    ///         g.system_channel_flags(
    ///             SystemChannelFlags::SUPPRESS_JOIN_NOTIFICATIONS
    ///                 | SystemChannelFlags::SUPPRESS_GUILD_REMINDER_NOTIFICATIONS,
    ///         )
    ///     })
    ///     .await;
    ///
    /// if let Err(why) = edit {
    ///     println!("Error setting verification level: {:?}", why);
    /// }
    /// #     Ok(())
    /// # }
    /// ```
    pub fn system_channel_flags(&mut self, system_channel_flags: SystemChannelFlags) -> &mut Self {
        self.0.insert("system_channel_flags", system_channel_flags.bits().into());
        self
    }
}
<|endoftext|>


<commit_before>use curl;
use git2;

use anyhow::Error;

use crate::util::errors::{CargoResult, HttpNot200};
use crate::util::Config;

pub struct Retry<'a> {
    config: &'a Config,
    remaining: u32,
}

impl<'a> Retry<'a> {
    pub fn new(config: &'a Config) -> CargoResult<Retry<'a>> {
        Ok(Retry {
            config,
            remaining: config.net_config()?.retry.unwrap_or(2),
        })
    }

    pub fn r#try<T>(&mut self, f: impl FnOnce() -> CargoResult<T>) -> CargoResult<Option<T>> {
        match f() {
            Err(ref e) if maybe_spurious(e) && self.remaining > 0 => {
                let msg = format!(
                    "spurious network error ({} tries \
                     remaining): {}",
                    self.remaining, e
                );
                self.config.shell().warn(msg)?;
                self.remaining -= 1;
                Ok(None)
            }
            other => other.map(Some),
        }
    }
}

fn maybe_spurious(err: &Error) -> bool {
    for e in err.chain() {
        if let Some(git_err) = e.downcast_ref::<git2::Error>() {
            match git_err.class() {
                git2::ErrorClass::Net | git2::ErrorClass::Os => return true,
                _ => (),
            }
        }
        if let Some(curl_err) = e.downcast_ref::<curl::Error>() {
            if curl_err.is_couldnt_connect()
                || curl_err.is_couldnt_resolve_proxy()
                || curl_err.is_couldnt_resolve_host()
                || curl_err.is_operation_timedout()
                || curl_err.is_recv_error()
                || curl_err.is_http2_stream_error()
                || curl_err.is_ssl_connect_error()
            {
                return true;
            }
        }
        if let Some(not_200) = e.downcast_ref::<HttpNot200>() {
            if 500 <= not_200.code && not_200.code < 600 {
                return true;
            }
        }
    }
    false
}

/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object `net.retry`. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a `CargoResult`.
///
/// # Examples
///
/// ```
/// # use crate::cargo::util::{CargoResult, Config};
/// # let download_something = || return Ok(());
/// # let config = Config::default().unwrap();
/// use cargo::util::network;
/// let cargo_result = network::with_retry(&config, || download_something());
/// ```
pub fn with_retry<T, F>(config: &Config, mut callback: F) -> CargoResult<T>
where
    F: FnMut() -> CargoResult<T>,
{
    let mut retry = Retry::new(config)?;
    loop {
        if let Some(ret) = retry.r#try(&mut callback)? {
            return Ok(ret);
        }
    }
}

#[test]
fn with_retry_repeats_the_call_then_works() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    let error1 = HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    }
    .into();
    let error2 = HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    }
    .into();
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn with_retry_finds_nested_spurious_errors() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    //String error messages are not considered spurious
    let error1 = anyhow::Error::from(HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    });
    let error1 = anyhow::Error::from(error1.context("A non-spurious wrapping err"));
    let error2 = anyhow::Error::from(HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    });
    let error2 = anyhow::Error::from(error2.context("A second chained error"));
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn curle_http2_stream_is_spurious() {
    let code = curl_sys::CURLE_HTTP2_STREAM;
    let err = curl::Error::new(code);
    assert!(maybe_spurious(&err.into()));
}
<commit_msg>Add another curl spurious network error<commit_after>use curl;
use git2;

use anyhow::Error;

use crate::util::errors::{CargoResult, HttpNot200};
use crate::util::Config;

pub struct Retry<'a> {
    config: &'a Config,
    remaining: u32,
}

impl<'a> Retry<'a> {
    pub fn new(config: &'a Config) -> CargoResult<Retry<'a>> {
        Ok(Retry {
            config,
            remaining: config.net_config()?.retry.unwrap_or(2),
        })
    }

    pub fn r#try<T>(&mut self, f: impl FnOnce() -> CargoResult<T>) -> CargoResult<Option<T>> {
        match f() {
            Err(ref e) if maybe_spurious(e) && self.remaining > 0 => {
                let msg = format!(
                    "spurious network error ({} tries \
                     remaining): {}",
                    self.remaining, e
                );
                self.config.shell().warn(msg)?;
                self.remaining -= 1;
                Ok(None)
            }
            other => other.map(Some),
        }
    }
}

fn maybe_spurious(err: &Error) -> bool {
    for e in err.chain() {
        if let Some(git_err) = e.downcast_ref::<git2::Error>() {
            match git_err.class() {
                git2::ErrorClass::Net | git2::ErrorClass::Os => return true,
                _ => (),
            }
        }
        if let Some(curl_err) = e.downcast_ref::<curl::Error>() {
            if curl_err.is_couldnt_connect()
                || curl_err.is_couldnt_resolve_proxy()
                || curl_err.is_couldnt_resolve_host()
                || curl_err.is_operation_timedout()
                || curl_err.is_recv_error()
                || curl_err.is_http2_stream_error()
                || curl_err.is_ssl_connect_error()
                || curl_err.is_partial_file()
            {
                return true;
            }
        }
        if let Some(not_200) = e.downcast_ref::<HttpNot200>() {
            if 500 <= not_200.code && not_200.code < 600 {
                return true;
            }
        }
    }
    false
}

/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object `net.retry`. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a `CargoResult`.
///
/// # Examples
///
/// ```
/// # use crate::cargo::util::{CargoResult, Config};
/// # let download_something = || return Ok(());
/// # let config = Config::default().unwrap();
/// use cargo::util::network;
/// let cargo_result = network::with_retry(&config, || download_something());
/// ```
pub fn with_retry<T, F>(config: &Config, mut callback: F) -> CargoResult<T>
where
    F: FnMut() -> CargoResult<T>,
{
    let mut retry = Retry::new(config)?;
    loop {
        if let Some(ret) = retry.r#try(&mut callback)? {
            return Ok(ret);
        }
    }
}

#[test]
fn with_retry_repeats_the_call_then_works() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    let error1 = HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    }
    .into();
    let error2 = HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    }
    .into();
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn with_retry_finds_nested_spurious_errors() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    //String error messages are not considered spurious
    let error1 = anyhow::Error::from(HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    });
    let error1 = anyhow::Error::from(error1.context("A non-spurious wrapping err"));
    let error2 = anyhow::Error::from(HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    });
    let error2 = anyhow::Error::from(error2.context("A second chained error"));
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn curle_http2_stream_is_spurious() {
    let code = curl_sys::CURLE_HTTP2_STREAM;
    let err = curl::Error::new(code);
    assert!(maybe_spurious(&err.into()));
}
<|endoftext|>


<commit_before>use crate::analysis::symbols;
use once_cell::sync::Lazy;
use regex::{Captures, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, symbols: &symbols::Info) -> String {
    code_blocks_transformation(input, symbols)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, symbols: &symbols::Info) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, symbols));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, symbols));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(mut input: &str, symbols: &symbols::Info) -> String {
    let mut ret = String::with_capacity(input.len());
    loop {
        let (before, after) = try_split(input, "`");
        ret.push_str(&replace_c_types(before, symbols));
        if let Some(after) = after {
            ret.push('`');
            let (before, after) = try_split(after, "`");
            // don't touch anything enclosed in backticks
            ret.push_str(before);
            if let Some(after) = after {
                ret.push('`');
                input = after;
            } else {
                return ret;
            }
        } else {
            return ret;
        }
    }
}

static SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(^|[^\\])[@#%]([\w]+\b)([:.]+[\w_-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\b[a-z0-9_]+)\(\)").unwrap());
static GDK_GTK: Lazy<Regex> = Lazy::new(|| Regex::new(r"G[dt]k[A-Z][\w]+\b").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ][ ]+").unwrap());

fn replace_c_types(entry: &str, symbols: &symbols::Info) -> String {
    let lookup = |s: &str| -> String {
        symbols
            .by_c_name(s)
            .map(symbols::Symbol::full_rust_name)
            .unwrap_or_else(|| s.into())
    };
    let out = SYMBOL.replace_all(entry, |caps: &Captures<'_>| {
        format!(
            "{}`{}{}`",
            &caps[1],
            lookup(&caps[2]),
            caps.get(3).map(|m| m.as_str()).unwrap_or("")
        )
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| {
        format!("`{}`", lookup(&caps[0]))
    });
    let out = FUNCTION.replace_all(&out, |caps: &Captures<'_>| {
        format!("`{}`", lookup(&caps[1]))
    });
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}
<commit_msg>codegen/doc: Clean up regexes and extract reference type symbol<commit_after>use crate::analysis::symbols;
use once_cell::sync::Lazy;
use regex::{Captures, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, symbols: &symbols::Info) -> String {
    code_blocks_transformation(input, symbols)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, symbols: &symbols::Info) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, symbols));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, symbols));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(mut input: &str, symbols: &symbols::Info) -> String {
    let mut ret = String::with_capacity(input.len());
    loop {
        let (before, after) = try_split(input, "`");
        ret.push_str(&replace_c_types(before, symbols));
        if let Some(after) = after {
            ret.push('`');
            let (before, after) = try_split(after, "`");
            // don't touch anything enclosed in backticks
            ret.push_str(before);
            if let Some(after) = after {
                ret.push('`');
                input = after;
            } else {
                return ret;
            }
        } else {
            return ret;
        }
    }
}

static SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(^|[^\\])([@#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\b[a-z0-9_]+)\(\)").unwrap());
static GDK_GTK: Lazy<Regex> = Lazy::new(|| Regex::new(r"G[dt]k[A-Z]\w+\b").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, symbols: &symbols::Info) -> String {
    let lookup = |s: &str| -> String {
        symbols
            .by_c_name(s)
            .map(symbols::Symbol::full_rust_name)
            .unwrap_or_else(|| s.into())
    };

    let out = SYMBOL.replace_all(entry, |caps: &Captures<'_>| {
        let member = caps.get(4).map(|m| m.as_str()).unwrap_or("");
        match &caps[2] {
            "@" | "#" | "%" => format!("{}`{}{}`", &caps[1], lookup(&caps[3]), member),
            u => panic!("Unknown reference type {}", u),
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| {
        format!("`{}`", lookup(&caps[0]))
    });
    let out = FUNCTION.replace_all(&out, |caps: &Captures<'_>| {
        format!("`{}`", lookup(&caps[1]))
    });
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}
<|endoftext|>


<commit_before>use crate::{
    analysis::{rust_type::*, types::*},
    codegen::sys::{ffi_type::ffi_type, functions::function_signature},
    env::Env,
    library::*,
    traits::{IntoString, MaybeRefAs},
};

pub struct Fields {
    /// Name of union, class, or a record that contains the fields.
    pub name: String,
    /// Is this external type?
    pub external: bool,
    /// Reason for truncating the representation, if any.
    pub truncated: Option<String>,
    derives_copy: bool,
    /// "struct" or "union"
    pub kind: &'static str,
    /// specified GObject cfg condition
    pub cfg_condition: Option<String>,
    pub fields: Vec<FieldInfo>,
}

pub struct FieldInfo {
    /// Rust field name
    pub name: String,
    /// Rust type name
    pub typ: String,
    /// Does access to this field require unsafe block?
    unsafe_access: bool,
    /// Include this field in Debug impl?
    pub debug: bool,
}

impl Fields {
    /// List of derived traits
    pub fn derived_traits(&self) -> Vec<&'static str> {
        let mut traits = Vec::new();
        if self.derives_copy {
            traits.push("Copy");
            traits.push("Clone");
        }
        traits
    }
}

impl FieldInfo {
    /// Generates a string that accesses the field in the context of &self receiver.
    pub fn access_str(&self) -> String {
        let mut s = format!("&self.{}", self.name);
        if self.unsafe_access {
            s = format!("unsafe {{ {} }}", s);
        }
        s
    }
}

pub fn from_record(env: &Env, record: &Record) -> Fields {
    let (fields, truncated) = analyze_fields(env, false, &record.fields);
    Fields {
        name: record.c_type.clone(),
        external: record.is_external(&env.library),
        truncated,
        derives_copy: record.derives_copy(&env.library),
        kind: "struct",
        cfg_condition: get_gobject_cfg_condition(env, &record.name),
        fields,
    }
}

pub fn from_class(env: &Env, klass: &Class) -> Fields {
    let (fields, truncated) = analyze_fields(env, false, &klass.fields);

    Fields {
        name: klass.c_type.clone(),
        external: klass.is_external(&env.library),
        truncated,
        derives_copy: klass.derives_copy(&env.library),
        kind: "struct",
        cfg_condition: get_gobject_cfg_condition(env, &klass.name),
        fields,
    }
}

pub fn from_union(env: &Env, union: &Union) -> Fields {
    let (fields, truncated) = analyze_fields(env, true, &union.fields);
    Fields {
        name: union.c_type.as_ref().unwrap().clone(),
        external: union.is_external(&env.library),
        truncated,
        derives_copy: union.derives_copy(&env.library),
        kind: "union",
        cfg_condition: None,
        fields,
    }
}

fn analyze_fields(
    env: &Env,
    unsafe_access: bool,
    fields: &[Field],
) -> (Vec<FieldInfo>, Option<String>) {
    let mut truncated = None;
    let mut infos = Vec::with_capacity(fields.len());

    let mut is_bitfield = false;
    for field in fields {
        // See IsIncomplete for &[Field].
        if is_bitfield && field.bits.is_some() {
            truncated = Some(format!("field {} has incomplete type", &field.name));
            break;
        }
        is_bitfield = field.bits.is_some();

        let typ = match field_ffi_type(env, field) {
            e @ Err(..) => {
                truncated = Some(e.into_string());
                break;
            }
            Ok(typ) => typ,
        };
        // Skip private fields from Debug impl. Ignore volatile as well,
        // they are usually used as synchronization primites,
        // so we wouldn't want to introduce additional reads.
        let debug = !field.private && !field.is_volatile() && field.implements_debug(&env.library);

        infos.push(FieldInfo {
            name: field.name.clone(),
            typ,
            debug,
            unsafe_access,
        });
    }

    (infos, truncated)
}

fn field_ffi_type(env: &Env, field: &Field) -> Result {
    if field.is_incomplete(&env.library) {
        return Err(TypeError::Ignored(format!(
            "field {} has incomplete type",
            &field.name
        )));
    }
    if let Some(ref c_type) = field.c_type {
        ffi_type(env, field.typ, c_type)
    } else if let Some(func) = env.library.type_(field.typ).maybe_ref_as::<Function>() {
        let (failure, signature) = function_signature(env, func, true);
        let signature = format!("Option<unsafe extern \"C\" fn{}>", signature);
        if failure {
            Err(TypeError::Unimplemented(signature))
        } else {
            Ok(signature)
        }
    } else {
        Err(TypeError::Ignored(format!(
            "field {} has empty c:type",
            &field.name
        )))
    }
}

fn get_gobject_cfg_condition(env: &Env, name: &str) -> Option<String> {
    let full_name = format!("{}.{}", env.namespaces.main().name, name);
    if let Some(obj) = env.config.objects.get(&full_name) {
        obj.cfg_condition.clone()
    } else {
        None
    }
}
<commit_msg>Don't derive Copy, Clone for truncated in sys mode<commit_after>use crate::{
    analysis::{rust_type::*, types::*},
    codegen::sys::{ffi_type::ffi_type, functions::function_signature},
    env::Env,
    library::*,
    traits::{IntoString, MaybeRefAs},
};

pub struct Fields {
    /// Name of union, class, or a record that contains the fields.
    pub name: String,
    /// Is this external type?
    pub external: bool,
    /// Reason for truncating the representation, if any.
    pub truncated: Option<String>,
    derives_copy: bool,
    /// "struct" or "union"
    pub kind: &'static str,
    /// specified GObject cfg condition
    pub cfg_condition: Option<String>,
    pub fields: Vec<FieldInfo>,
}

pub struct FieldInfo {
    /// Rust field name
    pub name: String,
    /// Rust type name
    pub typ: String,
    /// Does access to this field require unsafe block?
    unsafe_access: bool,
    /// Include this field in Debug impl?
    pub debug: bool,
}

impl Fields {
    /// List of derived traits
    pub fn derived_traits(&self) -> Vec<&'static str> {
        let mut traits = Vec::new();
        if self.derives_copy {
            traits.push("Copy");
            traits.push("Clone");
        }
        traits
    }
}

impl FieldInfo {
    /// Generates a string that accesses the field in the context of &self receiver.
    pub fn access_str(&self) -> String {
        let mut s = format!("&self.{}", self.name);
        if self.unsafe_access {
            s = format!("unsafe {{ {} }}", s);
        }
        s
    }
}

pub fn from_record(env: &Env, record: &Record) -> Fields {
    let (fields, truncated) = analyze_fields(env, false, &record.fields);
    let derives_copy = truncated.is_none() && record.derives_copy(&env.library);
    Fields {
        name: record.c_type.clone(),
        external: record.is_external(&env.library),
        truncated,
        derives_copy,
        kind: "struct",
        cfg_condition: get_gobject_cfg_condition(env, &record.name),
        fields,
    }
}

pub fn from_class(env: &Env, klass: &Class) -> Fields {
    let (fields, truncated) = analyze_fields(env, false, &klass.fields);
    let derives_copy = truncated.is_none() && klass.derives_copy(&env.library);
    Fields {
        name: klass.c_type.clone(),
        external: klass.is_external(&env.library),
        truncated,
        derives_copy,
        kind: "struct",
        cfg_condition: get_gobject_cfg_condition(env, &klass.name),
        fields,
    }
}

pub fn from_union(env: &Env, union: &Union) -> Fields {
    let (fields, truncated) = analyze_fields(env, true, &union.fields);
    let derives_copy = truncated.is_none() && union.derives_copy(&env.library);
    Fields {
        name: union.c_type.as_ref().unwrap().clone(),
        external: union.is_external(&env.library),
        truncated,
        derives_copy,
        kind: "union",
        cfg_condition: None,
        fields,
    }
}

fn analyze_fields(
    env: &Env,
    unsafe_access: bool,
    fields: &[Field],
) -> (Vec<FieldInfo>, Option<String>) {
    let mut truncated = None;
    let mut infos = Vec::with_capacity(fields.len());

    let mut is_bitfield = false;
    for field in fields {
        // See IsIncomplete for &[Field].
        if is_bitfield && field.bits.is_some() {
            truncated = Some(format!("field {} has incomplete type", &field.name));
            break;
        }
        is_bitfield = field.bits.is_some();

        let typ = match field_ffi_type(env, field) {
            e @ Err(..) => {
                truncated = Some(e.into_string());
                break;
            }
            Ok(typ) => typ,
        };
        // Skip private fields from Debug impl. Ignore volatile as well,
        // they are usually used as synchronization primites,
        // so we wouldn't want to introduce additional reads.
        let debug = !field.private && !field.is_volatile() && field.implements_debug(&env.library);

        infos.push(FieldInfo {
            name: field.name.clone(),
            typ,
            debug,
            unsafe_access,
        });
    }

    (infos, truncated)
}

fn field_ffi_type(env: &Env, field: &Field) -> Result {
    if field.is_incomplete(&env.library) {
        return Err(TypeError::Ignored(format!(
            "field {} has incomplete type",
            &field.name
        )));
    }
    if let Some(ref c_type) = field.c_type {
        ffi_type(env, field.typ, c_type)
    } else if let Some(func) = env.library.type_(field.typ).maybe_ref_as::<Function>() {
        let (failure, signature) = function_signature(env, func, true);
        let signature = format!("Option<unsafe extern \"C\" fn{}>", signature);
        if failure {
            Err(TypeError::Unimplemented(signature))
        } else {
            Ok(signature)
        }
    } else {
        Err(TypeError::Ignored(format!(
            "field {} has empty c:type",
            &field.name
        )))
    }
}

fn get_gobject_cfg_condition(env: &Env, name: &str) -> Option<String> {
    let full_name = format!("{}.{}", env.namespaces.main().name, name);
    if let Some(obj) = env.config.objects.get(&full_name) {
        obj.cfg_condition.clone()
    } else {
        None
    }
}
<|endoftext|>


<commit_before>use gen::GenError;
use super::{Serializer, Serialized, Reset};

#[derive(Debug)]
pub struct Slice<'a> {
  value: &'a [u8],
  index: usize,
}

impl<'a> Slice<'a> {
  #[inline(always)]
  pub fn new(s: &'a [u8]) -> Slice<'a> {
    Slice {
      value: s,
      index: 0,
    }
  }
}

impl<'a> Serializer for Slice<'a> {
  #[inline(always)]
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    let output_len = output.len();
    let self_len = self.value.len() - self.index;
    if self_len <= output_len {
      (&mut output[..self_len]).copy_from_slice(&self.value[self.index..]);
      Ok((self_len, Serialized::Done))
    } else {
      output.copy_from_slice(&self.value[self.index..output_len]);
      self.index += output_len;
      Ok((output_len, Serialized::Continue))
    }
  }
}

impl<'a> Reset for Slice<'a> {
  fn reset(&mut self) -> bool {
    self.index = 0;
    true
  }
}

pub struct BigEndian<N> {
  pub num: N,
}

pub struct LittleEndian<N> {
  pub num: N,
}

pub trait Num: Copy {
  fn be(&self) -> BigEndian<Self> {
    BigEndian {
      num: *self,
    }
  }

  fn le(&self) -> LittleEndian<Self> {
    LittleEndian {
      num: *self,
    }
  }
}

impl Num for u8 {
}

impl Num for u16 {
}

impl Num for u32 {
}

impl Num for u64 {
}

impl Serializer for BigEndian<u8> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() == 0 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = self.num;
      Ok((1, Serialized::Done))
    }
  }
}

impl Serializer for BigEndian<u16> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 2 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num >>  8) & 0xff) as u8;
      output[1] = ((self.num      ) & 0xff) as u8;
      Ok((2, Serialized::Done))
    }
  }
}

impl Serializer for BigEndian<u32> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 4 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num >> 24) & 0xff) as u8;
      output[1] = ((self.num >> 16) & 0xff) as u8;
      output[2] = ((self.num >>  8) & 0xff) as u8;
      output[3] = ((self.num      ) & 0xff) as u8;
      Ok((4, Serialized::Done))
    }
  }
}

impl Serializer for BigEndian<u64> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 8 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num >> 56) & 0xff) as u8;
      output[1] = ((self.num >> 48) & 0xff) as u8;
      output[2] = ((self.num >> 40) & 0xff) as u8;
      output[3] = ((self.num >> 32) & 0xff) as u8;
      output[4] = ((self.num >> 24) & 0xff) as u8;
      output[5] = ((self.num >> 16) & 0xff) as u8;
      output[6] = ((self.num >>  8) & 0xff) as u8;
      output[7] = ((self.num      ) & 0xff) as u8;
      Ok((8, Serialized::Done))
    }
  }
}

impl Serializer for LittleEndian<u8> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() == 0 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = self.num;
      Ok((1, Serialized::Done))
    }
  }
}

impl Serializer for LittleEndian<u16> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 2 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num      ) & 0xff) as u8;
      output[1] = ((self.num >>  8) & 0xff) as u8;
      Ok((2, Serialized::Done))
    }
  }
}

impl Serializer for LittleEndian<u32> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 4 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num      ) & 0xff) as u8;
      output[1] = ((self.num >>  8) & 0xff) as u8;
      output[2] = ((self.num >> 16) & 0xff) as u8;
      output[3] = ((self.num >> 24) & 0xff) as u8;
      Ok((4, Serialized::Done))
    }
  }
}

impl Serializer for LittleEndian<u64> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 8 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num      ) & 0xff) as u8;
      output[1] = ((self.num >>  8) & 0xff) as u8;
      output[2] = ((self.num >> 16) & 0xff) as u8;
      output[3] = ((self.num >> 24) & 0xff) as u8;
      output[4] = ((self.num >> 32) & 0xff) as u8;
      output[5] = ((self.num >> 40) & 0xff) as u8;
      output[6] = ((self.num >> 48) & 0xff) as u8;
      output[7] = ((self.num >> 56) & 0xff) as u8;
      Ok((8, Serialized::Done))
    }
  }
}

impl<U> Reset for BigEndian<U> {
  fn reset(&mut self) -> bool {
    true
  }
}

impl<U> Reset for LittleEndian<U> {
  fn reset(&mut self) -> bool {
    true
  }
}

pub fn be<N: Num>(n: N) -> BigEndian<N> {
  n.be()
}

pub fn le<N: Num>(n: N) -> LittleEndian<N> {
  n.le()
}

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

  #[test]
  fn be_u8() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 0, 0, 0, 0, 0, 0, 0];
      assert_eq!(0x01u8.be().serialize(&mut mem), Ok((1, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn be_u16() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 2, 0, 0, 0, 0, 0, 0];
      assert_eq!(0x0102u16.be().serialize(&mut mem), Ok((2, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn be_u32() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 2, 3, 4, 0, 0, 0, 0];
      assert_eq!(0x01020304u32.be().serialize(&mut mem), Ok((4, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn be_u64() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 2, 3, 4, 5, 6, 7, 8];
      assert_eq!(0x0102030405060708u64.be().serialize(&mut mem), Ok((8, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn le_u8() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 0, 0, 0, 0, 0, 0, 0];
      assert_eq!(le(0x01u8).serialize(&mut mem), Ok((1, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn le_u16() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [2, 1, 0, 0, 0, 0, 0, 0];
      assert_eq!(le(0x0102u16).serialize(&mut mem), Ok((2, Serialized::Done)));
      assert_eq!(mem, expected);
  }
  #[test]
  fn le_u32() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [4, 3, 2, 1, 0, 0, 0, 0];
      assert_eq!(le(0x01020304u32).serialize(&mut mem), Ok((4, Serialized::Done)));
      assert_eq!(mem, expected);
  }
  #[test]
  fn le_u64() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [8, 7, 6, 5, 4, 3, 2, 1];
      assert_eq!(le(0x0102030405060708u64).serialize(&mut mem), Ok((8, Serialized::Done)));
      assert_eq!(mem, expected);
  }
}
<commit_msg>add a length value combinator<commit_after>use gen::GenError;
use super::{Serializer, Serialized, Reset};

#[derive(Debug)]
pub struct Slice<'a> {
  value: &'a [u8],
  index: usize,
}

impl<'a> Slice<'a> {
  #[inline(always)]
  pub fn new(s: &'a [u8]) -> Slice<'a> {
    Slice {
      value: s,
      index: 0,
    }
  }
}

impl<'a> Serializer for Slice<'a> {
  #[inline(always)]
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    let output_len = output.len();
    let self_len = self.value.len() - self.index;
    if self_len <= output_len {
      (&mut output[..self_len]).copy_from_slice(&self.value[self.index..]);
      Ok((self_len, Serialized::Done))
    } else {
      output.copy_from_slice(&self.value[self.index..output_len]);
      self.index += output_len;
      Ok((output_len, Serialized::Continue))
    }
  }
}

impl<'a> Reset for Slice<'a> {
  fn reset(&mut self) -> bool {
    self.index = 0;
    true
  }
}

pub struct LengthValue<A, B> {
  skip: usize,
  length: fn(usize) -> A,
  value: B,
}

impl<A:Serializer, B:Serializer> LengthValue<A, B> {
  #[inline(always)]
  pub fn new<U>(skip: U, length: fn(usize) -> A, value: B) -> Self
    where usize: From<U> {
    LengthValue {
      skip: usize::from(skip),
      length,
      value,
    }
  }
}

impl<A:Serializer, B:Serializer> Serializer for LengthValue<A,B> {
  #[inline(always)]
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    match self.value.serialize(&mut output[self.skip..])? {
      // we need to write the full length/value structure at once
      (_, Serialized::Continue) => Ok((0, Serialized::Continue)),
      (i, Serialized::Done) => {
        match (self.length)(i).serialize(&mut output[..self.skip])? {
          (_, Serialized::Continue) => Err(GenError::InvalidOffset),
          (_, Serialized::Done) => Ok((self.skip+i, Serialized::Done))
        }
      }
    }
  }
}

pub fn length_value<A:Serializer, B:Serializer>(skip: usize, length: fn(usize) -> A, value: B) -> LengthValue<A, B> {
  LengthValue::new(skip, length, value)
}

pub struct BigEndian<N> {
  pub num: N,
}

pub struct LittleEndian<N> {
  pub num: N,
}

pub trait Num: Copy {
  fn be(&self) -> BigEndian<Self> {
    BigEndian {
      num: *self,
    }
  }

  fn le(&self) -> LittleEndian<Self> {
    LittleEndian {
      num: *self,
    }
  }
}

impl Num for u8 {
}

impl Num for u16 {
}

impl Num for u32 {
}

impl Num for u64 {
}

impl Serializer for BigEndian<u8> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() == 0 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = self.num;
      Ok((1, Serialized::Done))
    }
  }
}

impl Serializer for BigEndian<u16> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 2 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num >>  8) & 0xff) as u8;
      output[1] = ((self.num      ) & 0xff) as u8;
      Ok((2, Serialized::Done))
    }
  }
}

impl Serializer for BigEndian<u32> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 4 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num >> 24) & 0xff) as u8;
      output[1] = ((self.num >> 16) & 0xff) as u8;
      output[2] = ((self.num >>  8) & 0xff) as u8;
      output[3] = ((self.num      ) & 0xff) as u8;
      Ok((4, Serialized::Done))
    }
  }
}

impl Serializer for BigEndian<u64> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 8 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num >> 56) & 0xff) as u8;
      output[1] = ((self.num >> 48) & 0xff) as u8;
      output[2] = ((self.num >> 40) & 0xff) as u8;
      output[3] = ((self.num >> 32) & 0xff) as u8;
      output[4] = ((self.num >> 24) & 0xff) as u8;
      output[5] = ((self.num >> 16) & 0xff) as u8;
      output[6] = ((self.num >>  8) & 0xff) as u8;
      output[7] = ((self.num      ) & 0xff) as u8;
      Ok((8, Serialized::Done))
    }
  }
}

impl Serializer for LittleEndian<u8> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() == 0 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = self.num;
      Ok((1, Serialized::Done))
    }
  }
}

impl Serializer for LittleEndian<u16> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 2 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num      ) & 0xff) as u8;
      output[1] = ((self.num >>  8) & 0xff) as u8;
      Ok((2, Serialized::Done))
    }
  }
}

impl Serializer for LittleEndian<u32> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 4 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num      ) & 0xff) as u8;
      output[1] = ((self.num >>  8) & 0xff) as u8;
      output[2] = ((self.num >> 16) & 0xff) as u8;
      output[3] = ((self.num >> 24) & 0xff) as u8;
      Ok((4, Serialized::Done))
    }
  }
}

impl Serializer for LittleEndian<u64> {
  fn serialize<'b, 'c>(&'b mut self, output: &'c mut [u8]) -> Result<(usize, Serialized), GenError> {
    if output.len() < 8 {
      Ok((0, Serialized::Continue))
    } else {
      output[0] = ((self.num      ) & 0xff) as u8;
      output[1] = ((self.num >>  8) & 0xff) as u8;
      output[2] = ((self.num >> 16) & 0xff) as u8;
      output[3] = ((self.num >> 24) & 0xff) as u8;
      output[4] = ((self.num >> 32) & 0xff) as u8;
      output[5] = ((self.num >> 40) & 0xff) as u8;
      output[6] = ((self.num >> 48) & 0xff) as u8;
      output[7] = ((self.num >> 56) & 0xff) as u8;
      Ok((8, Serialized::Done))
    }
  }
}

impl<U> Reset for BigEndian<U> {
  fn reset(&mut self) -> bool {
    true
  }
}

impl<U> Reset for LittleEndian<U> {
  fn reset(&mut self) -> bool {
    true
  }
}

pub fn be<N: Num>(n: N) -> BigEndian<N> {
  n.be()
}

pub fn le<N: Num>(n: N) -> LittleEndian<N> {
  n.le()
}

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

  #[test]
  fn be_u8() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 0, 0, 0, 0, 0, 0, 0];
      assert_eq!(0x01u8.be().serialize(&mut mem), Ok((1, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn be_u16() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 2, 0, 0, 0, 0, 0, 0];
      assert_eq!(0x0102u16.be().serialize(&mut mem), Ok((2, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn be_u32() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 2, 3, 4, 0, 0, 0, 0];
      assert_eq!(0x01020304u32.be().serialize(&mut mem), Ok((4, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn be_u64() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 2, 3, 4, 5, 6, 7, 8];
      assert_eq!(0x0102030405060708u64.be().serialize(&mut mem), Ok((8, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn le_u8() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [1, 0, 0, 0, 0, 0, 0, 0];
      assert_eq!(le(0x01u8).serialize(&mut mem), Ok((1, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn le_u16() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [2, 1, 0, 0, 0, 0, 0, 0];
      assert_eq!(le(0x0102u16).serialize(&mut mem), Ok((2, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn le_u32() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [4, 3, 2, 1, 0, 0, 0, 0];
      assert_eq!(le(0x01020304u32).serialize(&mut mem), Ok((4, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn le_u64() {
      let mut mem : [u8; 8] = [0; 8];
      let expected = [8, 7, 6, 5, 4, 3, 2, 1];
      assert_eq!(le(0x0102030405060708u64).serialize(&mut mem), Ok((8, Serialized::Done)));
      assert_eq!(mem, expected);
  }

  #[test]
  fn length_value_test() {
      let mut mem : [u8; 100] = [0; 100];
      let expected = b"\x00\x05hello";

      let mut sr = length_value(2, |nb| (nb as u16).be(), "hello".raw());

      assert_eq!(sr.serialize(&mut mem), Ok((7, Serialized::Done)));
      assert_eq!(&mem[..7], expected);
  }
}
<|endoftext|>