<commit_before>#![stable(feature = "metadata_ext", since = "1.1.0")]

use crate::fs::Metadata;
use crate::sys_common::AsInner;

#[allow(deprecated)]
use crate::os::linux::raw;

/// OS-specific extensions to [`fs::Metadata`].
///
/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
#[stable(feature = "metadata_ext", since = "1.1.0")]
pub trait MetadataExt {
    /// Gain a reference to the underlying `stat` structure which contains
    /// the raw information returned by the OS.
    ///
    /// The contents of the returned [`stat`] are **not** consistent across
    /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the
    /// cross-Unix abstractions contained within the raw stat.
    ///
    /// [`stat`]: ../../../../std/os/linux/raw/struct.stat.html
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     let stat = meta.as_raw_stat();
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext", since = "1.1.0")]
    #[rustc_deprecated(since = "1.8.0",
                       reason = "deprecated in favor of the accessor \
                                 methods of this trait")]
    #[allow(deprecated)]
    fn as_raw_stat(&self) -> &raw::stat;

    /// Returns the device ID on which this file resides.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_dev());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_dev(&self) -> u64;
    /// Returns the inode number.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_ino());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_ino(&self) -> u64;
    /// Returns the file type and mode.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_mode());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_mode(&self) -> u32;
    /// Returns the number of hard links to file.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_nlink());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_nlink(&self) -> u64;
    /// Returns the user ID of the file owner.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_uid());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_uid(&self) -> u32;
    /// Returns the group ID of the file owner.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_gid());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_gid(&self) -> u32;
    /// Returns the device ID that this file represents. Only relevant for special file.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_rdev());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_rdev(&self) -> u64;
    /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes.
    ///
    /// The size of a symbolic link is the length of the pathname it contains,
    /// without a terminating null byte.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_size());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_size(&self) -> u64;
    /// Returns the last access time of the file, in seconds since Unix Epoch.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_atime());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_atime(&self) -> i64;
    /// Returns the last access time of the file, in nanoseconds since [`st_atime`].
    ///
    /// [`st_atime`]: #tymethod.st_atime
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_atime_nsec());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_atime_nsec(&self) -> i64;
    /// Returns the last modification time of the file, in seconds since Unix Epoch.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_mtime());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_mtime(&self) -> i64;
    /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`].
    ///
    /// [`st_mtime`]: #tymethod.st_mtime
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_mtime_nsec());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_mtime_nsec(&self) -> i64;
    /// Returns the last status change time of the file, in seconds since Unix Epoch.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_ctime());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_ctime(&self) -> i64;
    /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`].
    ///
    /// [`st_ctime`]: #tymethod.st_ctime
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_ctime_nsec());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_ctime_nsec(&self) -> i64;
    /// Returns the "preferred" blocksize for efficient filesystem I/O.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_blksize());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_blksize(&self) -> u64;
    /// Returns the number of blocks allocated to the file, 512-byte units.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_blocks());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_blocks(&self) -> u64;
}

#[stable(feature = "metadata_ext", since = "1.1.0")]
impl MetadataExt for Metadata {
    #[allow(deprecated)]
    fn as_raw_stat(&self) -> &raw::stat {
        unsafe {
            &*(self.as_inner().as_inner() as *const libc::stat64
                                          as *const raw::stat)
        }
    }
    fn st_dev(&self) -> u64 {
        self.as_inner().as_inner().st_dev as u64
    }
    fn st_ino(&self) -> u64 {
        self.as_inner().as_inner().st_ino as u64
    }
    fn st_mode(&self) -> u32 {
        self.as_inner().as_inner().st_mode as u32
    }
    fn st_nlink(&self) -> u64 {
        self.as_inner().as_inner().st_nlink as u64
    }
    fn st_uid(&self) -> u32 {
        self.as_inner().as_inner().st_uid as u32
    }
    fn st_gid(&self) -> u32 {
        self.as_inner().as_inner().st_gid as u32
    }
    fn st_rdev(&self) -> u64 {
        self.as_inner().as_inner().st_rdev as u64
    }
    fn st_size(&self) -> u64 {
        self.as_inner().as_inner().st_size as u64
    }
    fn st_atime(&self) -> i64 {
        self.as_inner().as_inner().st_atime as i64
    }
    fn st_atime_nsec(&self) -> i64 {
        self.as_inner().as_inner().st_atime_nsec as i64
    }
    fn st_mtime(&self) -> i64 {
        self.as_inner().as_inner().st_mtime as i64
    }
    fn st_mtime_nsec(&self) -> i64 {
        self.as_inner().as_inner().st_mtime_nsec as i64
    }
    fn st_ctime(&self) -> i64 {
        self.as_inner().as_inner().st_ctime as i64
    }
    fn st_ctime_nsec(&self) -> i64 {
        self.as_inner().as_inner().st_ctime_nsec as i64
    }
    fn st_blksize(&self) -> u64 {
        self.as_inner().as_inner().st_blksize as u64
    }
    fn st_blocks(&self) -> u64 {
        self.as_inner().as_inner().st_blocks as u64
    }
}
<commit_msg>Rollup merge of #63327 - lzutao:fix-as_raw_stat-dep-msg, r=Mark-Simulacrum<commit_after>#![stable(feature = "metadata_ext", since = "1.1.0")]

use crate::fs::Metadata;
use crate::sys_common::AsInner;

#[allow(deprecated)]
use crate::os::linux::raw;

/// OS-specific extensions to [`fs::Metadata`].
///
/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
#[stable(feature = "metadata_ext", since = "1.1.0")]
pub trait MetadataExt {
    /// Gain a reference to the underlying `stat` structure which contains
    /// the raw information returned by the OS.
    ///
    /// The contents of the returned [`stat`] are **not** consistent across
    /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the
    /// cross-Unix abstractions contained within the raw stat.
    ///
    /// [`stat`]: ../../../../std/os/linux/raw/struct.stat.html
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     let stat = meta.as_raw_stat();
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext", since = "1.1.0")]
    #[rustc_deprecated(
        since = "1.8.0",
        reason = "other methods of this trait are now prefered"
    )]
    #[allow(deprecated)]
    fn as_raw_stat(&self) -> &raw::stat;

    /// Returns the device ID on which this file resides.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_dev());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_dev(&self) -> u64;
    /// Returns the inode number.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_ino());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_ino(&self) -> u64;
    /// Returns the file type and mode.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_mode());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_mode(&self) -> u32;
    /// Returns the number of hard links to file.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_nlink());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_nlink(&self) -> u64;
    /// Returns the user ID of the file owner.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_uid());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_uid(&self) -> u32;
    /// Returns the group ID of the file owner.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_gid());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_gid(&self) -> u32;
    /// Returns the device ID that this file represents. Only relevant for special file.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_rdev());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_rdev(&self) -> u64;
    /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes.
    ///
    /// The size of a symbolic link is the length of the pathname it contains,
    /// without a terminating null byte.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_size());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_size(&self) -> u64;
    /// Returns the last access time of the file, in seconds since Unix Epoch.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_atime());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_atime(&self) -> i64;
    /// Returns the last access time of the file, in nanoseconds since [`st_atime`].
    ///
    /// [`st_atime`]: #tymethod.st_atime
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_atime_nsec());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_atime_nsec(&self) -> i64;
    /// Returns the last modification time of the file, in seconds since Unix Epoch.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_mtime());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_mtime(&self) -> i64;
    /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`].
    ///
    /// [`st_mtime`]: #tymethod.st_mtime
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_mtime_nsec());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_mtime_nsec(&self) -> i64;
    /// Returns the last status change time of the file, in seconds since Unix Epoch.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_ctime());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_ctime(&self) -> i64;
    /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`].
    ///
    /// [`st_ctime`]: #tymethod.st_ctime
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_ctime_nsec());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_ctime_nsec(&self) -> i64;
    /// Returns the "preferred" blocksize for efficient filesystem I/O.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_blksize());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_blksize(&self) -> u64;
    /// Returns the number of blocks allocated to the file, 512-byte units.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs;
    /// use std::io;
    /// use std::os::linux::fs::MetadataExt;
    ///
    /// fn main() -> io::Result<()> {
    ///     let meta = fs::metadata("some_file")?;
    ///     println!("{}", meta.st_blocks());
    ///     Ok(())
    /// }
    /// ```
    #[stable(feature = "metadata_ext2", since = "1.8.0")]
    fn st_blocks(&self) -> u64;
}

#[stable(feature = "metadata_ext", since = "1.1.0")]
impl MetadataExt for Metadata {
    #[allow(deprecated)]
    fn as_raw_stat(&self) -> &raw::stat {
        unsafe {
            &*(self.as_inner().as_inner() as *const libc::stat64
                                          as *const raw::stat)
        }
    }
    fn st_dev(&self) -> u64 {
        self.as_inner().as_inner().st_dev as u64
    }
    fn st_ino(&self) -> u64 {
        self.as_inner().as_inner().st_ino as u64
    }
    fn st_mode(&self) -> u32 {
        self.as_inner().as_inner().st_mode as u32
    }
    fn st_nlink(&self) -> u64 {
        self.as_inner().as_inner().st_nlink as u64
    }
    fn st_uid(&self) -> u32 {
        self.as_inner().as_inner().st_uid as u32
    }
    fn st_gid(&self) -> u32 {
        self.as_inner().as_inner().st_gid as u32
    }
    fn st_rdev(&self) -> u64 {
        self.as_inner().as_inner().st_rdev as u64
    }
    fn st_size(&self) -> u64 {
        self.as_inner().as_inner().st_size as u64
    }
    fn st_atime(&self) -> i64 {
        self.as_inner().as_inner().st_atime as i64
    }
    fn st_atime_nsec(&self) -> i64 {
        self.as_inner().as_inner().st_atime_nsec as i64
    }
    fn st_mtime(&self) -> i64 {
        self.as_inner().as_inner().st_mtime as i64
    }
    fn st_mtime_nsec(&self) -> i64 {
        self.as_inner().as_inner().st_mtime_nsec as i64
    }
    fn st_ctime(&self) -> i64 {
        self.as_inner().as_inner().st_ctime as i64
    }
    fn st_ctime_nsec(&self) -> i64 {
        self.as_inner().as_inner().st_ctime_nsec as i64
    }
    fn st_blksize(&self) -> u64 {
        self.as_inner().as_inner().st_blksize as u64
    }
    fn st_blocks(&self) -> u64 {
        self.as_inner().as_inner().st_blocks as u64
    }
}
<|endoftext|>


