<commit_before>#[cfg(feature = "specialize")]
use crate::HasherExt;
use core::hash::Hash;
use core::hash::Hasher;

/// Provides a way to get an optimized hasher for a given data type.
/// Rather than using a Hasher generically which can hash any value, this provides a way to get a specialized hash
/// for a specific type. So this may be faster for primitive types. It does however consume the hasher in the process.
/// # Example
/// ```
/// use std::hash::BuildHasher;
/// use ahash::RandomState;
/// use ahash::CallHasher;
///
/// let hash_builder = RandomState::new();
/// //...
/// let value = 17;
/// let hash = value.get_hash(hash_builder.build_hasher());
/// ```
pub trait CallHasher: Hash {
    fn get_hash<H: Hasher>(&self, hasher: H) -> u64;
}

#[cfg(not(feature = "specialize"))]
impl<T> CallHasher for T
where
    T: Hash,
{
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        self.hash(&mut hasher);
        hasher.finish()
    }
}

#[cfg(feature = "specialize")]
impl<T> CallHasher for T
where
    T: Hash,
{
    #[inline]
    default fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        self.hash(&mut hasher);
        hasher.finish()
    }
}

macro_rules! call_hasher_impl {
    ($typ:ty) => {
        #[cfg(feature = "specialize")]
        impl CallHasher for $typ {
            #[inline]
            fn get_hash<H: Hasher>(&self, hasher: H) -> u64 {
                hasher.hash_u64(*self as u64)
            }
        }
        #[cfg(feature = "specialize")]
        impl CallHasher for &$typ {
            #[inline]
            fn get_hash<H: Hasher>(&self, hasher: H) -> u64 {
                hasher.hash_u64(**self as u64)
            }
        }
    };
}
call_hasher_impl!(u8);
call_hasher_impl!(u16);
call_hasher_impl!(u32);
call_hasher_impl!(u64);
call_hasher_impl!(i8);
call_hasher_impl!(i16);
call_hasher_impl!(i32);
call_hasher_impl!(i64);

#[cfg(feature = "specialize")]
impl CallHasher for u128 {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write_u128(*self);
        hasher.short_finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for i128 {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write_u128(*self as u128);
        hasher.short_finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for [u8] {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self);
        hasher.finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for &[u8] {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self);
        hasher.finish()
    }
}

#[cfg(all(feature = "specialize", feature = "std"))]
impl CallHasher for Vec<u8> {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self);
        hasher.finish()
    }
}

#[cfg(all(feature = "specialize", feature = "std"))]
impl CallHasher for &Vec<u8> {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self);
        hasher.finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for str {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self.as_bytes());
        hasher.finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for &str {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self.as_bytes());
        hasher.finish()
    }
}

#[cfg(all(feature = "specialize", feature = "std"))]
impl CallHasher for String {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self.as_bytes());
        hasher.finish()
    }
}

#[cfg(all(feature = "specialize", feature = "std"))]
impl CallHasher for &String {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self.as_bytes());
        hasher.finish()
    }
}

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

    #[test]
    #[cfg(feature = "specialize")]
    pub fn test_specialized_invoked() {
        let shortened = 0_u64.get_hash(AHasher::new_with_keys(1, 2));
        let mut hasher = AHasher::new_with_keys(1, 2);
        0_u64.hash(&mut hasher);
        assert_ne!(hasher.finish(), shortened);
    }

    /// Tests that some non-trivial transformation takes place.
    #[test]
    pub fn test_input_processed() {
        let hasher = || AHasher::new_with_keys(3, 2);
        assert_ne!(0, 0_u64.get_hash(hasher()));
        assert_ne!(1, 0_u64.get_hash(hasher()));
        assert_ne!(2, 0_u64.get_hash(hasher()));
        assert_ne!(3, 0_u64.get_hash(hasher()));
        assert_ne!(4, 0_u64.get_hash(hasher()));
        assert_ne!(5, 0_u64.get_hash(hasher()));

        assert_ne!(0, 1_u64.get_hash(hasher()));
        assert_ne!(1, 1_u64.get_hash(hasher()));
        assert_ne!(2, 1_u64.get_hash(hasher()));
        assert_ne!(3, 1_u64.get_hash(hasher()));
        assert_ne!(4, 1_u64.get_hash(hasher()));
        assert_ne!(5, 1_u64.get_hash(hasher()));

        let xored = 0_u64.get_hash(hasher()) ^ 1_u64.get_hash(hasher());
        assert_ne!(0, xored);
        assert_ne!(1, xored);
        assert_ne!(2, xored);
        assert_ne!(3, xored);
        assert_ne!(4, xored);
        assert_ne!(5, xored);
    }
}
<commit_msg>Add test for ref invariance<commit_after>#[cfg(feature = "specialize")]
use crate::HasherExt;
use core::hash::Hash;
use core::hash::Hasher;

/// Provides a way to get an optimized hasher for a given data type.
/// Rather than using a Hasher generically which can hash any value, this provides a way to get a specialized hash
/// for a specific type. So this may be faster for primitive types. It does however consume the hasher in the process.
/// # Example
/// ```
/// use std::hash::BuildHasher;
/// use ahash::RandomState;
/// use ahash::CallHasher;
///
/// let hash_builder = RandomState::new();
/// //...
/// let value = 17;
/// let hash = value.get_hash(hash_builder.build_hasher());
/// ```
pub trait CallHasher: Hash {
    fn get_hash<H: Hasher>(&self, hasher: H) -> u64;
}

#[cfg(not(feature = "specialize"))]
impl<T> CallHasher for T
where
    T: Hash,
{
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        self.hash(&mut hasher);
        hasher.finish()
    }
}

#[cfg(feature = "specialize")]
impl<T> CallHasher for T
where
    T: Hash,
{
    #[inline]
    default fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        self.hash(&mut hasher);
        hasher.finish()
    }
}

macro_rules! call_hasher_impl {
    ($typ:ty) => {
        #[cfg(feature = "specialize")]
        impl CallHasher for $typ {
            #[inline]
            fn get_hash<H: Hasher>(&self, hasher: H) -> u64 {
                hasher.hash_u64(*self as u64)
            }
        }
        #[cfg(feature = "specialize")]
        impl CallHasher for &$typ {
            #[inline]
            fn get_hash<H: Hasher>(&self, hasher: H) -> u64 {
                hasher.hash_u64(**self as u64)
            }
        }
    };
}
call_hasher_impl!(u8);
call_hasher_impl!(u16);
call_hasher_impl!(u32);
call_hasher_impl!(u64);
call_hasher_impl!(i8);
call_hasher_impl!(i16);
call_hasher_impl!(i32);
call_hasher_impl!(i64);

#[cfg(feature = "specialize")]
impl CallHasher for u128 {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write_u128(*self);
        hasher.short_finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for i128 {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write_u128(*self as u128);
        hasher.short_finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for [u8] {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self);
        hasher.finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for &[u8] {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self);
        hasher.finish()
    }
}

#[cfg(all(feature = "specialize", feature = "std"))]
impl CallHasher for Vec<u8> {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self);
        hasher.finish()
    }
}

#[cfg(all(feature = "specialize", feature = "std"))]
impl CallHasher for &Vec<u8> {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self);
        hasher.finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for str {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self.as_bytes());
        hasher.finish()
    }
}

#[cfg(feature = "specialize")]
impl CallHasher for &str {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self.as_bytes());
        hasher.finish()
    }
}

#[cfg(all(feature = "specialize", feature = "std"))]
impl CallHasher for String {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self.as_bytes());
        hasher.finish()
    }
}

#[cfg(all(feature = "specialize", feature = "std"))]
impl CallHasher for &String {
    #[inline]
    fn get_hash<H: Hasher>(&self, mut hasher: H) -> u64 {
        hasher.write(self.as_bytes());
        hasher.finish()
    }
}

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

    #[test]
    #[cfg(feature = "specialize")]
    pub fn test_specialized_invoked() {
        let shortened = 0_u64.get_hash(AHasher::new_with_keys(1, 2));
        let mut hasher = AHasher::new_with_keys(1, 2);
        0_u64.hash(&mut hasher);
        assert_ne!(hasher.finish(), shortened);
    }

    /// Tests that some non-trivial transformation takes place.
    #[test]
    pub fn test_input_processed() {
        let hasher = || AHasher::new_with_keys(3, 2);
        assert_ne!(0, 0_u64.get_hash(hasher()));
        assert_ne!(1, 0_u64.get_hash(hasher()));
        assert_ne!(2, 0_u64.get_hash(hasher()));
        assert_ne!(3, 0_u64.get_hash(hasher()));
        assert_ne!(4, 0_u64.get_hash(hasher()));
        assert_ne!(5, 0_u64.get_hash(hasher()));

        assert_ne!(0, 1_u64.get_hash(hasher()));
        assert_ne!(1, 1_u64.get_hash(hasher()));
        assert_ne!(2, 1_u64.get_hash(hasher()));
        assert_ne!(3, 1_u64.get_hash(hasher()));
        assert_ne!(4, 1_u64.get_hash(hasher()));
        assert_ne!(5, 1_u64.get_hash(hasher()));

        let xored = 0_u64.get_hash(hasher()) ^ 1_u64.get_hash(hasher());
        assert_ne!(0, xored);
        assert_ne!(1, xored);
        assert_ne!(2, xored);
        assert_ne!(3, xored);
        assert_ne!(4, xored);
        assert_ne!(5, xored);
    }

    #[test]
    pub fn test_ref_independent() {
        let hasher = || AHasher::new_with_keys(3, 2);
        assert_eq!((&1_u8).get_hash(hasher()), 1_u8.get_hash(hasher()));
        assert_eq!((&2_u16).get_hash(hasher()), 2_u16.get_hash(hasher()));
        assert_eq!((&3_u32).get_hash(hasher()), 3_u32.get_hash(hasher()));
        assert_eq!((&4_u64).get_hash(hasher()), 4_u64.get_hash(hasher()));
        assert_eq!((&5_u128).get_hash(hasher()), 5_u128.get_hash(hasher()));
        assert_eq!((&"test").get_hash(hasher()), "test".get_hash(hasher()));
        assert_eq!((&"test").get_hash(hasher()), "test".to_string().get_hash(hasher()));
        assert_eq!((&"test"[..]).get_hash(hasher()), "test".to_string().into_bytes().get_hash(hasher()));
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::{convert, result};

use super::{Error, Result, Statement};
use crate::types::{FromSql, FromSqlError, ValueRef};

/// An handle for the resulting rows of a query.
pub struct Rows<'stmt> {
    stmt: Option<&'stmt Statement<'stmt>>,
}