<commit_before>//! Processor
//!
//! ## Terminololgy
//! * Evaluator - a code fragment to evaluate an expression
//! * Processor - A list of evaluators
//!
//! ## Phases for Generation
//!

use std::convert::From;
use std::rc::Rc;

use common::err::{Error, Result, Void, void_ok};
use common::func::{
	NoArgFn,
	UnaryFn,
	BinaryFn,
	TrinityFn
};
use common::plugin::{
	FuncRegistry,
	TypeRegistry
};
use common::rows::{
	MiniPage,
  FMiniPage,
	Page,
	OwnedPageBuilder,
	PageId
};
use common::session::Session;
use common::types::{Ty, name};

use jit::{JitCompiler};
use jit::builder::{Builder, CastOp};
use jit::types::{self, LLVMTy};
use jit::value::{Function, Predicate, Value, ValueRef, ToValue};
use plan::expr::*;
use plan::expr::visitor::{accept_by_default, Visitor};

use driver::DriverContext;
use super::NamedSchema;

#[repr(C)]
pub struct MiniPage2 {
  ptr: *const u8,
  size: usize
}

pub type MapFunc = extern "C" fn(&mut MiniPage2) -> i32;

macro_rules! unary_codegen (
  ($name:ident, $func:ident) => (
    pub fn $name(&mut self, expr: &Expr)
    {
      let val = self.visit(expr);
      let builder = &self.builder;

      self.stack.push(builder.$func(&val));
    }
  );
);

macro_rules! bin_codegen (
  ($name:ident, $func:ident) => (
    pub fn $name(&mut self, lhs: &Expr, rhs: &Expr)
    {
      println!("before lhs");
      let lhs_val = self.visit(lhs);
      println!("before rhs");
      let rhs_val = self.visit(rhs);
      let builder = &self.builder;

      self.stack.push(builder.$func(&lhs_val, &rhs_val));
    }
  );
);

fn to_llvm_ty<'a>(jit: &'a JitCompiler, ty: &Ty) -> &'a types::Ty
{
  match *ty {
    Ty::Bool => jit.get_void_ty(),
    Ty::I8   => jit.get_void_ty(),
    Ty::I16  => jit.get_void_ty(),
    Ty::I32  => jit.get_void_ty(),
    Ty::I64  => jit.get_void_ty(),
    Ty::F32  => jit.get_void_ty(),
    Ty::F64  => jit.get_void_ty(),
    _        => panic!("not supported type: {}", ty.base())
  }
}

pub struct MapCompiler<'a>
{
  jit  : &'a JitCompiler,
  types: &'a Vec<Ty>,
	names: &'a Vec<&'a str>,
	stack: Vec<Value>,
  error: Option<Error>,
  func: Function,
  builder: Builder
}

impl<'a> MapCompiler<'a> {
  fn new(jit: &'a JitCompiler,
         fn_registry: &FuncRegistry,
		     session: &Session,
		     schema : &'a NamedSchema,) -> MapCompiler<'a> {
    let builder = jit.new_builder();
    let func = MapCompiler::generate_fn_prototype(jit, &builder);

		MapCompiler {
      jit   : jit,
			types : &schema.types,
			names : &schema.names,
			stack : Vec::new(),
			error : None,
      func: func,
      builder: builder
		}
	}

  pub fn compile(
            jit: &'a JitCompiler,
						fn_registry: &FuncRegistry,
		        session    : &'a Session,
					  schema     : &'a NamedSchema,
					  expr       : &Expr) -> Result<Rc<MapFunc>>
	{
    let mut map = MapCompiler::new(jit, fn_registry, session, schema);
    println!("before accept");
    map.accept(expr);
    println!("after accept");
    MapCompiler::generate_scalar_func(&mut map, jit);
    println!("after generate_scalar_func");
    map.ret_func(jit)
  }

  fn generate_fn_prototype(jit: &JitCompiler, bld: &Builder) -> Function {
    let minipage_ty = jit.get_ty("struct.MiniPage").unwrap();
    let minipage_ptr_ty = jit.get_pointer_ty(&minipage_ty);
    jit.create_func_prototype(
      "processor",
      &i32::llvm_ty(jit.context()),
      &[&minipage_ptr_ty],
      Some(bld))
  }

  fn generate_scalar_func(map: &mut MapCompiler, jit: &JitCompiler) {
    let func = &map.func;
    let builder = &map.builder;

    let codegen = map.stack.pop().unwrap();
    let minipage: Value = func.arg(0).into();

    let call = match jit.get_func("test") {
      Some(f) => {
        builder.create_call(&f, &[&minipage, &codegen])
      },
      _       => panic!("No such a function")
    };
    builder.create_ret(&call);
  }

  fn ret_func(&self, jit: &JitCompiler) -> Result<Rc<MapFunc>>
  {
    // dump code
    jit.dump();

    match jit.verify() {
      Ok(_) => {
        let func_ptr = unsafe { jit.get_func_ptr(&self.func).unwrap() };
        let func: MapFunc = unsafe {::std::mem::transmute(func_ptr)};
        Ok(Rc::new(func))
      }
      Err(msg) => {
        error!("{}", msg);
        Err(Error::FunctionCorrupted)
      }
    }
  }

  #[inline(always)]
  fn visit(&mut self, expr: &Expr) -> Value
  {
    self.accept(expr);
    // Stack must contain at least one Value item.
    // Otherwise, it is definitely a bug.
    self.stack.pop().unwrap()
  }

  #[inline(always)]
  fn push_value<T: ToValue>(&mut self, val: &T)
  {
    let v = val.to_value(self.jit.context());
    self.stack.push(v);
  }

  unary_codegen!(Not, create_not);

	pub fn IsNull(&self, c: &Expr)
	{
	}

	pub fn IsNotNull(&self, e: &Expr)
	{
	}

	pub fn PlusSign(&mut self, e: &Expr)
	{
    self.accept(e);
	}

  unary_codegen!(MinusSign, create_neg);

	pub fn Cast(&mut self, e: &Expr, from: &Ty, to: &Ty)
	{
    // if the both types are equivalent, just return.
    if from == to { return; }

    let cast_op = if from.is_sint() && to.is_sint() {
      if from.size_of() < to.size_of() {
        CastOp::SExt
      } else {
        CastOp::Trunc
      }
    } else if from.is_sint() && to.is_float() {
      CastOp::SIToFP
    } else if from.is_uint() && to.is_float() {
      CastOp::UIToFP
    } else if from.is_float() && to.is_sint() {
      CastOp::FPToSI
    } else if from.is_float() && to.is_uint() {
      CastOp::FPToUI
    } else if from.is_float() && to.is_float() {
      if from.size_of() < to.size_of() {
        CastOp::FPExt
      } else {
        CastOp::FPTrunc
      }
    } else {
      panic!("not supported cast from {} to {}", from.base(), to.base());
    };

    let val = self.visit(e);
    let builder = &self.builder;

    self.stack.push(builder.create_cast(cast_op, &val, to_llvm_ty(self.jit, to)));
	}

  bin_codegen!(And, create_and);
  bin_codegen!(Or,  create_or);
  bin_codegen!(Xor, create_xor);

	pub fn Cmp(&mut self, op: &CmpOp, lhs: &Expr, rhs: &Expr)
	{
    let lhs_val = self.visit(lhs);
    let rhs_val = self.visit(rhs);

    let builder = &self.builder;

    let pred = match *op {
      CmpOp::Eq => Predicate::Eq,
      CmpOp::Ne => Predicate::Ne,
      CmpOp::Lt => Predicate::Lt,
      CmpOp::Le => Predicate::Le,
      CmpOp::Gt => Predicate::Gt,
      CmpOp::Ge => Predicate::Ge
    };

    self.stack.push(builder.create_cmp(&lhs_val, &rhs_val, pred));
	}

	pub fn Arithm(&mut self, ty: &Ty, op: &ArithmOp, lhs: &Expr, rhs: &Expr)
	{
    let lhs_val = self.visit(lhs);
    let rhs_val = self.visit(rhs);

    let builder = &self.builder;

    self.stack.push(match *op {
      ArithmOp::Plus => {builder.create_add(&lhs_val, &rhs_val)}
      ArithmOp::Sub  => {builder.create_sub(&lhs_val, &rhs_val)}
      ArithmOp::Mul  => {builder.create_mul(&lhs_val, &rhs_val)}
      ArithmOp::Div  => {builder.create_div(&lhs_val, &rhs_val)}
      ArithmOp::Rem  => {builder.create_rem(&lhs_val, &rhs_val)}
    });
	}

	pub fn Func(&self, f: &FnDecl, args: &Vec<Box<Expr>>)
	{
	}

  pub fn Const(&mut self, l: &Literal)
  {
    match *l {
      Literal::U8(ref v)  => self.push_value(v),
      Literal::I8(ref v)  => self.push_value(v),
      Literal::I16(ref v) => self.push_value(v),
      Literal::I32(ref v) => self.push_value(v),
      Literal::I64(ref v) => self.push_value(v),
      Literal::F32(ref v) => self.push_value(v),
      Literal::F64(ref v) => self.push_value(v),
      _                    => unimplemented!()
    }
  }

	pub fn Field(&mut self, ty: &Ty, name: &str)
	{
		/*
    let found: Option<(usize, &Ty, &&str)>;

		found = izip!(0 .. self.types.len(), self.types, self.names)
						.find(|&(i, t, n)| *n == name);

    let eval = match found {
    	Some(f) => FieldEvaluator {idx: f.0, ty: f.1.clone()},
    	None    => panic!("no such field for {}", name)
    };*/
	}
}

impl<'a> visitor::Visitor for MapCompiler<'a>
{
	fn accept(&mut self, e: &Expr)
	{
	  match *e.kind() {
	    ExprKind::Not      (ref c)               => self.Not(c),
	    ExprKind::IsNull   (ref c)               => self.IsNull(c),
	    ExprKind::IsNotNull(ref c)               => self.IsNotNull(c),
	    ExprKind::PlusSign (ref c)               => self.PlusSign(c),
	    ExprKind::MinusSign(ref c)               => self.MinusSign(c),
	    ExprKind::Cast     (ref c, ref f, ref t) => self.Cast(c, f, t),

	    ExprKind::And      (ref l, ref r)        => self.And(l, r),
	    ExprKind::Or       (ref l, ref r)        => self.Or(l, r),
	    ExprKind::Cmp      (ref o, ref l, ref r) => self.Cmp(o, l, r),
	    ExprKind::Arithm   (ref o, ref l, ref r) => self.Arithm(e.ty(), o, l, r),

	    ExprKind::Fn     (ref f, ref args)  => self.Func(f, args),
			ExprKind::Field  (ref name)         => self.Field(e.ty(), name),
	    ExprKind::Const  (ref lit)          => self.Const(lit),
	    /*
	    ExprKind::Switch(ref cases, ref default) => {
	    	for c in cases.iter() {
	    		v.accept(c);
	   	  }

	    	v.accept(default);
	    },

	    ExprKind::Case   (ref l, ref r) => { v.accept(l); v.accept(r) },*/
	    _ => panic!("")
	  }
	}
}

pub trait Processor
{
  fn process(
    &self,
    input: &Page,
    builder: &mut OwnedPageBuilder) -> Void;
}

#[cfg(test)]
mod tests {
	use common::rows::{
		Page,
		OwnedPageBuilder,
		PageId
	};
	use common::plugin::*;
	use common::session::Session;
	use common::storage::{RandomTable, MemTable};
	use common::types::*;

  use jit::JitCompiler;

	use plan::expr::*;
	use driver::DriverContext;

	use super::super::NamedSchema;

	use super::*;

  #[test]
  pub fn codegen() {
    let plugin_mgr = PluginManager::new();
    let jit = JitCompiler::new_from_bc("../common/target/ir/common.bc").ok().unwrap();
    assert!(jit.get_ty("struct.MiniPage").is_some());
    assert!(jit.get_ty("struct.Page").is_some());

    let types: Vec<Ty> = schema!(
	    I64, // l_orderkey      bigint
	    I64, // l_partkey       bigint
	    I64, // l_suppkey       bigint
	    I32, // l_linenumber    int
	    F64, // l_quantity      double,
	    F64, // l_extendedprice double,
	    F64, // l_discount      double,
	    F64  // l_tax           double,
	    // string types are not implmeneted yet.
    );

	  let names: Vec<&str> = vec![
	  	"l_orderkey",
	  	"l_partkey",
	  	"l_suppkey",
	  	"l_linenumber",
	  	"l_quantity",
	  	"l_extendedprice",
	  	"l_discount",
	  	"l_tax"
	  ];

    let expr = Plus(I32, Const(19800401i32), Const(1i32));
    //let expr = Or(Const(4i32), Const(2i32));
    //let expr = Not(Const(0i32));
    //let expr = MinusSign(Const(7i32));
    let schema  = NamedSchema::new(&names, &types);
  	let session = Session;

    let map = MapCompiler::compile(&jit,
                                   plugin_mgr.fn_registry(),
																	 &session,
																	 &schema,
																	 &expr).ok().unwrap();

    let mut minipage = MiniPage2 {
      ptr: ::std::ptr::null(),
      size: 0
    };
    assert_eq!(map(&mut minipage), 19800401i32);
  }
}<commit_msg>Rename MapCompiler to ExprCompiler.<commit_after>//! Processor
//!
//! ## Terminololgy
//! * Evaluator - a code fragment to evaluate an expression
//! * Processor - A list of evaluators
//!
//! ## Phases for Generation
//!

use std::convert::From;
use std::rc::Rc;

use common::err::{Error, Result, Void, void_ok};
use common::func::{
	NoArgFn,
	UnaryFn,
	BinaryFn,
	TrinityFn
};
use common::plugin::{
	FuncRegistry,
	TypeRegistry
};
use common::rows::{
	MiniPage,
  FMiniPage,
	Page,
	OwnedPageBuilder,
	PageId
};
use common::session::Session;
use common::types::{Ty, name};

use jit::{JitCompiler};
use jit::builder::{Builder, CastOp};
use jit::types::{self, LLVMTy};
use jit::value::{Function, Predicate, Value, ValueRef, ToValue};
use plan::expr::*;
use plan::expr::visitor::{accept_by_default, Visitor};

use driver::DriverContext;
use super::NamedSchema;

#[repr(C)]
pub struct MiniPage2 {
  ptr: *const u8,
  size: usize
}

pub type MapFunc = extern "C" fn(&mut MiniPage2) -> i32;

macro_rules! unary_codegen (
  ($name:ident, $func:ident) => (
    pub fn $name(&mut self, expr: &Expr)
    {
      let val = self.visit(expr);
      let builder = &self.builder;

      self.stack.push(builder.$func(&val));
    }
  );
);

macro_rules! bin_codegen (
  ($name:ident, $func:ident) => (
    pub fn $name(&mut self, lhs: &Expr, rhs: &Expr)
    {
      println!("before lhs");
      let lhs_val = self.visit(lhs);
      println!("before rhs");
      let rhs_val = self.visit(rhs);
      let builder = &self.builder;

      self.stack.push(builder.$func(&lhs_val, &rhs_val));
    }
  );
);

fn to_llvm_ty<'a>(jit: &'a JitCompiler, ty: &Ty) -> &'a types::Ty
{
  match *ty {
    Ty::Bool => jit.get_void_ty(),
    Ty::I8   => jit.get_void_ty(),
    Ty::I16  => jit.get_void_ty(),
    Ty::I32  => jit.get_void_ty(),
    Ty::I64  => jit.get_void_ty(),
    Ty::F32  => jit.get_void_ty(),
    Ty::F64  => jit.get_void_ty(),
    _        => panic!("not supported type: {}", ty.base())
  }
}

pub struct ExprCompiler<'a>
{
  jit  : &'a JitCompiler,
  types: &'a Vec<Ty>,
	names: &'a Vec<&'a str>,
	stack: Vec<Value>,
  error: Option<Error>,
  func: Function,
  builder: Builder
}