impl<'stmt> Rows<'stmt> {
    fn reset(&mut self) {
        if let Some(stmt) = self.stmt.take() {
            stmt.reset();
        }
    }

    /// Attempt to get the next row from the query. Returns `Some(Ok(Row))` if
    /// there is another row, `Some(Err(...))` if there was an error
    /// getting the next row, and `None` if all rows have been retrieved.
    ///
    /// ## Note
    ///
    /// This interface is not compatible with Rust's `Iterator` trait, because
    /// the lifetime of the returned row is tied to the lifetime of `self`.
    /// This is a "streaming iterator". For a more natural interface,
    /// consider using `query_map` or `query_and_then` instead, which
    /// return types that implement `Iterator`.
    #[allow(clippy::should_implement_trait)] // cannot implement Iterator
    pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
        self.stmt.and_then(|stmt| match stmt.step() {
            Ok(true) => Some(Ok(Row {
                stmt,
                phantom: PhantomData,
            })),
            Ok(false) => {
                self.reset();
                None
            }
            Err(err) => {
                self.reset();
                Some(Err(err))
            }
        })
    }
}

impl<'stmt> Rows<'stmt> {
    pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
        Rows { stmt: Some(stmt) }
    }

    pub(crate) fn get_expected_row<'a>(&'a mut self) -> Result<Row<'a, 'stmt>> {
        match self.next() {
            Some(row) => row,
            None => Err(Error::QueryReturnedNoRows),
        }
    }
}

impl<'stmt> Drop for Rows<'stmt> {
    fn drop(&mut self) {
        self.reset();
    }
}

/// An iterator over the mapped resulting rows of a query.
pub struct MappedRows<'stmt, F> {
    rows: Rows<'stmt>,
    map: F,
}

impl<'stmt, T, F> MappedRows<'stmt, F>
where
    F: FnMut(&Row<'_, '_>) -> T,
{
    pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
        MappedRows { rows, map: f }
    }
}

impl<'conn, T, F> Iterator for MappedRows<'conn, F>
where
    F: FnMut(&Row<'_, '_>) -> T,
{
    type Item = Result<T>;

    fn next(&mut self) -> Option<Result<T>> {
        let map = &mut self.map;
        self.rows
            .next()
            .map(|row_result| row_result.map(|row| (map)(&row)))
    }
}

/// An iterator over the mapped resulting rows of a query, with an Error type
/// unifying with Error.
pub struct AndThenRows<'stmt, F> {
    rows: Rows<'stmt>,
    map: F,
}

impl<'stmt, T, E, F> AndThenRows<'stmt, F>
where
    F: FnMut(&Row<'_, '_>) -> result::Result<T, E>,
{
    pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
        AndThenRows { rows, map: f }
    }
}

impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
where
    E: convert::From<Error>,
    F: FnMut(&Row<'_, '_>) -> result::Result<T, E>,
{
    type Item = result::Result<T, E>;

    fn next(&mut self) -> Option<Self::Item> {
        let map = &mut self.map;
        self.rows
            .next()
            .map(|row_result| row_result.map_err(E::from).and_then(|row| (map)(&row)))
    }
}

/// A single result row of a query.
pub struct Row<'a, 'stmt: 'a> {
    stmt: &'stmt Statement<'stmt>,
    phantom: PhantomData<&'a ()>,
}

impl<'a, 'stmt> Row<'a, 'stmt> {
    /// Get the value of a particular column of the result row.
    ///
    /// ## Failure
    ///
    /// Panics if calling `row.get_checked(idx)` would return an error,
    /// including:
    ///
    ///    * If the underlying SQLite column type is not a valid type as a
    ///      source for `T`
    ///    * If the underlying SQLite integral value is outside the range
    ///      representable by `T`
    ///    * If `idx` is outside the range of columns in the returned query
    pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
        self.get_checked(idx).unwrap()
    }

    /// Get the value of a particular column of the result row.
    ///
    /// ## Failure
    ///
    /// Returns an `Error::InvalidColumnType` if the underlying SQLite column
    /// type is not a valid type as a source for `T`.
    ///
    /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
    /// column range for this row.
    ///
    /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
    /// name for this row.
    ///
    /// If the result type is i128 (which requires the `i128_blob` feature to be
    /// enabled), and the underlying SQLite column is a blob whose size is not
    /// 16 bytes, `Error::InvalidColumnType` will also be returned.
    pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
        let idx = idx.idx(self.stmt)?;
        let value = self.stmt.value_ref(idx);
        FromSql::column_result(value).map_err(|err| match err {
            FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
            FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
            FromSqlError::Other(err) => {
                Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
            }
            #[cfg(feature = "i128_blob")]
            FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, value.data_type()),
        })
    }

    /// Get the value of a particular column of the result row as a `ValueRef`,
    /// allowing data to be read out of a row without copying.
    ///
    /// This `ValueRef` is valid only as long as this Row, which is enforced by
    /// it's lifetime. This means that while this method is completely safe,
    /// it can be somewhat difficult to use, and most callers will be better
    /// served by `get` or `get_checked`.
    ///
    /// ## Failure
    ///
    /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
    /// column range for this row.
    ///
    /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
    /// name for this row.
    pub fn get_raw_checked<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'a>> {
        let idx = idx.idx(self.stmt)?;
        // Narrowing from `ValueRef<'stmt>` (which `self.stmt.value_ref(idx)`
        // returns) to `ValueRef<'a>` is needed because it's only valid until
        // the next call to sqlite3_step.
        let val_ref = self.stmt.value_ref(idx);
        Ok(val_ref)
    }

    /// Get the value of a particular column of the result row as a `ValueRef`,
    /// allowing data to be read out of a row without copying.
    ///
    /// This `ValueRef` is valid only as long as this Row, which is enforced by
    /// it's lifetime. This means that while this method is completely safe,
    /// it can be difficult to use, and most callers will be better served by
    /// `get` or `get_checked`.
    ///
    /// ## Failure
    ///
    /// Panics if calling `row.get_raw_checked(idx)` would return an error,
    /// including:
    ///
    ///    * If `idx` is outside the range of columns in the returned query.
    ///    * If `idx` is not a valid column name for this row.
    pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'a> {
        self.get_raw_checked(idx).unwrap()
    }

    /// Return the number of columns in the current row.
    pub fn column_count(&self) -> usize {
        self.stmt.column_count()
    }
}

/// A trait implemented by types that can index into columns of a row.
pub trait RowIndex {
    /// Returns the index of the appropriate column, or `None` if no such
    /// column exists.
    fn idx(&self, stmt: &Statement<'_>) -> Result<usize>;
}

impl RowIndex for usize {
    #[inline]
    fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
        if *self >= stmt.column_count() {
            Err(Error::InvalidColumnIndex(*self))
        } else {
            Ok(*self)
        }
    }
}

impl<'a> RowIndex for &'a str {
    #[inline]
    fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
        stmt.column_index(*self)
    }
}
<commit_msg>Fix rustdoc warnings<commit_after>use std::marker::PhantomData;
use std::{convert, result};

use super::{Error, Result, Statement};
use crate::types::{FromSql, FromSqlError, ValueRef};

/// An handle for the resulting rows of a query.
pub struct Rows<'stmt> {
    stmt: Option<&'stmt Statement<'stmt>>,
}

impl<'stmt> Rows<'stmt> {
    fn reset(&mut self) {
        if let Some(stmt) = self.stmt.take() {
            stmt.reset();
        }
    }

    /// Attempt to get the next row from the query. Returns `Some(Ok(Row))` if
    /// there is another row, `Some(Err(...))` if there was an error
    /// getting the next row, and `None` if all rows have been retrieved.
    ///
    /// ## Note
    ///
    /// This interface is not compatible with Rust's `Iterator` trait, because
    /// the lifetime of the returned row is tied to the lifetime of `self`.
    /// This is a "streaming iterator". For a more natural interface,
    /// consider using `query_map` or `query_and_then` instead, which
    /// return types that implement `Iterator`.
    #[allow(clippy::should_implement_trait)] // cannot implement Iterator
    pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
        self.stmt.and_then(|stmt| match stmt.step() {
            Ok(true) => Some(Ok(Row {
                stmt,
                phantom: PhantomData,
            })),
            Ok(false) => {
                self.reset();
                None
            }
            Err(err) => {
                self.reset();
                Some(Err(err))
            }
        })
    }
}

impl<'stmt> Rows<'stmt> {
    pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
        Rows { stmt: Some(stmt) }
    }

    pub(crate) fn get_expected_row<'a>(&'a mut self) -> Result<Row<'a, 'stmt>> {
        match self.next() {
            Some(row) => row,
            None => Err(Error::QueryReturnedNoRows),
        }
    }
}

impl<'stmt> Drop for Rows<'stmt> {
    fn drop(&mut self) {
        self.reset();
    }
}