impl<'a> ExprCompiler<'a> {
  fn new(jit: &'a JitCompiler,
         fn_registry: &FuncRegistry,
		     session: &Session,
		     schema : &'a NamedSchema,) -> ExprCompiler<'a> {
    let builder = jit.new_builder();
    let func = ExprCompiler::generate_fn_prototype(jit, &builder);

		ExprCompiler {
      jit   : jit,
			types : &schema.types,
			names : &schema.names,
			stack : Vec::new(),
			error : None,
      func: func,
      builder: builder
		}
	}

  pub fn compile(
            jit: &'a JitCompiler,
						fn_registry: &FuncRegistry,
		        session    : &'a Session,
					  schema     : &'a NamedSchema,
					  expr       : &Expr) -> Result<Rc<MapFunc>>
	{
    let mut map = ExprCompiler::new(jit, fn_registry, session, schema);
    println!("before accept");
    map.accept(expr);
    println!("after accept");
    ExprCompiler::generate_scalar_func(&mut map, jit);
    println!("after generate_scalar_func");
    map.ret_func(jit)
  }

  fn generate_fn_prototype(jit: &JitCompiler, bld: &Builder) -> Function {
    let minipage_ty = jit.get_ty("struct.MiniPage").unwrap();
    let minipage_ptr_ty = jit.get_pointer_ty(&minipage_ty);
    jit.create_func_prototype(
      "processor",
      &i32::llvm_ty(jit.context()),
      &[&minipage_ptr_ty],
      Some(bld))
  }

  fn generate_scalar_func(map: &mut ExprCompiler, jit: &JitCompiler) {
    let func = &map.func;
    let builder = &map.builder;

    let codegen = map.stack.pop().unwrap();
    let minipage: Value = func.arg(0).into();

    let call = match jit.get_func("test") {
      Some(f) => {
        builder.create_call(&f, &[&minipage, &codegen])
      },
      _       => panic!("No such a function")
    };
    builder.create_ret(&call);
  }

  fn ret_func(&self, jit: &JitCompiler) -> Result<Rc<MapFunc>>
  {
    // dump code
    jit.dump();

    match jit.verify() {
      Ok(_) => {
        let func_ptr = unsafe { jit.get_func_ptr(&self.func).unwrap() };
        let func: MapFunc = unsafe {::std::mem::transmute(func_ptr)};
        Ok(Rc::new(func))
      }
      Err(msg) => {
        error!("{}", msg);
        Err(Error::FunctionCorrupted)
      }
    }
  }

  #[inline(always)]
  fn visit(&mut self, expr: &Expr) -> Value
  {
    self.accept(expr);
    // Stack must contain at least one Value item.
    // Otherwise, it is definitely a bug.
    self.stack.pop().unwrap()
  }

  #[inline(always)]
  fn push_value<T: ToValue>(&mut self, val: &T)
  {
    let v = val.to_value(self.jit.context());
    self.stack.push(v);
  }

  unary_codegen!(Not, create_not);

	pub fn IsNull(&self, c: &Expr)
	{
	}

	pub fn IsNotNull(&self, e: &Expr)
	{
	}

	pub fn PlusSign(&mut self, e: &Expr)
	{
    self.accept(e);
	}

  unary_codegen!(MinusSign, create_neg);

	pub fn Cast(&mut self, e: &Expr, from: &Ty, to: &Ty)
	{
    // if the both types are equivalent, just return.
    if from == to { return; }

    let cast_op = if from.is_sint() && to.is_sint() {
      if from.size_of() < to.size_of() {
        CastOp::SExt
      } else {
        CastOp::Trunc
      }
    } else if from.is_sint() && to.is_float() {
      CastOp::SIToFP
    } else if from.is_uint() && to.is_float() {
      CastOp::UIToFP
    } else if from.is_float() && to.is_sint() {
      CastOp::FPToSI
    } else if from.is_float() && to.is_uint() {
      CastOp::FPToUI
    } else if from.is_float() && to.is_float() {
      if from.size_of() < to.size_of() {
        CastOp::FPExt
      } else {
        CastOp::FPTrunc
      }
    } else {
      panic!("not supported cast from {} to {}", from.base(), to.base());
    };

    let val = self.visit(e);
    let builder = &self.builder;

    self.stack.push(builder.create_cast(cast_op, &val, to_llvm_ty(self.jit, to)));
	}

  bin_codegen!(And, create_and);
  bin_codegen!(Or,  create_or);
  bin_codegen!(Xor, create_xor);

	pub fn Cmp(&mut self, op: &CmpOp, lhs: &Expr, rhs: &Expr)
	{
    let lhs_val = self.visit(lhs);
    let rhs_val = self.visit(rhs);

    let builder = &self.builder;

    let pred = match *op {
      CmpOp::Eq => Predicate::Eq,
      CmpOp::Ne => Predicate::Ne,
      CmpOp::Lt => Predicate::Lt,
      CmpOp::Le => Predicate::Le,
      CmpOp::Gt => Predicate::Gt,
      CmpOp::Ge => Predicate::Ge
    };

    self.stack.push(builder.create_cmp(&lhs_val, &rhs_val, pred));
	}

	pub fn Arithm(&mut self, ty: &Ty, op: &ArithmOp, lhs: &Expr, rhs: &Expr)
	{
    let lhs_val = self.visit(lhs);
    let rhs_val = self.visit(rhs);

    let builder = &self.builder;

    self.stack.push(match *op {
      ArithmOp::Plus => {builder.create_add(&lhs_val, &rhs_val)}
      ArithmOp::Sub  => {builder.create_sub(&lhs_val, &rhs_val)}
      ArithmOp::Mul  => {builder.create_mul(&lhs_val, &rhs_val)}
      ArithmOp::Div  => {builder.create_div(&lhs_val, &rhs_val)}
      ArithmOp::Rem  => {builder.create_rem(&lhs_val, &rhs_val)}
    });
	}

	pub fn Func(&self, f: &FnDecl, args: &Vec<Box<Expr>>)
	{
	}

  pub fn Const(&mut self, l: &Literal)
  {
    match *l {
      Literal::U8(ref v)  => self.push_value(v),
      Literal::I8(ref v)  => self.push_value(v),
      Literal::I16(ref v) => self.push_value(v),
      Literal::I32(ref v) => self.push_value(v),
      Literal::I64(ref v) => self.push_value(v),
      Literal::F32(ref v) => self.push_value(v),
      Literal::F64(ref v) => self.push_value(v),
      _                    => unimplemented!()
    }
  }

	pub fn Field(&mut self, ty: &Ty, name: &str)
	{
		/*
    let found: Option<(usize, &Ty, &&str)>;

		found = izip!(0 .. self.types.len(), self.types, self.names)
						.find(|&(i, t, n)| *n == name);

    let eval = match found {
    	Some(f) => FieldEvaluator {idx: f.0, ty: f.1.clone()},
    	None    => panic!("no such field for {}", name)
    };*/
	}
}

impl<'a> visitor::Visitor for ExprCompiler<'a>
{
	fn accept(&mut self, e: &Expr)
	{
	  match *e.kind() {
	    ExprKind::Not      (ref c)               => self.Not(c),
	    ExprKind::IsNull   (ref c)               => self.IsNull(c),
	    ExprKind::IsNotNull(ref c)               => self.IsNotNull(c),
	    ExprKind::PlusSign (ref c)               => self.PlusSign(c),
	    ExprKind::MinusSign(ref c)               => self.MinusSign(c),
	    ExprKind::Cast     (ref c, ref f, ref t) => self.Cast(c, f, t),

	    ExprKind::And      (ref l, ref r)        => self.And(l, r),
	    ExprKind::Or       (ref l, ref r)        => self.Or(l, r),
	    ExprKind::Cmp      (ref o, ref l, ref r) => self.Cmp(o, l, r),
	    ExprKind::Arithm   (ref o, ref l, ref r) => self.Arithm(e.ty(), o, l, r),

	    ExprKind::Fn     (ref f, ref args)  => self.Func(f, args),
			ExprKind::Field  (ref name)         => self.Field(e.ty(), name),
	    ExprKind::Const  (ref lit)          => self.Const(lit),
	    /*
	    ExprKind::Switch(ref cases, ref default) => {
	    	for c in cases.iter() {
	    		v.accept(c);
	   	  }

	    	v.accept(default);
	    },

	    ExprKind::Case   (ref l, ref r) => { v.accept(l); v.accept(r) },*/
	    _ => panic!("")
	  }
	}
}

pub trait Processor
{
  fn process(
    &self,
    input: &Page,
    builder: &mut OwnedPageBuilder) -> Void;
}

#[cfg(test)]
mod tests {
	use common::rows::{
		Page,
		OwnedPageBuilder,
		PageId
	};
	use common::plugin::*;
	use common::session::Session;
	use common::storage::{RandomTable, MemTable};
	use common::types::*;

  use jit::JitCompiler;

	use plan::expr::*;
	use driver::DriverContext;

	use super::super::NamedSchema;

	use super::*;

  #[test]
  pub fn codegen() {
    let plugin_mgr = PluginManager::new();
    let jit = JitCompiler::new_from_bc("../common/target/ir/common.bc").ok().unwrap();
    assert!(jit.get_ty("struct.MiniPage").is_some());
    assert!(jit.get_ty("struct.Page").is_some());

    let types: Vec<Ty> = schema!(
	    I64, // l_orderkey      bigint
	    I64, // l_partkey       bigint
	    I64, // l_suppkey       bigint
	    I32, // l_linenumber    int
	    F64, // l_quantity      double,
	    F64, // l_extendedprice double,
	    F64, // l_discount      double,
	    F64  // l_tax           double,
	    // string types are not implmeneted yet.
    );

	  let names: Vec<&str> = vec![
	  	"l_orderkey",
	  	"l_partkey",
	  	"l_suppkey",
	  	"l_linenumber",
	  	"l_quantity",
	  	"l_extendedprice",
	  	"l_discount",
	  	"l_tax"
	  ];

    let expr = Plus(I32, Const(19800401i32), Const(1i32));
    //let expr = Or(Const(4i32), Const(2i32));
    //let expr = Not(Const(0i32));
    //let expr = MinusSign(Const(7i32));
    let schema  = NamedSchema::new(&names, &types);
  	let session = Session;

    let map = ExprCompiler::compile(&jit,
                                   plugin_mgr.fn_registry(),
																	 &session,
																	 &schema,
																	 &expr).ok().unwrap();

    let mut minipage = MiniPage2 {
      ptr: ::std::ptr::null(),
      size: 0
    };
    assert_eq!(map(&mut minipage), 19800401i32);
  }
}<|endoftext|>


<commit_before>#![allow(clippy::unreadable_literal)]

use chrono::Utc;
use imagesize::size;
use orgize::{
    export::{html::Escape, DefaultHtmlHandler, HtmlHandler},
    Element,
};
use std::{io::Write, path::PathBuf, process::Command};
use syntect::{
    easy::HighlightLines,
    highlighting::ThemeSet,
    html::{styled_line_to_highlighted_html, IncludeBackground},
    parsing::SyntaxSet,
};
use url::Url;

use crate::error::{Error, Result};

struct SolomonBaseHandler {
    default: DefaultHtmlHandler,
    last_char: Option<char>,
}

impl Default for SolomonBaseHandler {
    fn default() -> Self {
        SolomonBaseHandler {
            default: DefaultHtmlHandler,
            last_char: None,
        }
    }
}

impl SolomonBaseHandler {
    fn highlight(&self, language: Option<&str>, content: &str) -> String {
        lazy_static::lazy_static! {
            static ref SYNTAX_SET: SyntaxSet = {
                let set = SyntaxSet::load_defaults_newlines();
                let mut builder = set.into_builder();
                // add extra language syntax files
                builder.add_from_folder("gen/syntax", true).unwrap();
                builder.build()
            };
            static ref THEME_SET: ThemeSet = ThemeSet::load_defaults();
        }

        let language = match language {
            Some("elisp") | Some("emacs-lisp") => Some("lisp"),
            _ => language,
        };
        let mut highlighter = HighlightLines::new(
            language
                .and_then(|lang| SYNTAX_SET.find_syntax_by_token(lang))
                .unwrap_or_else(|| SYNTAX_SET.find_syntax_plain_text()),
            &THEME_SET.themes["InspiredGitHub"],
        );
        let regions = highlighter.highlight(content, &SYNTAX_SET);
        styled_line_to_highlighted_html(&regions[..], IncludeBackground::No)
    }
}

impl HtmlHandler<Error> for SolomonBaseHandler {
    fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
        match element {
            Element::Document => (),
            Element::InlineSrc(inline_src) => write!(
                w,
                "{}",
                self.highlight(Some(&inline_src.lang), &inline_src.body)
            )?,
            Element::SourceBlock(block) => {
                if block.language.is_empty() {
                    write!(w, "
{}
", block.contents)?;
                } else {
                    write!(
                        w,
                        "
{}
",
                        self.highlight(Some(&block.language), &block.contents)
                    )?
                }
            }
            Element::FixedWidth { value } => {
                write!(w, "
{}
", self.highlight(None, value))?
            }
            Element::ExampleBlock(block) => write!(
                w,
                "
{}
",
                self.highlight(None, &block.contents)
            )?,
            Element::Macros(macros) => match &macros.name as &str {
                "age-days" => {
                    write!(w, " {:.} ", (Utc::now().timestamp() - 1382071200) / 86400)?;
                }
                "angular-core-version" => {
                    write_nodejs_package_version(w, "@angular/core")?;
                }
                "angular-material-version" => {
                    write_nodejs_package_version(w, "@angular/material")?;
                }
                "angular-cli-version" => {
                    write_nodejs_package_version(w, "@angular/cli")?;
                }
                _ => (),
            }
            Element::Paragraph => {
                self.last_char = None;
                write!(w, "
")?;
            }
            Element::Link(link) => {
                let text = link.desc.as_ref().unwrap_or(&link.path);
                if should_insert_space(self.last_char, text.chars().next()) {
                    write!(w, " ")?;
                }
                self.last_char = text.chars().last();
                write!(w, "{}", Escape(&link.path), Escape(text))?;
            }
            Element::Text { value } => {
                for line in value.lines() {
                    let text = line.trim();
                    if should_insert_space(self.last_char, text.chars().next()) {
                        write!(w, " ")?;
                    }
                    self.last_char = text.chars().last();
                    write!(w, "{}", Escape(text))?;
                }
            }
            Element::Verbatim { value } | Element::Code { value } => {
                let text = value.trim();
                if should_insert_space(self.last_char, text.chars().next()) {
                    write!(w, " ")?;
                }
                self.last_char = text.chars().last();
                write!(w, "{}", Escape(text))?;
            }
            _ => self.default.start(w, element)?,
        }
        Ok(())
    }
}

#[derive(Default)]
pub struct SolomonHtmlHandler(SolomonBaseHandler);

impl HtmlHandler<Error> for SolomonHtmlHandler {
    fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
        if let Element::Link(link) = element {
            let url = Url::parse(&link.path)?;
            if url.scheme() == "file" {
                let size = size(PathBuf::from(r"content/post").join(&url.path()[1..]))?;

                write!(
                    w,
                    "
\

\

",
                    url.path(),
                    (size.height as f32 / size.width as f32) * 100.
                )?;
            } else {
                self.0.start(w, element)?;
            }
        } else {
            self.0.start(w, element)?;
        }

        Ok(())
    }
}

#[derive(Default)]
pub struct SolomonRssHandler(SolomonBaseHandler);

impl HtmlHandler<Error> for SolomonRssHandler {
    fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
        if let Element::Link(link) = element {
            let url = Url::parse(&link.path)?;
            if url.scheme() == "file" {
                let size = size(PathBuf::from(r"content/post").join(&url.path()[1..]))?;

                write!(
                    w,
                    r#""#,
                    url.path(),
                    size.width,
                    size.height
                )?;
            } else {
                self.0.start(w, element)?;
            }
        } else {
            self.0.start(w, element)?;
        }

        Ok(())
    }
}

#[derive(Default)]
pub struct SolomonAmpHandler(SolomonBaseHandler);

impl HtmlHandler<Error> for SolomonAmpHandler {
    fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
        if let Element::Link(link) = element {
            let url = Url::parse(&link.path)?;
            if url.scheme() == "file" {
                let size = size(PathBuf::from(r"content/post").join(&url.path()[1..]))?;

                write!(
                    w,
                    "<amp-img src=\"{}\" width=\"{}\" height=\"{}\" layout=\"responsive\" \
                     class=\"i-amphtml-layout-responsive i-amphtml-layout-size-defined\" i-amphtml-layout=\"responsive\">\
                     </amp-img>",
                    url.path(),
                    size.width,
                    size.height,
                    (size.height as f32 / size.width as f32) * 100.
                )?;
            } else {
                self.0.start(w, element)?;
            }
        } else {
            self.0.start(w, element)?;
        }

        Ok(())
    }
}

fn write_nodejs_package_version<W: Write>(mut w: W, package: &str) -> Result<()> {
    write!(w, "")?;

    let output = Command::new("yarn")
        .args(&["--cwd", "web", "--silent", "list", "--pattern", package])
        .output()?;

    let output = String::from_utf8(output.stdout)?;

    if let Some(version) = output.trim().split_whitespace().last() {
        write!(w, "{}", version)?;
    }

    write!(w, "")?;

    Ok(())
}

fn should_insert_space(c1: Option<char>, c2: Option<char>) -> bool {
    const PUNCTUATIONS: [char; 14] = [
        '。', '？', '，', '、', '；', '：', '“', '”', '「', '」', '（', '）', '《', '》',
    ];

    if let (Some(c1), Some(c2)) = (c1, c2) {
        (c1.is_ascii_graphic() && c2.is_ascii_graphic())
            || (c1.is_ascii_graphic()
                && 0x4E00 < (c2 as u32)
                && (c2 as u32) < 0x9FFF
                && !PUNCTUATIONS.contains(&c2))
            || (c2.is_ascii_graphic()
                && 0x4E00 < (c1 as u32)
                && (c1 as u32) < 0x9FFF
                && !PUNCTUATIONS.contains(&c1))
    } else {
        false
    }
}
<commit_msg>fix(gen): don't append closing tag <commit_after>#![allow(clippy::unreadable_literal)]

use chrono::Utc;
use imagesize::size;
use orgize::{
    export::{html::Escape, DefaultHtmlHandler, HtmlHandler},
    Element,
};
use std::{io::Write, path::PathBuf, process::Command};
use syntect::{
    easy::HighlightLines,
    highlighting::ThemeSet,
    html::{styled_line_to_highlighted_html, IncludeBackground},
    parsing::SyntaxSet,
};
use url::Url;

use crate::error::{Error, Result};

struct SolomonBaseHandler {
    default: DefaultHtmlHandler,
    last_char: Option<char>,
}

impl Default for SolomonBaseHandler {
    fn default() -> Self {
        SolomonBaseHandler {
            default: DefaultHtmlHandler,
            last_char: None,
        }
    }
}

impl SolomonBaseHandler {
    fn highlight(&self, language: Option<&str>, content: &str) -> String {
        lazy_static::lazy_static! {
            static ref SYNTAX_SET: SyntaxSet = {
                let set = SyntaxSet::load_defaults_newlines();
                let mut builder = set.into_builder();
                // add extra language syntax files
                builder.add_from_folder("gen/syntax", true).unwrap();
                builder.build()
            };
            static ref THEME_SET: ThemeSet = ThemeSet::load_defaults();
        }

        let language = match language {
            Some("elisp") | Some("emacs-lisp") => Some("lisp"),
            _ => language,
        };
        let mut highlighter = HighlightLines::new(
            language
                .and_then(|lang| SYNTAX_SET.find_syntax_by_token(lang))
                .unwrap_or_else(|| SYNTAX_SET.find_syntax_plain_text()),
            &THEME_SET.themes["InspiredGitHub"],
        );
        let regions = highlighter.highlight(content, &SYNTAX_SET);
        styled_line_to_highlighted_html(&regions[..], IncludeBackground::No)
    }
}

impl HtmlHandler<Error> for SolomonBaseHandler {
    fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
        match element {
            Element::Document => (),
            Element::InlineSrc(inline_src) => write!(
                w,
                "{}",
                self.highlight(Some(&inline_src.lang), &inline_src.body)
            )?,
            Element::SourceBlock(block) => {
                if block.language.is_empty() {
                    write!(w, "
{}
", block.contents)?;
                } else {
                    write!(
                        w,
                        "
{}
",
                        self.highlight(Some(&block.language), &block.contents)
                    )?
                }
            }
            Element::FixedWidth { value } => {
                write!(w, "
{}
", self.highlight(None, value))?
            }
            Element::ExampleBlock(block) => write!(
                w,
                "
{}
",
                self.highlight(None, &block.contents)
            )?,
            Element::Macros(macros) => match &macros.name as &str {
                "age-days" => {
                    write!(w, " {:.} ", (Utc::now().timestamp() - 1382071200) / 86400)?;
                }
                "angular-core-version" => {
                    write_nodejs_package_version(w, "@angular/core")?;
                }
                "angular-material-version" => {
                    write_nodejs_package_version(w, "@angular/material")?;
                }
                "angular-cli-version" => {
                    write_nodejs_package_version(w, "@angular/cli")?;
                }
                _ => (),
            }
            Element::Paragraph => {
                self.last_char = None;
                write!(w, "
")?;
            }
            Element::Link(link) => {
                let text = link.desc.as_ref().unwrap_or(&link.path);
                if should_insert_space(self.last_char, text.chars().next()) {
                    write!(w, " ")?;
                }
                self.last_char = text.chars().last();
                write!(w, "{}", Escape(&link.path), Escape(text))?;
            }
            Element::Text { value } => {
                for line in value.lines() {
                    let text = line.trim();
                    if should_insert_space(self.last_char, text.chars().next()) {
                        write!(w, " ")?;
                    }
                    self.last_char = text.chars().last();
                    write!(w, "{}", Escape(text))?;
                }
            }
            Element::Verbatim { value } | Element::Code { value } => {
                let text = value.trim();
                if should_insert_space(self.last_char, text.chars().next()) {
                    write!(w, " ")?;
                }
                self.last_char = text.chars().last();
                write!(w, "{}", Escape(text))?;
            }
            _ => self.default.start(w, element)?,
        }
        Ok(())
    }

    fn end<W: Write>(&mut self, w: W, element: &Element<'_>) -> Result<()> {
        match element {
            Element::Document => (),
            _ => self.default.end(w, element)?,
        }
        Ok(())
    }
}

#[derive(Default)]
pub struct SolomonHtmlHandler(SolomonBaseHandler);

impl HtmlHandler<Error> for SolomonHtmlHandler {
    fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
        if let Element::Link(link) = element {
            let url = Url::parse(&link.path)?;
            if url.scheme() == "file" {
                let size = size(PathBuf::from(r"content/post").join(&url.path()[1..]))?;

                write!(
                    w,
                    "
\

\

",
                    url.path(),
                    (size.height as f32 / size.width as f32) * 100.
                )?;
            } else {
                self.0.start(w, element)?;
            }
        } else {
            self.0.start(w, element)?;
        }

        Ok(())
    }

    fn end<W: Write>(&mut self, w: W, element: &Element<'_>) -> Result<()> {
        self.0.end(w, element)
    }
}

#[derive(Default)]
pub struct SolomonRssHandler(SolomonBaseHandler);

impl HtmlHandler<Error> for SolomonRssHandler {
    fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
        if let Element::Link(link) = element {
            let url = Url::parse(&link.path)?;
            if url.scheme() == "file" {
                let size = size(PathBuf::from(r"content/post").join(&url.path()[1..]))?;

                write!(
                    w,
                    r#""#,
                    url.path(),
                    size.width,
                    size.height
                )?;
            } else {
                self.0.start(w, element)?;
            }
        } else {
            self.0.start(w, element)?;
        }