/// An iterator over the mapped resulting rows of a query.
pub struct MappedRows<'stmt, F> {
    rows: Rows<'stmt>,
    map: F,
}

impl<'stmt, T, F> MappedRows<'stmt, F>
where
    F: FnMut(&Row<'_, '_>) -> T,
{
    pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
        MappedRows { rows, map: f }
    }
}

impl<'conn, T, F> Iterator for MappedRows<'conn, F>
where
    F: FnMut(&Row<'_, '_>) -> T,
{
    type Item = Result<T>;

    fn next(&mut self) -> Option<Result<T>> {
        let map = &mut self.map;
        self.rows
            .next()
            .map(|row_result| row_result.map(|row| (map)(&row)))
    }
}

/// An iterator over the mapped resulting rows of a query, with an Error type
/// unifying with Error.
pub struct AndThenRows<'stmt, F> {
    rows: Rows<'stmt>,
    map: F,
}

impl<'stmt, T, E, F> AndThenRows<'stmt, F>
where
    F: FnMut(&Row<'_, '_>) -> result::Result<T, E>,
{
    pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
        AndThenRows { rows, map: f }
    }
}

impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
where
    E: convert::From<Error>,
    F: FnMut(&Row<'_, '_>) -> result::Result<T, E>,
{
    type Item = result::Result<T, E>;

    fn next(&mut self) -> Option<Self::Item> {
        let map = &mut self.map;
        self.rows
            .next()
            .map(|row_result| row_result.map_err(E::from).and_then(|row| (map)(&row)))
    }
}

/// A single result row of a query.
pub struct Row<'a, 'stmt: 'a> {
    stmt: &'stmt Statement<'stmt>,
    phantom: PhantomData<&'a ()>,
}

impl<'a, 'stmt> Row<'a, 'stmt> {
    /// Get the value of a particular column of the result row.
    ///
    /// ## Failure
    ///
    /// Panics if calling `row.get_checked(idx)` would return an error,
    /// including:
    ///
    /// * If the underlying SQLite column type is not a valid type as a
    ///   source for `T`
    /// * If the underlying SQLite integral value is outside the range
    ///   representable by `T`
    /// * If `idx` is outside the range of columns in the returned query
    pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
        self.get_checked(idx).unwrap()
    }

    /// Get the value of a particular column of the result row.
    ///
    /// ## Failure
    ///
    /// Returns an `Error::InvalidColumnType` if the underlying SQLite column
    /// type is not a valid type as a source for `T`.
    ///
    /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
    /// column range for this row.
    ///
    /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
    /// name for this row.
    ///
    /// If the result type is i128 (which requires the `i128_blob` feature to be
    /// enabled), and the underlying SQLite column is a blob whose size is not
    /// 16 bytes, `Error::InvalidColumnType` will also be returned.
    pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
        let idx = idx.idx(self.stmt)?;
        let value = self.stmt.value_ref(idx);
        FromSql::column_result(value).map_err(|err| match err {
            FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
            FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
            FromSqlError::Other(err) => {
                Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
            }
            #[cfg(feature = "i128_blob")]
            FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, value.data_type()),
        })
    }

    /// Get the value of a particular column of the result row as a `ValueRef`,
    /// allowing data to be read out of a row without copying.
    ///
    /// This `ValueRef` is valid only as long as this Row, which is enforced by
    /// it's lifetime. This means that while this method is completely safe,
    /// it can be somewhat difficult to use, and most callers will be better
    /// served by `get` or `get_checked`.
    ///
    /// ## Failure
    ///
    /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
    /// column range for this row.
    ///
    /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
    /// name for this row.
    pub fn get_raw_checked<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'a>> {
        let idx = idx.idx(self.stmt)?;
        // Narrowing from `ValueRef<'stmt>` (which `self.stmt.value_ref(idx)`
        // returns) to `ValueRef<'a>` is needed because it's only valid until
        // the next call to sqlite3_step.
        let val_ref = self.stmt.value_ref(idx);
        Ok(val_ref)
    }

    /// Get the value of a particular column of the result row as a `ValueRef`,
    /// allowing data to be read out of a row without copying.
    ///
    /// This `ValueRef` is valid only as long as this Row, which is enforced by
    /// it's lifetime. This means that while this method is completely safe,
    /// it can be difficult to use, and most callers will be better served by
    /// `get` or `get_checked`.
    ///
    /// ## Failure
    ///
    /// Panics if calling `row.get_raw_checked(idx)` would return an error,
    /// including:
    ///
    /// * If `idx` is outside the range of columns in the returned query.
    /// * If `idx` is not a valid column name for this row.
    pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'a> {
        self.get_raw_checked(idx).unwrap()
    }

    /// Return the number of columns in the current row.
    pub fn column_count(&self) -> usize {
        self.stmt.column_count()
    }
}

/// A trait implemented by types that can index into columns of a row.
pub trait RowIndex {
    /// Returns the index of the appropriate column, or `None` if no such
    /// column exists.
    fn idx(&self, stmt: &Statement<'_>) -> Result<usize>;
}

impl RowIndex for usize {
    #[inline]
    fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
        if *self >= stmt.column_count() {
            Err(Error::InvalidColumnIndex(*self))
        } else {
            Ok(*self)
        }
    }
}

impl<'a> RowIndex for &'a str {
    #[inline]
    fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
        stmt.column_index(*self)
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::ops::Deref;
use phf;

use util::HasChildren;
use markup::Node;
use rendering::TextureRule;
use uil_shared::asset::ImageData;
use super::Value;
use super::KwValue;
use super::Stylesheet;
use super::Unit;
use super::Rule;
use super::SelectorState;

/// List of style properties
///
/// If you do a change here, you must update STYLE_PROPERTIES
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum PropertyName {
    // Absolute positioning properties
    LEFT,
    RIGHT,
    TOP,
    BOTTOM,
    HEIGHT,
    WIDTH,
    // Margin properties
    MARGIN,
    MARGIN_LEFT,
    MARGIN_RIGHT,
    MARGIN_TOP,
    MARGIN_BOTTOM,
    // Padding properties
    PADDING,
    PADDING_LEFT,
    PADDING_RIGHT,
    PADDING_TOP,
    PADDING_BOTTOM,
    // Border properties
    BORDER,
    BORDER_LEFT,
    BORDER_RIGHT,
    BORDER_TOP,
    BORDER_BOTTOM,
    // Layout mode (absolute / rtl / ltr)
    LAYOUT_MODE,

    /// Background
    /// Possibles rules:
    /// * `"fit"` will scale the image with the node content size.
    /// * `"repeat"` won't scale the image but will repeat it
    ///
    /// In any case, the node content bounds will be the final image bounds
    BACKGROUND_IMAGE_RULE,
    /// This property can only have Value::Image.
    BACKGROUND_IMAGE,
}

pub struct StyledNode<'a> {
    pub node: &'a Node,
    property_values: HashMap<PropertyName, Value>,
    focus_property_values: HashMap<PropertyName, Value>,
    pub kids: Vec<StyledNode<'a>>,
}

// ======================================== //
//                  HELPERS                 //
// ======================================== //

// Thanks to lastest rustc nightly macros can't be
// defined at the end of the file anymore. Shame.
macro_rules! return_length_or_zero {

    ($this:ident try $prop_name:ident) => {
        return_length_or_zero!(rec $this try $prop_name else { 0f32 });
    };

    ($this:ident try $prop_name:ident else $other:ident) => {
        return_length_or_zero!(rec $this try $prop_name else {
            return_length_or_zero!(rec $this try $other else { 0f32 })
        });
    };

    (rec $this:ident try $prop_name:ident else $none_case:block) => {
        match $this.property_values.get(&$prop_name) {
            Some(v) => {
                if let Value::Length(val, Unit::Px) = *v {
                    val
                } else {
                    0f32
                }
            }
            None => $none_case
        }
    };
}

// ======================================== //
//                 INTERFACE                //
// ======================================== //

pub fn build_style_tree<'a, 'b>(node: &'a Node, stylesheet: &'b Stylesheet) -> StyledNode<'a> {
    let mut styled_node = StyledNode::<'a>::new(node);
    styled_node.set_properties(stylesheet);
    styled_node
}

impl<'a> HasChildren for StyledNode<'a> {

    fn children(&self) -> &Vec<StyledNode<'a>> {
        &self.kids
    }
}

impl<'a> StyledNode<'a> {