        Ok(())
    }

    fn end<W: Write>(&mut self, w: W, element: &Element<'_>) -> Result<()> {
        self.0.end(w, element)
    }
}

#[derive(Default)]
pub struct SolomonAmpHandler(SolomonBaseHandler);

impl HtmlHandler<Error> for SolomonAmpHandler {
    fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
        if let Element::Link(link) = element {
            let url = Url::parse(&link.path)?;
            if url.scheme() == "file" {
                let size = size(PathBuf::from(r"content/post").join(&url.path()[1..]))?;

                write!(
                    w,
                    "<amp-img src=\"{}\" width=\"{}\" height=\"{}\" layout=\"responsive\" \
                     class=\"i-amphtml-layout-responsive i-amphtml-layout-size-defined\" i-amphtml-layout=\"responsive\">\
                     </amp-img>",
                    url.path(),
                    size.width,
                    size.height,
                    (size.height as f32 / size.width as f32) * 100.
                )?;
            } else {
                self.0.start(w, element)?;
            }
        } else {
            self.0.start(w, element)?;
        }

        Ok(())
    }

    fn end<W: Write>(&mut self, w: W, element: &Element<'_>) -> Result<()> {
        self.0.end(w, element)
    }
}

fn write_nodejs_package_version<W: Write>(mut w: W, package: &str) -> Result<()> {
    write!(w, "")?;

    let output = Command::new("yarn")
        .args(&["--cwd", "web", "--silent", "list", "--pattern", package])
        .output()?;

    let output = String::from_utf8(output.stdout)?;

    if let Some(version) = output.trim().split_whitespace().last() {
        write!(w, "{}", version)?;
    }

    write!(w, "")?;

    Ok(())
}

fn should_insert_space(c1: Option<char>, c2: Option<char>) -> bool {
    const PUNCTUATIONS: [char; 14] = [
        '。', '？', '，', '、', '；', '：', '“', '”', '「', '」', '（', '）', '《', '》',
    ];

    if let (Some(c1), Some(c2)) = (c1, c2) {
        (c1.is_ascii_graphic() && c2.is_ascii_graphic())
            || (c1.is_ascii_graphic()
                && 0x4E00 < (c2 as u32)
                && (c2 as u32) < 0x9FFF
                && !PUNCTUATIONS.contains(&c2))
            || (c2.is_ascii_graphic()
                && 0x4E00 < (c1 as u32)
                && (c1 as u32) < 0x9FFF
                && !PUNCTUATIONS.contains(&c1))
    } else {
        false
    }
}
<|endoftext|>


<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.

#![unstable(reason = "not public", issue = "0", feature = "fd")]

use cmp;
use io::{self, Read};
use libc::{self, c_int, c_void, ssize_t};
use mem;
use sync::atomic::{AtomicBool, Ordering};
use sys::cvt;
use sys_common::AsInner;

#[derive(Debug)]
pub struct FileDesc {
    fd: c_int,
}

fn max_len() -> usize {
    // The maximum read limit on most posix-like systems is `SSIZE_MAX`,
    // with the man page quoting that if the count of bytes to read is
    // greater than `SSIZE_MAX` the result is "unspecified".
    //
    // On macOS, however, apparently the 64-bit libc is either buggy or
    // intentionally showing odd behavior by rejecting any read with a size
    // larger than or equal to INT_MAX. To handle both of these the read
    // size is capped on both platforms.
    if cfg!(target_os = "macos") {
        <c_int>::max_value() as usize - 1
    } else {
        <ssize_t>::max_value() as usize
    }
}

impl FileDesc {
    pub fn new(fd: c_int) -> FileDesc {
        FileDesc { fd: fd }
    }

    pub fn raw(&self) -> c_int { self.fd }

    /// Extracts the actual filedescriptor without closing it.
    pub fn into_raw(self) -> c_int {
        let fd = self.fd;
        mem::forget(self);
        fd
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::read(self.fd,
                       buf.as_mut_ptr() as *mut c_void,
                       cmp::min(buf.len(), max_len()))
        })?;
        Ok(ret as usize)
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }

    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pread64;

        #[cfg(target_os = "emscripten")]
        unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
            -> io::Result<isize>
        {
            use libc::pread64;
            cvt(pread64(fd, buf, count, offset as i32))
        }

        #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
        unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
            -> io::Result<isize>
        {
            #[cfg(target_os = "linux")]
            use libc::pread64;
            #[cfg(not(target_os = "linux"))]
            use libc::pread as pread64;
            cvt(pread64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pread64(self.fd,
                        buf.as_mut_ptr() as *mut c_void,
                        cmp::min(buf.len(), max_len()),
                        offset as i64)
                .map(|n| n as usize)
        }
    }

    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::write(self.fd,
                        buf.as_ptr() as *const c_void,
                        cmp::min(buf.len(), max_len()))
        })?;
        Ok(ret as usize)
    }

    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pwrite64;

        #[cfg(target_os = "emscripten")]
        unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
            -> io::Result<isize>
        {
            use libc::pwrite64;
            cvt(pwrite64(fd, buf, count, offset as i32))
        }

        #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
        unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
            -> io::Result<isize>
        {
            #[cfg(target_os = "linux")]
            use libc::pwrite64;
            #[cfg(not(target_os = "linux"))]
            use libc::pwrite as pwrite64;
            cvt(pwrite64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pwrite64(self.fd,
                         buf.as_ptr() as *const c_void,
                         cmp::min(buf.len(), max_len()),
                         offset as i64)
                .map(|n| n as usize)
        }
    }

    #[cfg(not(any(target_env = "newlib",
                  target_os = "solaris",
                  target_os = "emscripten",
                  target_os = "fuchsia",
                  target_os = "l4re",
                  target_os = "haiku")))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
            Ok(())
        }
    }
    #[cfg(any(target_env = "newlib",
              target_os = "solaris",
              target_os = "emscripten",
              target_os = "fuchsia",
              target_os = "l4re",
              target_os = "haiku"))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
            let new = previous | libc::FD_CLOEXEC;
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
            }
            Ok(())
        }
    }

    #[cfg(target_os = "linux")]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let v = nonblocking as c_int;
            cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
            Ok(())
        }
    }

    #[cfg(not(target_os = "linux"))]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
            let new = if nonblocking {
                previous | libc::O_NONBLOCK
            } else {
                previous & !libc::O_NONBLOCK
            };
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
            }
            Ok(())
        }
    }

    pub fn duplicate(&self) -> io::Result<FileDesc> {
        // We want to atomically duplicate this file descriptor and set the
        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
        // flag, however, isn't supported on older Linux kernels (earlier than
        // 2.6.24).
        //
        // To detect this and ensure that CLOEXEC is still set, we
        // follow a strategy similar to musl [1] where if passing
        // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
        // supported (the third parameter, 0, is always valid), so we stop
        // trying that.
        //
        // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
        // resolve so we at least compile this.
        //
        // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
        #[cfg(any(target_os = "android", target_os = "haiku"))]
        use libc::F_DUPFD as F_DUPFD_CLOEXEC;
        #[cfg(not(any(target_os = "android", target_os="haiku")))]
        use libc::F_DUPFD_CLOEXEC;

        let make_filedesc = |fd| {
            let fd = FileDesc::new(fd);
            fd.set_cloexec()?;
            Ok(fd)
        };
        static TRY_CLOEXEC: AtomicBool =
            AtomicBool::new(!cfg!(target_os = "android"));
        let fd = self.raw();
        if TRY_CLOEXEC.load(Ordering::Relaxed) {
            match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
                // We *still* call the `set_cloexec` method as apparently some
                // linux kernel at some point stopped setting CLOEXEC even
                // though it reported doing so on F_DUPFD_CLOEXEC.
                Ok(fd) => {
                    return Ok(if cfg!(target_os = "linux") {
                        make_filedesc(fd)?
                    } else {
                        FileDesc::new(fd)
                    })
                }
                Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
                    TRY_CLOEXEC.store(false, Ordering::Relaxed);
                }
                Err(e) => return Err(e),
            }
        }
        cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc)
    }
}

impl<'a> Read for &'a FileDesc {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }
}

impl AsInner<c_int> for FileDesc {
    fn as_inner(&self) -> &c_int { &self.fd }
}

impl Drop for FileDesc {
    fn drop(&mut self) {
        // Note that errors are ignored when closing a file descriptor. The
        // reason for this is that if an error occurs we don't actually know if
        // the file descriptor was closed or not, and if we retried (for
        // something like EINTR), we might close another valid file descriptor
        // (opened after we closed ours.
        let _ = unsafe { libc::close(self.fd) };
    }
}
<commit_msg>Do not silently truncate offsets for `read_at`/`write_at` on emscripten<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.

#![unstable(reason = "not public", issue = "0", feature = "fd")]

use cmp;
use io::{self, Read};
use libc::{self, c_int, c_void, ssize_t};
use mem;
use sync::atomic::{AtomicBool, Ordering};
use sys::cvt;
use sys_common::AsInner;

#[derive(Debug)]
pub struct FileDesc {
    fd: c_int,
}

fn max_len() -> usize {
    // The maximum read limit on most posix-like systems is `SSIZE_MAX`,
    // with the man page quoting that if the count of bytes to read is
    // greater than `SSIZE_MAX` the result is "unspecified".
    //
    // On macOS, however, apparently the 64-bit libc is either buggy or
    // intentionally showing odd behavior by rejecting any read with a size
    // larger than or equal to INT_MAX. To handle both of these the read
    // size is capped on both platforms.
    if cfg!(target_os = "macos") {
        <c_int>::max_value() as usize - 1
    } else {
        <ssize_t>::max_value() as usize
    }
}

impl FileDesc {
    pub fn new(fd: c_int) -> FileDesc {
        FileDesc { fd: fd }
    }

    pub fn raw(&self) -> c_int { self.fd }

    /// Extracts the actual filedescriptor without closing it.
    pub fn into_raw(self) -> c_int {
        let fd = self.fd;
        mem::forget(self);
        fd
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::read(self.fd,
                       buf.as_mut_ptr() as *mut c_void,
                       cmp::min(buf.len(), max_len()))
        })?;
        Ok(ret as usize)
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }

    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pread64;

        #[cfg(target_os = "emscripten")]
        unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
            -> io::Result<isize>
        {
            use convert::TryInto;
            use libc::pread64;
            // pread64 on emscripten actually takes a 32 bit offset
            if let Ok(o) = offset.try_into() {
                cvt(pread64(fd, buf, count, o))
            } else {
                Err(io::Error::new(io::ErrorKind::InvalidInput,
                                   "cannot pread >2GB"))
            }
        }

        #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
        unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
            -> io::Result<isize>
        {
            #[cfg(target_os = "linux")]
            use libc::pread64;
            #[cfg(not(target_os = "linux"))]
            use libc::pread as pread64;
            cvt(pread64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pread64(self.fd,
                        buf.as_mut_ptr() as *mut c_void,
                        cmp::min(buf.len(), max_len()),
                        offset as i64)
                .map(|n| n as usize)
        }
    }

    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::write(self.fd,
                        buf.as_ptr() as *const c_void,
                        cmp::min(buf.len(), max_len()))
        })?;
        Ok(ret as usize)
    }

    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pwrite64;

        #[cfg(target_os = "emscripten")]
        unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
            -> io::Result<isize>
        {
            use convert::TryInto;
            use libc::pwrite64;
            // pwrite64 on emscripten actually takes a 32 bit offset
            if let Ok(o) = offset.try_into() {
                cvt(pwrite64(fd, buf, count, o))
            } else {
                Err(io::Error::new(io::ErrorKind::InvalidInput,
                                   "cannot pwrite >2GB"))
            }
        }

        #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
        unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
            -> io::Result<isize>
        {
            #[cfg(target_os = "linux")]
            use libc::pwrite64;
            #[cfg(not(target_os = "linux"))]
            use libc::pwrite as pwrite64;
            cvt(pwrite64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pwrite64(self.fd,
                         buf.as_ptr() as *const c_void,
                         cmp::min(buf.len(), max_len()),
                         offset as i64)
                .map(|n| n as usize)
        }
    }

    #[cfg(not(any(target_env = "newlib",
                  target_os = "solaris",
                  target_os = "emscripten",
                  target_os = "fuchsia",
                  target_os = "l4re",
                  target_os = "haiku")))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
            Ok(())
        }
    }
    #[cfg(any(target_env = "newlib",
              target_os = "solaris",
              target_os = "emscripten",
              target_os = "fuchsia",
              target_os = "l4re",
              target_os = "haiku"))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
            let new = previous | libc::FD_CLOEXEC;
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
            }
            Ok(())
        }
    }

    #[cfg(target_os = "linux")]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let v = nonblocking as c_int;
            cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
            Ok(())
        }
    }

    #[cfg(not(target_os = "linux"))]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
            let new = if nonblocking {
                previous | libc::O_NONBLOCK
            } else {
                previous & !libc::O_NONBLOCK
            };
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
            }
            Ok(())
        }
    }

    pub fn duplicate(&self) -> io::Result<FileDesc> {
        // We want to atomically duplicate this file descriptor and set the
        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
        // flag, however, isn't supported on older Linux kernels (earlier than
        // 2.6.24).
        //
        // To detect this and ensure that CLOEXEC is still set, we
        // follow a strategy similar to musl [1] where if passing
        // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
        // supported (the third parameter, 0, is always valid), so we stop
        // trying that.
        //
        // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
        // resolve so we at least compile this.
        //
        // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
        #[cfg(any(target_os = "android", target_os = "haiku"))]
        use libc::F_DUPFD as F_DUPFD_CLOEXEC;
        #[cfg(not(any(target_os = "android", target_os="haiku")))]
        use libc::F_DUPFD_CLOEXEC;

        let make_filedesc = |fd| {
            let fd = FileDesc::new(fd);
            fd.set_cloexec()?;
            Ok(fd)
        };
        static TRY_CLOEXEC: AtomicBool =
            AtomicBool::new(!cfg!(target_os = "android"));
        let fd = self.raw();
        if TRY_CLOEXEC.load(Ordering::Relaxed) {
            match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
                // We *still* call the `set_cloexec` method as apparently some
                // linux kernel at some point stopped setting CLOEXEC even
                // though it reported doing so on F_DUPFD_CLOEXEC.
                Ok(fd) => {
                    return Ok(if cfg!(target_os = "linux") {
                        make_filedesc(fd)?
                    } else {
                        FileDesc::new(fd)
                    })
                }
                Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
                    TRY_CLOEXEC.store(false, Ordering::Relaxed);
                }
                Err(e) => return Err(e),
            }
        }
        cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc)
    }
}

impl<'a> Read for &'a FileDesc {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }
}

impl AsInner<c_int> for FileDesc {
    fn as_inner(&self) -> &c_int { &self.fd }
}

impl Drop for FileDesc {
    fn drop(&mut self) {
        // Note that errors are ignored when closing a file descriptor. The
        // reason for this is that if an error occurs we don't actually know if
        // the file descriptor was closed or not, and if we retried (for
        // something like EINTR), we might close another valid file descriptor
        // (opened after we closed ours.
        let _ = unsafe { libc::close(self.fd) };
    }
}
<|endoftext|>


<commit_before>use crate::any::Any;
use crate::error::Error as StdError;
use crate::ffi::{OsString, OsStr, CString, CStr};
use crate::fmt;
use crate::io;
use crate::marker::PhantomData;
use crate::os::wasi::prelude::*;
use crate::path::{self, PathBuf};
use crate::ptr;
use crate::str;
use crate::sys::memchr;
use crate::sys::{cvt, unsupported, Void};
use crate::vec;

use wasi::wasi_unstable as wasi;

#[cfg(not(target_feature = "atomics"))]
pub unsafe fn env_lock() -> impl Any {
    // No need for a lock if we're single-threaded, but this function will need
    // to get implemented for multi-threaded scenarios
}

pub fn errno() -> i32 {
    panic!("unsupported")
}

pub fn error_string(errno: i32) -> String {
    wasi::error_string(errno).to_string()
}

pub fn getcwd() -> io::Result<PathBuf> {
    unsupported()
}

pub fn chdir(_: &path::Path) -> io::Result<()> {
    unsupported()
}

pub struct SplitPaths<'a>(&'a Void);

pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
    panic!("unsupported")
}

impl<'a> Iterator for SplitPaths<'a> {
    type Item = PathBuf;
    fn next(&mut self) -> Option<PathBuf> {
        match *self.0 {}
    }
}

#[derive(Debug)]
pub struct JoinPathsError;

pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
    where I: Iterator<Item=T>, T: AsRef<OsStr>
{
    Err(JoinPathsError)
}

impl fmt::Display for JoinPathsError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "not supported on wasm yet".fmt(f)
    }
}

impl StdError for JoinPathsError {
    fn description(&self) -> &str {
        "not supported on wasm yet"
    }
}

pub fn current_exe() -> io::Result<PathBuf> {
    unsupported()
}

pub struct Env {
    iter: vec::IntoIter<(OsString, OsString)>,
    _dont_send_or_sync_me: PhantomData<*mut ()>,
}

impl Iterator for Env {
    type Item = (OsString, OsString);
    fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() }
    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}

pub fn env() -> Env {
    unsafe {
        let _guard = env_lock();
        // FIXME: replace with wasi::environ_get
        let mut environ = libc::environ;
        let mut result = Vec::new();
        while environ != ptr::null_mut() && *environ != ptr::null_mut() {
            if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
                result.push(key_value);
            }
            environ = environ.offset(1);
        }
        return Env {
            iter: result.into_iter(),
            _dont_send_or_sync_me: PhantomData,
        }
    }

    // See src/libstd/sys/unix/os.rs, same as that
    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
        if input.is_empty() {
            return None;
        }
        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
        pos.map(|p| (
            OsStringExt::from_vec(input[..p].to_vec()),
            OsStringExt::from_vec(input[p+1..].to_vec()),
        ))
    }
}

pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
    let k = CString::new(k.as_bytes())?;
    unsafe {
        let _guard = env_lock();
        let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
        let ret = if s.is_null() {
            None
        } else {
            Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
        };
        Ok(ret)
    }
}

pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
    let k = CString::new(k.as_bytes())?;
    let v = CString::new(v.as_bytes())?;

    unsafe {
        let _guard = env_lock();
        cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ())
    }
}

pub fn unsetenv(n: &OsStr) -> io::Result<()> {
    let nbuf = CString::new(n.as_bytes())?;

    unsafe {
        let _guard = env_lock();
        cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ())
    }
}