    fn new(node: &'a Node) -> StyledNode<'a> {
        let mut kids = Vec::with_capacity(node.children.len());
        for kid in node.children.iter() {
            kids.push(StyledNode::new(kid));
        }

        StyledNode {
            node: node,
            property_values: HashMap::new(),
            focus_property_values: HashMap::new(),
            kids: kids
        }
    }

    pub fn has_property_expand(&self, prop_name: PropertyName) -> bool {
        self.has_property_eq_kw(prop_name, KwValue::Expand)
    }

    pub fn has_property_auto(&self, prop_name: PropertyName) -> bool {
        self.has_property_eq_kw(prop_name, KwValue::Auto)
    }

    pub fn has_property_eq_kw(&self, prop_name: PropertyName, kw: KwValue) -> bool {
        match self.property_values.get(&prop_name) {
            Some(&Value::Keyword(v)) if v == kw => {
                true
            },
            _ => false
        }
    }

    pub fn get_background_rule(&self) -> Option<TextureRule> {
        match self.property_values.get(&PropertyName::BACKGROUND_IMAGE_RULE) {
            Some(&Value::Keyword(v)) => match v {
                KwValue::Fit => Some(TextureRule::Fit),
                KwValue::Repeat => Some(TextureRule::Repeat),
                _ => None
            },
            _ => None
        }
    }

    pub fn get_background_image(&self) -> Option<ImageData> {
        match self.property_values.get(&PropertyName::BACKGROUND_IMAGE) {
            Some(&Value::Image(ref id)) => Some(id.clone()),
            _ => None
        }
    }

    pub fn size_prop_as_opt(&self, prop_name: PropertyName) -> Option<f32> {
        match self.property_values.get(&prop_name) {
            Some(v) => {
                if let Value::Length(val, Unit::Px) = *v {
                    Some(val)
                } else {
                    None
                }
            }
            None => None
        }
    }

    pub fn size_of_prop(&self, prop_name: PropertyName) -> f32 {
        use self::PropertyName::MARGIN;
        use self::PropertyName::PADDING;
        use self::PropertyName::BORDER;

        match prop_name {
            PropertyName::LEFT
            | PropertyName::RIGHT
            | PropertyName::TOP
            | PropertyName::BOTTOM
            | PropertyName::HEIGHT
            | PropertyName::WIDTH => {
                return_length_or_zero!(self try prop_name)
            }
            PropertyName::MARGIN_LEFT
            | PropertyName::MARGIN_RIGHT
            | PropertyName::MARGIN_TOP
            | PropertyName::MARGIN_BOTTOM => {
                return_length_or_zero!(self try prop_name else MARGIN)
            }
            PropertyName::PADDING_LEFT
            | PropertyName::PADDING_RIGHT
            | PropertyName::PADDING_TOP
            | PropertyName::PADDING_BOTTOM => {
                return_length_or_zero!(self try prop_name else PADDING)
            }
            PropertyName::BORDER_LEFT
            | PropertyName::BORDER_RIGHT
            | PropertyName::BORDER_TOP
            | PropertyName::BORDER_BOTTOM => {
                return_length_or_zero!(self try prop_name else BORDER)
            }
            _ => panic!()
        }
    }

    pub fn tree_size(&self) -> usize {
        let mut count = 1;

        for kid in &self.kids {
            count += kid.tree_size();
        }

        count
    }

    fn set_properties(&mut self, style: &Stylesheet) {
        let classes = self.node.classes();
        let ref mut properties = self.property_values;
        let ref mut focus_properties = self.focus_property_values;
        // We loop over rules because at some
        // point, we might want to sort them based
        // on specificity in the same way that it is done
        // in CSS. It would help understanding which
        // rule does define a particular property.
        // Thus the code below wouldn't change.
        for rule in style.rules.iter() {
            // FIXME:
            // using deref instead of as_ref because
            // of an inference problem.
            if classes.contains(rule.selector.name.deref()) {
                // Loop over declaration in the rule.
                // If some properties are declared multiple times
                // the order matters here.
                match rule.selector.state {
                    SelectorState::Focus => {
                        StyledNode::set_properties_for_hashmap(properties, rule);
                    }
                    SelectorState::Default => {
                        StyledNode::set_properties_for_hashmap(focus_properties, rule);
                    }
                }
            }
        }

        for kid in self.kids.iter_mut() {
            kid.set_properties(style);
        }
    }

    fn set_properties_for_hashmap(
        properties: &mut HashMap<PropertyName, Value>,
        rule: &Rule)
    {
        for dec in rule.declarations.iter() {

            if let Some(property) = STYLE_PROPERTIES.get(dec.name.deref()) {

                properties.insert(*property, dec.value.clone());
            }
        }
    }
}

static STYLE_PROPERTIES: phf::Map<&'static str, PropertyName> = phf_map! {
    // Absolute positioning properties
    "left" => PropertyName::LEFT,
    "right" => PropertyName::RIGHT,
    "top" => PropertyName::TOP,
    "bottom" => PropertyName::BOTTOM,
    "height" => PropertyName::HEIGHT,
    "width" => PropertyName::WIDTH,
    // Margin properties
    "margin" => PropertyName::MARGIN,
    "margin-left" => PropertyName::MARGIN_LEFT,
    "margin-right" => PropertyName::MARGIN_RIGHT,
    "margin-top" => PropertyName::MARGIN_TOP,
    "margin-bottom" => PropertyName::MARGIN_BOTTOM,
    // Padding properties
    "padding" => PropertyName::PADDING,
    "padding-left" => PropertyName::PADDING_LEFT,
    "padding-right" => PropertyName::PADDING_RIGHT,
    "padding-top" => PropertyName::PADDING_TOP,
    "padding-bottom" => PropertyName::PADDING_BOTTOM,
    // Border properties
    "border" => PropertyName::BORDER,
    "border-left" => PropertyName::BORDER_LEFT,
    "border-right" => PropertyName::BORDER_RIGHT,
    "border-top" => PropertyName::BORDER_TOP,
    "border-bottom" => PropertyName::BORDER_BOTTOM,
    // Layout mode (absolute / rtl / ltr)
    "layout" => PropertyName::LAYOUT_MODE,
    // Background image
    "background-image" => PropertyName::BACKGROUND_IMAGE,
    "background-image-rule" => PropertyName::BACKGROUND_IMAGE_RULE,
};
<commit_msg>Fixed state bug from 517925 commit.<commit_after>use std::collections::HashMap;
use std::ops::Deref;
use phf;

use util::HasChildren;
use markup::Node;
use rendering::TextureRule;
use uil_shared::asset::ImageData;
use super::Value;
use super::KwValue;
use super::Stylesheet;
use super::Unit;
use super::Rule;
use super::SelectorState;

/// List of style properties
///
/// If you do a change here, you must update STYLE_PROPERTIES
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum PropertyName {
    // Absolute positioning properties
    LEFT,
    RIGHT,
    TOP,
    BOTTOM,
    HEIGHT,
    WIDTH,
    // Margin properties
    MARGIN,
    MARGIN_LEFT,
    MARGIN_RIGHT,
    MARGIN_TOP,
    MARGIN_BOTTOM,
    // Padding properties
    PADDING,
    PADDING_LEFT,
    PADDING_RIGHT,
    PADDING_TOP,
    PADDING_BOTTOM,
    // Border properties
    BORDER,
    BORDER_LEFT,
    BORDER_RIGHT,
    BORDER_TOP,
    BORDER_BOTTOM,
    // Layout mode (absolute / rtl / ltr)
    LAYOUT_MODE,

    /// Background
    /// Possibles rules:
    /// * `"fit"` will scale the image with the node content size.
    /// * `"repeat"` won't scale the image but will repeat it
    ///
    /// In any case, the node content bounds will be the final image bounds
    BACKGROUND_IMAGE_RULE,
    /// This property can only have Value::Image.
    BACKGROUND_IMAGE,
}

pub struct StyledNode<'a> {
    pub node: &'a Node,
    property_values: HashMap<PropertyName, Value>,
    focus_property_values: HashMap<PropertyName, Value>,
    pub kids: Vec<StyledNode<'a>>,
}

// ======================================== //
//                  HELPERS                 //
// ======================================== //

// Thanks to lastest rustc nightly macros can't be
// defined at the end of the file anymore. Shame.
macro_rules! return_length_or_zero {

    ($this:ident try $prop_name:ident) => {
        return_length_or_zero!(rec $this try $prop_name else { 0f32 });
    };

    ($this:ident try $prop_name:ident else $other:ident) => {
        return_length_or_zero!(rec $this try $prop_name else {
            return_length_or_zero!(rec $this try $other else { 0f32 })
        });
    };

    (rec $this:ident try $prop_name:ident else $none_case:block) => {
        match $this.property_values.get(&$prop_name) {
            Some(v) => {
                if let Value::Length(val, Unit::Px) = *v {
                    val
                } else {
                    0f32
                }
            }
            None => $none_case
        }
    };
}

// ======================================== //
//                 INTERFACE                //
// ======================================== //

pub fn build_style_tree<'a, 'b>(node: &'a Node, stylesheet: &'b Stylesheet) -> StyledNode<'a> {
    let mut styled_node = StyledNode::<'a>::new(node);
    styled_node.set_properties(stylesheet);
    styled_node
}

impl<'a> HasChildren for StyledNode<'a> {

    fn children(&self) -> &Vec<StyledNode<'a>> {
        &self.kids
    }
}

impl<'a> StyledNode<'a> {

    fn new(node: &'a Node) -> StyledNode<'a> {
        let mut kids = Vec::with_capacity(node.children.len());
        for kid in node.children.iter() {
            kids.push(StyledNode::new(kid));
        }

        StyledNode {
            node: node,
            property_values: HashMap::new(),
            focus_property_values: HashMap::new(),
            kids: kids
        }
    }

    pub fn has_property_expand(&self, prop_name: PropertyName) -> bool {
        self.has_property_eq_kw(prop_name, KwValue::Expand)
    }

    pub fn has_property_auto(&self, prop_name: PropertyName) -> bool {
        self.has_property_eq_kw(prop_name, KwValue::Auto)
    }

    pub fn has_property_eq_kw(&self, prop_name: PropertyName, kw: KwValue) -> bool {
        match self.property_values.get(&prop_name) {
            Some(&Value::Keyword(v)) if v == kw => {
                true
            },
            _ => false
        }
    }

    pub fn get_background_rule(&self) -> Option<TextureRule> {
        match self.property_values.get(&PropertyName::BACKGROUND_IMAGE_RULE) {
            Some(&Value::Keyword(v)) => match v {
                KwValue::Fit => Some(TextureRule::Fit),
                KwValue::Repeat => Some(TextureRule::Repeat),
                _ => None
            },
            _ => None
        }
    }

    pub fn get_background_image(&self) -> Option<ImageData> {
        match self.property_values.get(&PropertyName::BACKGROUND_IMAGE) {
            Some(&Value::Image(ref id)) => Some(id.clone()),
            _ => None
        }
    }

    pub fn size_prop_as_opt(&self, prop_name: PropertyName) -> Option<f32> {
        match self.property_values.get(&prop_name) {
            Some(v) => {
                if let Value::Length(val, Unit::Px) = *v {
                    Some(val)
                } else {
                    None
                }
            }
            None => None
        }
    }

    pub fn size_of_prop(&self, prop_name: PropertyName) -> f32 {
        use self::PropertyName::MARGIN;
        use self::PropertyName::PADDING;
        use self::PropertyName::BORDER;

        match prop_name {
            PropertyName::LEFT
            | PropertyName::RIGHT
            | PropertyName::TOP
            | PropertyName::BOTTOM
            | PropertyName::HEIGHT
            | PropertyName::WIDTH => {
                return_length_or_zero!(self try prop_name)
            }
            PropertyName::MARGIN_LEFT
            | PropertyName::MARGIN_RIGHT
            | PropertyName::MARGIN_TOP
            | PropertyName::MARGIN_BOTTOM => {
                return_length_or_zero!(self try prop_name else MARGIN)
            }
            PropertyName::PADDING_LEFT
            | PropertyName::PADDING_RIGHT
            | PropertyName::PADDING_TOP
            | PropertyName::PADDING_BOTTOM => {
                return_length_or_zero!(self try prop_name else PADDING)
            }
            PropertyName::BORDER_LEFT
            | PropertyName::BORDER_RIGHT
            | PropertyName::BORDER_TOP
            | PropertyName::BORDER_BOTTOM => {
                return_length_or_zero!(self try prop_name else BORDER)
            }
            _ => panic!()
        }
    }

    pub fn tree_size(&self) -> usize {
        let mut count = 1;

        for kid in &self.kids {
            count += kid.tree_size();
        }

        count
    }

    fn set_properties(&mut self, style: &Stylesheet) {
        let classes = self.node.classes();
        let ref mut properties = self.property_values;
        let ref mut focus_properties = self.focus_property_values;
        // We loop over rules because at some
        // point, we might want to sort them based
        // on specificity in the same way that it is done
        // in CSS. It would help understanding which
        // rule does define a particular property.
        // Thus the code below wouldn't change.
        for rule in style.rules.iter() {
            // FIXME:
            // using deref instead of as_ref because
            // of an inference problem.
            if classes.contains(rule.selector.name.deref()) {
                // Loop over declaration in the rule.
                // If some properties are declared multiple times
                // the order matters here.
                match rule.selector.state {
                    SelectorState::Focus => {
                        StyledNode::set_properties_for_hashmap(focus_properties, rule);
                    }
                    SelectorState::Default => {
                        StyledNode::set_properties_for_hashmap(properties, rule);
                    }
                }
            }
        }

        for kid in self.kids.iter_mut() {
            kid.set_properties(style);
        }
    }

    fn set_properties_for_hashmap(
        properties: &mut HashMap<PropertyName, Value>,
        rule: &Rule)
    {
        for dec in rule.declarations.iter() {

            if let Some(property) = STYLE_PROPERTIES.get(dec.name.deref()) {

                properties.insert(*property, dec.value.clone());
            }
        }
    }
}

static STYLE_PROPERTIES: phf::Map<&'static str, PropertyName> = phf_map! {
    // Absolute positioning properties
    "left" => PropertyName::LEFT,
    "right" => PropertyName::RIGHT,
    "top" => PropertyName::TOP,
    "bottom" => PropertyName::BOTTOM,
    "height" => PropertyName::HEIGHT,
    "width" => PropertyName::WIDTH,
    // Margin properties
    "margin" => PropertyName::MARGIN,
    "margin-left" => PropertyName::MARGIN_LEFT,
    "margin-right" => PropertyName::MARGIN_RIGHT,
    "margin-top" => PropertyName::MARGIN_TOP,
    "margin-bottom" => PropertyName::MARGIN_BOTTOM,
    // Padding properties
    "padding" => PropertyName::PADDING,
    "padding-left" => PropertyName::PADDING_LEFT,
    "padding-right" => PropertyName::PADDING_RIGHT,
    "padding-top" => PropertyName::PADDING_TOP,
    "padding-bottom" => PropertyName::PADDING_BOTTOM,
    // Border properties
    "border" => PropertyName::BORDER,
    "border-left" => PropertyName::BORDER_LEFT,
    "border-right" => PropertyName::BORDER_RIGHT,
    "border-top" => PropertyName::BORDER_TOP,
    "border-bottom" => PropertyName::BORDER_BOTTOM,
    // Layout mode (absolute / rtl / ltr)
    "layout" => PropertyName::LAYOUT_MODE,
    // Background image
    "background-image" => PropertyName::BACKGROUND_IMAGE,
    "background-image-rule" => PropertyName::BACKGROUND_IMAGE_RULE,
};
<|endoftext|>


<commit_before>/// The Toml Set extensions

use toml::Value;

use tokenizer::tokenize_with_seperator;
use tokenizer::Token;
use error::*;

pub trait TomlValueSetExt<'doc> {

    /// Extension function for setting a value in the current toml::Value document
    /// using a custom seperator
    ///
    /// # Semantics
    ///
    /// The function _never_ creates intermediate data structures (Tables or Arrays) in the
    /// document.
    ///
    /// # Return value
    ///
    /// * If the set operation worked correctly, `Ok(None)` is returned.
    /// * If the set operation replaced an existing value `Ok(Some(old_value))` is returned
    /// * On failure, `Err(e)` is returned:
    ///     * If the query is `"a.b.c"` but there is no table `"b"`: error
    ///     * If the query is `"a.b.[0]"` but "`b"` is not an array: error
    ///     * If the query is `"a.b.[3]"` but the array at "`b"` has no index `3`: error
    ///     * etc.
    ///
    fn set_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>>;

    /// Extension function for setting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueSetExt::set_with_seperator`
    fn set(&mut self, query: &String, value: Value) -> Result<Option<Value>> {
        self.set_with_seperator(query, '.', value)
    }

}

impl<'doc> TomlValueSetExt<'doc> for Value {

    fn set_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last = tokens.pop_last();
        let last = last.unwrap();

        let mut val = try!(resolve(self, &tokens));

        match *last {
            Token::Identifier { ident, .. } => {
                match val {
                    &mut Value::Table(ref mut t) => {
                        Ok(t.insert(ident, value))
                    }
                    _ => unimplemented!()
                }
            }

            Token::Index { idx, .. } => {
                match val {
                    &mut Value::Array(ref mut a) => {
                        if a.len() > idx {
                            let result = a.swap_remove(idx);
                            a.insert(idx, value);
                            Ok(Some(result))
                        } else {
                            a.push(value);
                            Ok(None)
                        }
                    }
                    _ => unimplemented!()
                }
            }

        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_set_with_seperator_into_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        a = 0
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Table(_)));
                match inner {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_key_nonexistent() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());
        let res = res.unwrap();

        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Table(_)));
                match inner {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_array() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = [ 0 ]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Array(_)));
                match inner {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_index_nonexistent() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Array(_)));
                match inner {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nested_table() {
        let mut toml : Value = toml_from_str(r#"
        [a.b.c]
        d = 0
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let a = t.get("a");
                assert!(a.is_some());

                let a = a.unwrap();
                assert!(is_match!(a, &Value::Table(_)));
                match a {
                    &Value::Table(ref a) => {
                        assert!(!a.is_empty());

                        let b_tab = a.get("b");
                        assert!(b_tab.is_some());

                        let b_tab = b_tab.unwrap();
                        assert!(is_match!(b_tab, &Value::Table(_)));
                        match b_tab {
                            &Value::Table(ref b) => {
                                assert!(!b.is_empty());

                                let c_tab = b.get("c");
                                assert!(c_tab.is_some());

                                let c_tab = c_tab.unwrap();
                                assert!(is_match!(c_tab, &Value::Table(_)));
                                match c_tab {
                                    &Value::Table(ref c) => {
                                        assert!(!c.is_empty());

                                        let d = c.get("d");
                                        assert!(d.is_some());

                                        let d = d.unwrap();
                                        assert!(is_match!(d, &Value::Integer(1)));
                                    },
                                    _ => panic!("What just happenend?"),
                                }
                            },
                            _ => panic!("What just happenend?"),
                        }
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_table() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::IdentifierNotFoundInDocument(_)));
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_array() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("[0]"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::NoIndexInTable(0)));
    }

}

<commit_msg>Impl Value::set_with_seperator to fix test_set_with_seperator_into_nonexistent_table<commit_after>/// The Toml Set extensions

use toml::Value;

use tokenizer::tokenize_with_seperator;
use tokenizer::Token;
use error::*;

pub trait TomlValueSetExt<'doc> {

    /// Extension function for setting a value in the current toml::Value document
    /// using a custom seperator
    ///
    /// # Semantics
    ///
    /// The function _never_ creates intermediate data structures (Tables or Arrays) in the
    /// document.
    ///
    /// # Return value
    ///
    /// * If the set operation worked correctly, `Ok(None)` is returned.
    /// * If the set operation replaced an existing value `Ok(Some(old_value))` is returned
    /// * On failure, `Err(e)` is returned:
    ///     * If the query is `"a.b.c"` but there is no table `"b"`: error
    ///     * If the query is `"a.b.[0]"` but "`b"` is not an array: error
    ///     * If the query is `"a.b.[3]"` but the array at "`b"` has no index `3`: error
    ///     * etc.
    ///
    fn set_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>>;

    /// Extension function for setting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueSetExt::set_with_seperator`
    fn set(&mut self, query: &String, value: Value) -> Result<Option<Value>> {
        self.set_with_seperator(query, '.', value)
    }

}

impl<'doc> TomlValueSetExt<'doc> for Value {

    fn set_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last = tokens.pop_last();

        let mut val = try!(resolve(self, &tokens));
        let last = last.unwrap_or_else(|| Box::new(tokens));