pub fn temp_dir() -> PathBuf {
    panic!("no filesystem on wasm")
}

pub fn home_dir() -> Option<PathBuf> {
    None
}

pub fn exit(code: i32) -> ! {
    unsafe { wasi::proc_exit(code as u32) }
}

pub fn getpid() -> u32 {
    panic!("unsupported");
}
<commit_msg>return 0 from errno function<commit_after>use crate::any::Any;
use crate::error::Error as StdError;
use crate::ffi::{OsString, OsStr, CString, CStr};
use crate::fmt;
use crate::io;
use crate::marker::PhantomData;
use crate::os::wasi::prelude::*;
use crate::path::{self, PathBuf};
use crate::ptr;
use crate::str;
use crate::sys::memchr;
use crate::sys::{cvt, unsupported, Void};
use crate::vec;

use wasi::wasi_unstable as wasi;

#[cfg(not(target_feature = "atomics"))]
pub unsafe fn env_lock() -> impl Any {
    // No need for a lock if we're single-threaded, but this function will need
    // to get implemented for multi-threaded scenarios
}

pub fn errno() -> i32 {
    0
}

pub fn error_string(errno: i32) -> String {
    wasi::error_string(errno).to_string()
}

pub fn getcwd() -> io::Result<PathBuf> {
    unsupported()
}

pub fn chdir(_: &path::Path) -> io::Result<()> {
    unsupported()
}

pub struct SplitPaths<'a>(&'a Void);

pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
    panic!("unsupported")
}

impl<'a> Iterator for SplitPaths<'a> {
    type Item = PathBuf;
    fn next(&mut self) -> Option<PathBuf> {
        match *self.0 {}
    }
}

#[derive(Debug)]
pub struct JoinPathsError;

pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
    where I: Iterator<Item=T>, T: AsRef<OsStr>
{
    Err(JoinPathsError)
}

impl fmt::Display for JoinPathsError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "not supported on wasm yet".fmt(f)
    }
}

impl StdError for JoinPathsError {
    fn description(&self) -> &str {
        "not supported on wasm yet"
    }
}

pub fn current_exe() -> io::Result<PathBuf> {
    unsupported()
}

pub struct Env {
    iter: vec::IntoIter<(OsString, OsString)>,
    _dont_send_or_sync_me: PhantomData<*mut ()>,
}

impl Iterator for Env {
    type Item = (OsString, OsString);
    fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() }
    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}

pub fn env() -> Env {
    unsafe {
        let _guard = env_lock();
        // FIXME: replace with wasi::environ_get
        let mut environ = libc::environ;
        let mut result = Vec::new();
        while environ != ptr::null_mut() && *environ != ptr::null_mut() {
            if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
                result.push(key_value);
            }
            environ = environ.offset(1);
        }
        return Env {
            iter: result.into_iter(),
            _dont_send_or_sync_me: PhantomData,
        }
    }

    // See src/libstd/sys/unix/os.rs, same as that
    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
        if input.is_empty() {
            return None;
        }
        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
        pos.map(|p| (
            OsStringExt::from_vec(input[..p].to_vec()),
            OsStringExt::from_vec(input[p+1..].to_vec()),
        ))
    }
}

pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
    let k = CString::new(k.as_bytes())?;
    unsafe {
        let _guard = env_lock();
        let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
        let ret = if s.is_null() {
            None
        } else {
            Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
        };
        Ok(ret)
    }
}

pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
    let k = CString::new(k.as_bytes())?;
    let v = CString::new(v.as_bytes())?;

    unsafe {
        let _guard = env_lock();
        cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ())
    }
}

pub fn unsetenv(n: &OsStr) -> io::Result<()> {
    let nbuf = CString::new(n.as_bytes())?;

    unsafe {
        let _guard = env_lock();
        cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ())
    }
}

pub fn temp_dir() -> PathBuf {
    panic!("no filesystem on wasm")
}

pub fn home_dir() -> Option<PathBuf> {
    None
}

pub fn exit(code: i32) -> ! {
    unsafe { wasi::proc_exit(code as u32) }
}

pub fn getpid() -> u32 {
    panic!("unsupported");
}
<|endoftext|>


<commit_before>use askama::Template;

#[derive(Template)]
#[template(path = "for.html")]
struct ForTemplate<'a> {
    strings: Vec<&'a str>,
    tuple_strings: Vec<(&'a str, &'a str)>,
}

#[test]
fn test_for() {
    let s = ForTemplate {
        strings: vec!["A", "alfa", "1"],
        tuple_strings: vec![("B", "beta")],
    };
    assert_eq!(
        s.render().unwrap(),
        "0. A (first)\n1. alfa\n2. 1\n\n0. B,beta (first)\n"
    );
}

#[derive(Template)]
#[template(path = "nested-for.html")]
struct NestedForTemplate<'a> {
    seqs: Vec<&'a [&'a str]>,
}

#[test]
fn test_nested_for() {
    let alpha = vec!["a", "b", "c"];
    let numbers = vec!["one", "two"];
    let s = NestedForTemplate {
        seqs: vec![&alpha, &numbers],
    };
    assert_eq!(s.render().unwrap(), "1\n  0a1b2c2\n  0one1two");
}

#[derive(Template)]
#[template(path = "precedence-for.html")]
struct PrecedenceTemplate<'a> {
    strings: Vec<&'a str>,
}

#[test]
fn test_precedence_for() {
    let s = PrecedenceTemplate {
        strings: vec!["A", "alfa", "1"],
    };
    assert_eq!(
        s.render().unwrap(),
        "0. A2 (first)\n1. alfa4\n2. 16 (last)\n"
    );
}

#[derive(Template)]
#[template(path = "for-range.html")]
struct ForRangeTemplate {
    init: i32,
    end: i32,
}

#[test]
fn test_for_range() {
    let s = ForRangeTemplate { init: -1, end: 1 };
    assert_eq!(
        s.render().unwrap(),
        "foo (first)\nfoo (last)\nbar\nbar\nfoo\nbar\nbar\n"
    );
}

#[derive(Template)]
#[template(source = "{% for i in [1, 2, 3] %}{{ i }}{% endfor %}", ext = "txt")]
struct ForArrayTemplate {}

#[test]
fn test_for_array() {
    let t = ForArrayTemplate {};
    assert_eq!(t.render().unwrap(), "123");
}

#[derive(Template)]
#[template(
    source = "{% for i in [1, 2, 3].iter() %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForMethodCallTemplate {}

#[test]
fn test_for_method_call() {
    let t = ForMethodCallTemplate {};
    assert_eq!(t.render().unwrap(), "123");
}

#[derive(Template)]
#[template(
    source = "{% for i in [1, 2, 3, 4, 5][3..] %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForIndexTemplate {}

#[test]
fn test_for_index() {
    let t = ForIndexTemplate {};
    assert_eq!(t.render().unwrap(), "45");
}
<commit_msg>Added copy literals related test case<commit_after>use askama::Template;

#[derive(Template)]
#[template(path = "for.html")]
struct ForTemplate<'a> {
    strings: Vec<&'a str>,
    tuple_strings: Vec<(&'a str, &'a str)>,
}

#[test]
fn test_for() {
    let s = ForTemplate {
        strings: vec!["A", "alfa", "1"],
        tuple_strings: vec![("B", "beta")],
    };
    assert_eq!(
        s.render().unwrap(),
        "0. A (first)\n1. alfa\n2. 1\n\n0. B,beta (first)\n"
    );
}

#[derive(Template)]
#[template(path = "nested-for.html")]
struct NestedForTemplate<'a> {
    seqs: Vec<&'a [&'a str]>,
}

#[test]
fn test_nested_for() {
    let alpha = vec!["a", "b", "c"];
    let numbers = vec!["one", "two"];
    let s = NestedForTemplate {
        seqs: vec![&alpha, &numbers],
    };
    assert_eq!(s.render().unwrap(), "1\n  0a1b2c2\n  0one1two");
}

#[derive(Template)]
#[template(path = "precedence-for.html")]
struct PrecedenceTemplate<'a> {
    strings: Vec<&'a str>,
}

#[test]
fn test_precedence_for() {
    let s = PrecedenceTemplate {
        strings: vec!["A", "alfa", "1"],
    };
    assert_eq!(
        s.render().unwrap(),
        "0. A2 (first)\n1. alfa4\n2. 16 (last)\n"
    );
}

#[derive(Template)]
#[template(path = "for-range.html")]
struct ForRangeTemplate {
    init: i32,
    end: i32,
}

#[test]
fn test_for_range() {
    let s = ForRangeTemplate { init: -1, end: 1 };
    assert_eq!(
        s.render().unwrap(),
        "foo (first)\nfoo (last)\nbar\nbar\nfoo\nbar\nbar\n"
    );
}

#[derive(Template)]
#[template(source = "{% for i in [1, 2, 3] %}{{ i }}{% endfor %}", ext = "txt")]
struct ForArrayTemplate {}

#[test]
fn test_for_array() {
    let t = ForArrayTemplate {};
    assert_eq!(t.render().unwrap(), "123");
}

#[derive(Template)]
#[template(
    source = "{% for i in [1, 2, 3].iter() %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForMethodCallTemplate {}

#[test]
fn test_for_method_call() {
    let t = ForMethodCallTemplate {};
    assert_eq!(t.render().unwrap(), "123");
}

#[derive(Template)]
#[template(
    source = "{% for i in ::std::iter::repeat(\"a\").take(5) %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForPathCallTemplate;

#[test]
fn test_for_path_call() {
    assert_eq!(ForPathCallTemplate.render().unwrap(), "aaaaa");
}

#[derive(Template)]
#[template(
    source = "{% for i in [1, 2, 3, 4, 5][3..] %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForIndexTemplate {}

#[test]
fn test_for_index() {
    let t = ForIndexTemplate {};
    assert_eq!(t.render().unwrap(), "45");
}
<|endoftext|>