        match *last {
            Token::Identifier { ident, .. } => {
                match val {
                    &mut Value::Table(ref mut t) => {
                        Ok(t.insert(ident, value))
                    }
                    _ => unimplemented!()
                }
            }

            Token::Index { idx, .. } => {
                match val {
                    &mut Value::Array(ref mut a) => {
                        if a.len() > idx {
                            let result = a.swap_remove(idx);
                            a.insert(idx, value);
                            Ok(Some(result))
                        } else {
                            a.push(value);
                            Ok(None)
                        }
                    }
                    _ => unimplemented!()
                }
            }

        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_set_with_seperator_into_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        a = 0
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Table(_)));
                match inner {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_key_nonexistent() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());
        let res = res.unwrap();

        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Table(_)));
                match inner {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_array() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = [ 0 ]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Array(_)));
                match inner {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_index_nonexistent() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Array(_)));
                match inner {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nested_table() {
        let mut toml : Value = toml_from_str(r#"
        [a.b.c]
        d = 0
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let a = t.get("a");
                assert!(a.is_some());

                let a = a.unwrap();
                assert!(is_match!(a, &Value::Table(_)));
                match a {
                    &Value::Table(ref a) => {
                        assert!(!a.is_empty());

                        let b_tab = a.get("b");
                        assert!(b_tab.is_some());

                        let b_tab = b_tab.unwrap();
                        assert!(is_match!(b_tab, &Value::Table(_)));
                        match b_tab {
                            &Value::Table(ref b) => {
                                assert!(!b.is_empty());

                                let c_tab = b.get("c");
                                assert!(c_tab.is_some());

                                let c_tab = c_tab.unwrap();
                                assert!(is_match!(c_tab, &Value::Table(_)));
                                match c_tab {
                                    &Value::Table(ref c) => {
                                        assert!(!c.is_empty());

                                        let d = c.get("d");
                                        assert!(d.is_some());

                                        let d = d.unwrap();
                                        assert!(is_match!(d, &Value::Integer(1)));
                                    },
                                    _ => panic!("What just happenend?"),
                                }
                            },
                            _ => panic!("What just happenend?"),
                        }
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_table() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::IdentifierNotFoundInDocument(_)));
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_array() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("[0]"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::NoIndexInTable(0)));
    }

}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::atomic::{Ordering};
use std::fmt::Write;

use syntex_syntax::util::small_vector::SmallVector;
use syntex_syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntex_syntax::{parse, ast};
use syntex_syntax::print::pprust;

use core::*;
use jni::generate_func_name as jni_generate_func_name;
use COMMON_CODE_GENERATED;

pub static RUST_OBJECT_TO_JOBJECT: &'static str = r#"
  let class_id = ::std::ffi::CString::new("{full_class_name}").unwrap();
  let jcls: jclass = unsafe { (**env).FindClass.unwrap()(env, class_id.as_ptr()) };
  //TODO: check exception and return values
  let jobj: jobject = unsafe { (**env).AllocObject.unwrap()(env, jcls) };
  //TODO: check exception and return values
  let field_id = ::std::ffi::CString::new("mNativeObj").unwrap();
  //TODO: check exception and return values
  let type_id = ::std::ffi::CString::new("J").unwrap();
//TODO: check exception and return values
  let field_id: jfieldID = unsafe { (**env).GetFieldID.unwrap()(env, jcls, field_id.as_ptr(), type_id.as_ptr()) };
//TODO: check exception and return values
  let ret = Box::into_raw(Box::new(ret)) as jlong;
  unsafe { (**env).SetLongField.unwrap()(env, jobj, field_id, ret) };
  //TODO: check exception and return values
  let ret = jobj;
"#;

fn jni_func_args_for_decl(rust_java_types_map: &RustToJavaTypes, method: &ForeignerMethod, skip: usize) -> String {
    let mut buf = String::new();
    for (i, it) in method.in_out_type.inputs.iter().skip(skip).enumerate() {
        let type_name = pprust::ty_to_string(&*it.ty);
        let type_name = rust_java_types_map.get(type_name.as_str()).unwrap().jni_type_name;
        write!(&mut buf, "a_{}: {}, ", i, type_name).unwrap();
    }
    buf
}

fn jni_convert_args(rust_java_types_map: &RustToJavaTypes, method: &ForeignerMethod, skip_args: usize) -> String {
    let mut buf = String::new();
    for (i, it) in method.in_out_type.inputs.iter().skip(skip_args).enumerate() {
        let type_name = pprust::ty_to_string(&*it.ty);
        let th: &TypeHandler = get_type_handler(rust_java_types_map, type_name.as_str());
        if let Some(ref from_jni_converter) = th.from_jni_converter {
            write!(&mut buf, "{}", from_jni_converter.apply(&format!("a_{}", i))).unwrap();
        }
    }
    buf
}

fn jni_result_type(rust_java_types_map: &RustToJavaTypes, method: &ForeignerMethod) -> String {
    match &method.in_out_type.output {
        &ast::FunctionRetTy::Default(_) => String::new(),
        &ast::FunctionRetTy::Ty(ref ret_type) =>
            format!("-> {}", get_type_handler(rust_java_types_map, pprust::ty_to_string(&*ret_type).as_str()).jni_type_name),
    }
}

fn jni_convert_output_type<'a> (rust_java_types_map: &'a RustToJavaTypes, method: &'a ForeignerMethod) -> &'a str {
    match &method.in_out_type.output {
        &ast::FunctionRetTy::Default(_) => "",
        &ast::FunctionRetTy::Ty(ref ret_type) => {
            let type_name = pprust::ty_to_string(&*ret_type);
            let th: &TypeHandler = get_type_handler(rust_java_types_map, type_name.as_str());
            if let Some(ref to_jni_converter) = th.to_jni_converter {
                to_jni_converter.apply()
            } else {
                ""
            }
        }
    }
}

fn jni_convert_this_in_constructor(constructor_ret_type: &str, rust_self_type: &str) -> String {
    if constructor_ret_type.starts_with(&format!("Result<{},", rust_self_type)) {
        format!(r#"
  let this = match this {{
    Ok(val) => val,
    Err(msg) => {{
      jni_throw_exception(env, &msg);
      return 0;
    }}
  }};
"#)
    } else {
        String::new()
    }
}

fn generate_rust_to_convert_this(constructor_ret_type: &str, rust_self_type: &str) -> String {
    if constructor_ret_type == rust_self_type {
        return String::new();
    }
    if format!("Arc<Mutex<{}>>", rust_self_type) == constructor_ret_type {
        return format!("let mut this = this.lock().unwrap();\n");
    } else if format!("Rc<RefCell<{}>>", rust_self_type) == constructor_ret_type {
        return format!("let mut this = this.borrow_mut();\n");
    } else {
        unimplemented!();
    }
}

pub fn generate_rust_code<'cx>(cx: &'cx mut ExtCtxt, rust_java_types_map: &RustToJavaTypes, class_info: &ForeignerClassInfo) -> Box<MacResult> {
    let mut jni_methods = Vec::new();
    let constructor = class_info.methods.iter().find(|&x| if let FuncVariant::Constructor = x.func_type { true } else { false });
    let constructor_ret_type = class_info.constructor_ret_type.as_ref();

    let constructor_ret_type_for_method = class_info.this_type_for_method.as_ref();

    let mut generated_func_names = HashMap::<String, usize>::new();
    for it in class_info.methods.iter() {
        let val_ref = generated_func_names.entry(match it.func_type {
            FuncVariant::StaticMethod => unimplemented!(),
            FuncVariant::Constructor => {
                "init".to_string()
            }
            FuncVariant::Method => {
                format!("do_{}", it.short_name())
            }
        });
        *val_ref.or_insert(0) += 1;
    }
    for it in class_info.methods.iter() {
        let java_input_args_iter = it.in_out_type.inputs
            .iter()
            .skip(if it.func_type == FuncVariant::Method { 1 } else { 0 })
            .map(|v| &get_type_handler(rust_java_types_map, &pprust::ty_to_string(&*v.ty).as_str()).java_type_name);
        match it.func_type {
            FuncVariant::StaticMethod => unimplemented!(),
            FuncVariant::Constructor => {
                let constructor_ret_type = constructor_ret_type.as_ref().unwrap().clone();
                let mut code = String::new();
                write!(&mut code, r#"
#[allow(non_snake_case)]
#[no_mangle]
#[allow(unused_variables)]
pub fn {func_name}(env: *mut JNIEnv, _: jclass, {decl_func_args}) -> jlong {{
{convert_jni_args}
  let this = {create_jni_obj}({jni_func_args});
{convert_this}
  Box::into_raw(Box::new(this)) as jlong
}}
"#,
                       func_name = jni_generate_func_name(
                           &class_info.package_name, &class_info.class_name, "init",
                           *generated_func_names.get("init").unwrap() > 1,
                           java_input_args_iter
                       ),
                       convert_this = jni_convert_this_in_constructor(&pprust::ty_to_string(&constructor_ret_type), &class_info.self_rust_type.to_string()),
                       decl_func_args = jni_func_args_for_decl(rust_java_types_map, &*it, 0),
                       convert_jni_args = jni_convert_args(rust_java_types_map, &*it, 0),
                       create_jni_obj = it.full_name(),
                       jni_func_args = it.in_out_type.inputs.iter().enumerate().map(|a| format!("a_{}, ", a.0))
                       .fold(String::new(), |acc, x| acc + &x),
                ).unwrap();
                debug!("we generate and parse code: {}", code);
                jni_methods.push(parse::parse_item_from_source_str(
                    "constructor".to_string(), code, cx.cfg.clone(), cx.parse_sess).unwrap().unwrap());
            }
            FuncVariant::Method => {
                if constructor.is_none() {
                    panic!("package_name {}, class_name {}, have methods, but no constructor",
                           class_info.package_name, class_info.class_name);
                }
                if class_info.self_rust_type.segments.is_empty() {
                    panic!("package_name {}, class_name {} have constructor, but self_type not defined",
                           class_info.package_name, class_info.class_name);
                }
                let constructor_ret_type = constructor_ret_type_for_method.as_ref().unwrap().clone();

                let mut code = String::new();
                let gen_func_name = format!("do_{}", &it.short_name());
                write!(&mut code, r#"
#[allow(non_snake_case)]
#[no_mangle]
#[allow(unused_variables)]
pub fn {func_name}(env: *mut JNIEnv, _: jclass, this: jlong, {decl_func_args}) {jni_result_type} {{
{convert_jni_args}
    let this: &mut {this_type} = unsafe {{ jlong_to_pointer::<{this_type}>(this).as_mut().unwrap() }};
{convert_this}
    let ret = {rust_func_name}(&mut *this, {jni_func_args});
    {convert_output}
    ret
}}
"#,
                       func_name = jni_generate_func_name(&class_info.package_name, &class_info.class_name, &gen_func_name,
                                                           *generated_func_names.get(&gen_func_name).unwrap() > 1,
                                                           java_input_args_iter),
                       decl_func_args = jni_func_args_for_decl(rust_java_types_map, &*it, 1),
                       convert_jni_args = jni_convert_args(rust_java_types_map, &*it, 1),
                       jni_result_type = jni_result_type(rust_java_types_map, &*it),
                       this_type = pprust::ty_to_string(&constructor_ret_type),
                       convert_this = generate_rust_to_convert_this(&pprust::ty_to_string(&constructor_ret_type), &class_info.self_rust_type.to_string()),
                       rust_func_name = it.path,
                       jni_func_args = it.in_out_type.inputs.iter().skip(1).enumerate().map(|a| format!("a_{}, ", a.0))
                       .fold(String::new(), |acc, x| acc + &x),
                       convert_output = jni_convert_output_type(rust_java_types_map, &*it),
                ).unwrap();
                debug!("we generate and parse code: {}", code);
                jni_methods.push(parse::parse_item_from_source_str(
                    "method".to_string(), code, cx.cfg.clone(), cx.parse_sess).unwrap().unwrap());
            }
        }
    }

    if constructor.is_some() {
        let constructor_ret_type = constructor_ret_type_for_method.as_ref().unwrap().clone();
        let mut code = String::new();
        write!(&mut code, r#"
#[allow(non_snake_case)]
#[no_mangle]
pub fn {jni_destructor_name}(_: *mut JNIEnv, _: jclass, this: jlong) {{
    let this: &mut {type_name} = unsafe {{ jlong_to_pointer::<{type_name}>(this).as_mut().unwrap() }};
    let this = unsafe {{ Box::from_raw(this) }};
    drop(this)
}}
"#,
               jni_destructor_name = jni_generate_func_name(&class_info.package_name, &class_info.class_name,
                                                            "do_delete", false, ::std::iter::empty::<&String>()),
               type_name = pprust::ty_to_string(&constructor_ret_type),
        ).unwrap();
        debug!("we generate and parse code: {}", code);
        jni_methods.push(parse::parse_item_from_source_str(
            "destructor".to_string(), code, cx.cfg.clone(), cx.parse_sess).unwrap().unwrap());
    }

    if !COMMON_CODE_GENERATED.swap(true, Ordering::SeqCst) {
        let mut parser = parse::new_parser_from_source_str(
            cx.parse_sess, cx.cfg.clone(), "addon".to_string(),
            r#"
#[cfg(target_pointer_width = "32")]
unsafe fn jlong_to_pointer<T>(val: jlong) -> *mut T {
    mem::transmute::<u32, *mut T>(val as u32)
}

#[cfg(target_pointer_width = "64")]
unsafe fn jlong_to_pointer<T>(val: jlong) -> *mut T {
    mem::transmute::<jlong, *mut T>(val)
}

struct JavaString {
    string: jstring,
    chars: *const ::std::os::raw::c_char,
    env: *mut JNIEnv
}
impl JavaString {
    fn new(env: *mut JNIEnv, js: jstring) -> JavaString {
        let chars = unsafe { (**env).GetStringUTFChars.unwrap()(env, js, ptr::null_mut()) };
        JavaString{string: js, chars: chars, env: env}
    }

    fn to_str(&self) -> &str {
        let s = unsafe { CStr::from_ptr(self.chars) };
        s.to_str().unwrap()
    }
}
impl Drop for JavaString {
    fn drop(&mut self) {
        assert!(self.env != ptr::null_mut() && self.chars != ptr::null_mut());
        unsafe { (**self.env).ReleaseStringUTFChars.unwrap()(self.env, self.string, self.chars) };
        self.env = ptr::null_mut();
        self.chars = ptr::null_mut();
    }
}

#[allow(dead_code)]
fn jni_throw(env: *mut JNIEnv, class_name: &'static str, message: &str) {
    let class_name_c = ::std::ffi::CString::new(class_name).unwrap();

    let ex_class = unsafe {
        (**env).FindClass.unwrap()(env, class_name_c.as_ptr())
    };
    if ex_class.is_null() {
        error!("throw_exception: can not find exp class {}, msg {}", class_name, message);
        return;
    }
    let c_message = ::std::ffi::CString::new(message).unwrap();
    let res = unsafe {
        (**env).ThrowNew.unwrap()(env, ex_class, c_message.as_ptr())
    };
    if res != 0 {
        error!("ThrowNew({}) for class {} failed", message, class_name);
    }
}

#[allow(dead_code)]
fn jni_throw_exception(env: *mut JNIEnv, message: &str) {
    jni_throw(env, "java/lang/Exception", message)
}
"#.to_string());
        let mut my_crate = parser.parse_crate_mod().unwrap();
        jni_methods.append(&mut my_crate.module.items);
    }
    MacEager::items(SmallVector::many(jni_methods))
}
<commit_msg>not require addition `use` for compilation of generated code<commit_after>use std::collections::HashMap;
use std::sync::atomic::{Ordering};
use std::fmt::Write;

use syntex_syntax::util::small_vector::SmallVector;
use syntex_syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntex_syntax::{parse, ast};
use syntex_syntax::print::pprust;

use core::*;
use jni::generate_func_name as jni_generate_func_name;
use COMMON_CODE_GENERATED;

pub static RUST_OBJECT_TO_JOBJECT: &'static str = r#"
  let class_id = ::std::ffi::CString::new("{full_class_name}").unwrap();
  let jcls: jclass = unsafe { (**env).FindClass.unwrap()(env, class_id.as_ptr()) };
  //TODO: check exception and return values
  let jobj: jobject = unsafe { (**env).AllocObject.unwrap()(env, jcls) };
  //TODO: check exception and return values
  let field_id = ::std::ffi::CString::new("mNativeObj").unwrap();
  //TODO: check exception and return values
  let type_id = ::std::ffi::CString::new("J").unwrap();
//TODO: check exception and return values
  let field_id: jfieldID = unsafe { (**env).GetFieldID.unwrap()(env, jcls, field_id.as_ptr(), type_id.as_ptr()) };
//TODO: check exception and return values
  let ret = Box::into_raw(Box::new(ret)) as jlong;
  unsafe { (**env).SetLongField.unwrap()(env, jobj, field_id, ret) };
  //TODO: check exception and return values
  let ret = jobj;
"#;

fn jni_func_args_for_decl(rust_java_types_map: &RustToJavaTypes, method: &ForeignerMethod, skip: usize) -> String {
    let mut buf = String::new();
    for (i, it) in method.in_out_type.inputs.iter().skip(skip).enumerate() {
        let type_name = pprust::ty_to_string(&*it.ty);
        let type_name = rust_java_types_map.get(type_name.as_str()).unwrap().jni_type_name;
        write!(&mut buf, "a_{}: {}, ", i, type_name).unwrap();
    }
    buf
}

fn jni_convert_args(rust_java_types_map: &RustToJavaTypes, method: &ForeignerMethod, skip_args: usize) -> String {
    let mut buf = String::new();
    for (i, it) in method.in_out_type.inputs.iter().skip(skip_args).enumerate() {
        let type_name = pprust::ty_to_string(&*it.ty);
        let th: &TypeHandler = get_type_handler(rust_java_types_map, type_name.as_str());
        if let Some(ref from_jni_converter) = th.from_jni_converter {
            write!(&mut buf, "{}", from_jni_converter.apply(&format!("a_{}", i))).unwrap();
        }
    }
    buf
}

fn jni_result_type(rust_java_types_map: &RustToJavaTypes, method: &ForeignerMethod) -> String {
    match &method.in_out_type.output {
        &ast::FunctionRetTy::Default(_) => String::new(),
        &ast::FunctionRetTy::Ty(ref ret_type) =>
            format!("-> {}", get_type_handler(rust_java_types_map, pprust::ty_to_string(&*ret_type).as_str()).jni_type_name),
    }
}

fn jni_convert_output_type<'a> (rust_java_types_map: &'a RustToJavaTypes, method: &'a ForeignerMethod) -> &'a str {
    match &method.in_out_type.output {
        &ast::FunctionRetTy::Default(_) => "",
        &ast::FunctionRetTy::Ty(ref ret_type) => {
            let type_name = pprust::ty_to_string(&*ret_type);
            let th: &TypeHandler = get_type_handler(rust_java_types_map, type_name.as_str());
            if let Some(ref to_jni_converter) = th.to_jni_converter {
                to_jni_converter.apply()
            } else {
                ""
            }
        }
    }
}

fn jni_convert_this_in_constructor(constructor_ret_type: &str, rust_self_type: &str) -> String {
    if constructor_ret_type.starts_with(&format!("Result<{},", rust_self_type)) {
        format!(r#"
  let this = match this {{
    Ok(val) => val,
    Err(msg) => {{
      jni_throw_exception(env, &msg);
      return 0;
    }}
  }};
"#)
    } else {
        String::new()
    }
}

fn generate_rust_to_convert_this(constructor_ret_type: &str, rust_self_type: &str) -> String {
    if constructor_ret_type == rust_self_type {
        return String::new();
    }
    if format!("Arc<Mutex<{}>>", rust_self_type) == constructor_ret_type {
        return format!("let mut this = this.lock().unwrap();\n");
    } else if format!("Rc<RefCell<{}>>", rust_self_type) == constructor_ret_type {
        return format!("let mut this = this.borrow_mut();\n");
    } else {
        unimplemented!();
    }
}

pub fn generate_rust_code<'cx>(cx: &'cx mut ExtCtxt, rust_java_types_map: &RustToJavaTypes, class_info: &ForeignerClassInfo) -> Box<MacResult> {
    let mut jni_methods = Vec::new();
    let constructor = class_info.methods.iter().find(|&x| if let FuncVariant::Constructor = x.func_type { true } else { false });
    let constructor_ret_type = class_info.constructor_ret_type.as_ref();

    let constructor_ret_type_for_method = class_info.this_type_for_method.as_ref();

    let mut generated_func_names = HashMap::<String, usize>::new();
    for it in class_info.methods.iter() {
        let val_ref = generated_func_names.entry(match it.func_type {
            FuncVariant::StaticMethod => unimplemented!(),
            FuncVariant::Constructor => {
                "init".to_string()
            }
            FuncVariant::Method => {
                format!("do_{}", it.short_name())
            }
        });
        *val_ref.or_insert(0) += 1;
    }
    for it in class_info.methods.iter() {
        let java_input_args_iter = it.in_out_type.inputs
            .iter()
            .skip(if it.func_type == FuncVariant::Method { 1 } else { 0 })
            .map(|v| &get_type_handler(rust_java_types_map, &pprust::ty_to_string(&*v.ty).as_str()).java_type_name);
        match it.func_type {
            FuncVariant::StaticMethod => unimplemented!(),
            FuncVariant::Constructor => {
                let constructor_ret_type = constructor_ret_type.as_ref().unwrap().clone();
                let mut code = String::new();
                write!(&mut code, r#"
#[allow(non_snake_case)]
#[no_mangle]
#[allow(unused_variables)]
pub fn {func_name}(env: *mut JNIEnv, _: jclass, {decl_func_args}) -> jlong {{
{convert_jni_args}
  let this = {create_jni_obj}({jni_func_args});
{convert_this}
  Box::into_raw(Box::new(this)) as jlong
}}
"#,
                       func_name = jni_generate_func_name(
                           &class_info.package_name, &class_info.class_name, "init",
                           *generated_func_names.get("init").unwrap() > 1,
                           java_input_args_iter
                       ),
                       convert_this = jni_convert_this_in_constructor(&pprust::ty_to_string(&constructor_ret_type), &class_info.self_rust_type.to_string()),
                       decl_func_args = jni_func_args_for_decl(rust_java_types_map, &*it, 0),
                       convert_jni_args = jni_convert_args(rust_java_types_map, &*it, 0),
                       create_jni_obj = it.full_name(),
                       jni_func_args = it.in_out_type.inputs.iter().enumerate().map(|a| format!("a_{}, ", a.0))
                       .fold(String::new(), |acc, x| acc + &x),
                ).unwrap();
                debug!("we generate and parse code: {}", code);
                jni_methods.push(parse::parse_item_from_source_str(
                    "constructor".to_string(), code, cx.cfg.clone(), cx.parse_sess).unwrap().unwrap());
            }
            FuncVariant::Method => {
                if constructor.is_none() {
                    panic!("package_name {}, class_name {}, have methods, but no constructor",
                           class_info.package_name, class_info.class_name);
                }
                if class_info.self_rust_type.segments.is_empty() {
                    panic!("package_name {}, class_name {} have constructor, but self_type not defined",
                           class_info.package_name, class_info.class_name);
                }
                let constructor_ret_type = constructor_ret_type_for_method.as_ref().unwrap().clone();

                let mut code = String::new();
                let gen_func_name = format!("do_{}", &it.short_name());
                write!(&mut code, r#"
#[allow(non_snake_case)]
#[no_mangle]
#[allow(unused_variables)]
pub fn {func_name}(env: *mut JNIEnv, _: jclass, this: jlong, {decl_func_args}) {jni_result_type} {{
{convert_jni_args}
    let this: &mut {this_type} = unsafe {{ jlong_to_pointer::<{this_type}>(this).as_mut().unwrap() }};
{convert_this}
    let ret = {rust_func_name}(&mut *this, {jni_func_args});
    {convert_output}
    ret
}}
"#,
                       func_name = jni_generate_func_name(&class_info.package_name, &class_info.class_name, &gen_func_name,
                                                           *generated_func_names.get(&gen_func_name).unwrap() > 1,
                                                           java_input_args_iter),
                       decl_func_args = jni_func_args_for_decl(rust_java_types_map, &*it, 1),
                       convert_jni_args = jni_convert_args(rust_java_types_map, &*it, 1),
                       jni_result_type = jni_result_type(rust_java_types_map, &*it),
                       this_type = pprust::ty_to_string(&constructor_ret_type),
                       convert_this = generate_rust_to_convert_this(&pprust::ty_to_string(&constructor_ret_type), &class_info.self_rust_type.to_string()),
                       rust_func_name = it.path,
                       jni_func_args = it.in_out_type.inputs.iter().skip(1).enumerate().map(|a| format!("a_{}, ", a.0))
                       .fold(String::new(), |acc, x| acc + &x),
                       convert_output = jni_convert_output_type(rust_java_types_map, &*it),
                ).unwrap();
                debug!("we generate and parse code: {}", code);
                jni_methods.push(parse::parse_item_from_source_str(
                    "method".to_string(), code, cx.cfg.clone(), cx.parse_sess).unwrap().unwrap());
            }
        }
    }

    if constructor.is_some() {
        let constructor_ret_type = constructor_ret_type_for_method.as_ref().unwrap().clone();
        let mut code = String::new();
        write!(&mut code, r#"
#[allow(non_snake_case)]
#[no_mangle]
pub fn {jni_destructor_name}(_: *mut JNIEnv, _: jclass, this: jlong) {{
    let this: &mut {type_name} = unsafe {{ jlong_to_pointer::<{type_name}>(this).as_mut().unwrap() }};
    let this = unsafe {{ Box::from_raw(this) }};
    drop(this)
}}
"#,
               jni_destructor_name = jni_generate_func_name(&class_info.package_name, &class_info.class_name,
                                                            "do_delete", false, ::std::iter::empty::<&String>()),
               type_name = pprust::ty_to_string(&constructor_ret_type),
        ).unwrap();
        debug!("we generate and parse code: {}", code);
        jni_methods.push(parse::parse_item_from_source_str(
            "destructor".to_string(), code, cx.cfg.clone(), cx.parse_sess).unwrap().unwrap());
    }

    if !COMMON_CODE_GENERATED.swap(true, Ordering::SeqCst) {
        let mut parser = parse::new_parser_from_source_str(
            cx.parse_sess, cx.cfg.clone(), "addon".to_string(),
            r#"
#[cfg(target_pointer_width = "32")]
unsafe fn jlong_to_pointer<T>(val: jlong) -> *mut T {
    ::std::mem::transmute::<u32, *mut T>(val as u32)
}

#[cfg(target_pointer_width = "64")]
unsafe fn jlong_to_pointer<T>(val: jlong) -> *mut T {
    ::std::mem::transmute::<jlong, *mut T>(val)
}

struct JavaString {
    string: jstring,
    chars: *const ::std::os::raw::c_char,
    env: *mut JNIEnv
}
impl JavaString {
    fn new(env: *mut JNIEnv, js: jstring) -> JavaString {
        let chars = unsafe { (**env).GetStringUTFChars.unwrap()(env, js, ::std::ptr::null_mut()) };
        JavaString{string: js, chars: chars, env: env}
    }

    fn to_str(&self) -> &str {
        let s = unsafe { ::std::ffi::CStr::from_ptr(self.chars) };
        s.to_str().unwrap()
    }
}
impl Drop for JavaString {
    fn drop(&mut self) {
        assert!(self.env != ::std::ptr::null_mut() && self.chars != ::std::ptr::null_mut());
        unsafe { (**self.env).ReleaseStringUTFChars.unwrap()(self.env, self.string, self.chars) };
        self.env = ::std::ptr::null_mut();
        self.chars = ::std::ptr::null_mut();
    }
}

#[allow(dead_code)]
fn jni_throw(env: *mut JNIEnv, class_name: &'static str, message: &str) {
    let class_name_c = ::std::ffi::CString::new(class_name).unwrap();

    let ex_class = unsafe {
        (**env).FindClass.unwrap()(env, class_name_c.as_ptr())
    };
    if ex_class.is_null() {
        error!("throw_exception: can not find exp class {}, msg {}", class_name, message);
        return;
    }
    let c_message = ::std::ffi::CString::new(message).unwrap();
    let res = unsafe {
        (**env).ThrowNew.unwrap()(env, ex_class, c_message.as_ptr())
    };
    if res != 0 {
        error!("ThrowNew({}) for class {} failed", message, class_name);
    }
}

#[allow(dead_code)]
fn jni_throw_exception(env: *mut JNIEnv, message: &str) {
    jni_throw(env, "java/lang/Exception", message)
}
"#.to_string());
        let mut my_crate = parser.parse_crate_mod().unwrap();
        jni_methods.append(&mut my_crate.module.items);
    }
    MacEager::items(SmallVector::many(jni_methods))
}
<|endoftext|>