<commit_before>use crate::hasher::Digest;
use crate::Error;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::convert::TryFrom;

use core::convert::TryInto;
use core::fmt::Debug;
#[cfg(feature = "serde-codec")]
use serde_big_array::BigArray;

use unsigned_varint::encode as varint_encode;

#[cfg(feature = "std")]
use std::io;

#[cfg(not(feature = "std"))]
use core2::io;

/// Trait that implements hashing.
///
/// It is usually implemented by a custom code table enum that derives the [`Multihash` derive].
///
/// [`Multihash` derive]: crate::derive
pub trait MultihashDigest<const S: usize>:
    TryFrom<u64> + Into<u64> + Send + Sync + Unpin + Copy + Eq + Debug + 'static
{
    /// Calculate the hash of some input data.
    ///
    /// # Example
    ///
    /// ```
    /// // `Code` implements `MultihashDigest`
    /// use multihash::{Code, MultihashDigest};
    ///
    /// let hash = Code::Sha3_256.digest(b"Hello world!");
    /// println!("{:02x?}", hash);
    /// ```
    fn digest(&self, input: &[u8]) -> Multihash<S>;

    /// Create a multihash from an existing [`Digest`].
    ///
    /// # Example
    ///
    /// ```
    /// use multihash::{Code, MultihashDigest, Sha3_256, StatefulHasher};
    ///
    /// let mut hasher = Sha3_256::default();
    /// hasher.update(b"Hello world!");
    /// let hash = Code::multihash_from_digest(&hasher.finalize());
    /// println!("{:02x?}", hash);
    /// ```
    #[allow(clippy::needless_lifetimes)]
    fn multihash_from_digest<'a, D, const DIGEST_SIZE: usize>(digest: &'a D) -> Multihash<S>
    where
        D: Digest<DIGEST_SIZE>,
        Self: From<&'a D>;
}

/// A Multihash instance that only supports the basic functionality and no hashing.
///
/// With this Multihash implementation you can operate on Multihashes in a generic way, but
/// no hasher implementation is associated with the code.
///
/// # Example
///
/// ```
/// use multihash::Multihash;
///
/// const Sha3_256: u64 = 0x16;
/// let digest_bytes = [
///     0x16, 0x20, 0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04, 0x09, 0x99, 0xaa, 0xc8, 0x9e,
///     0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c, 0x3b, 0xfb,
///     0xf2, 0x4e, 0x39, 0x38,
/// ];
/// let mh = Multihash::from_bytes(&digest_bytes).unwrap();
/// assert_eq!(mh.code(), Sha3_256);
/// assert_eq!(mh.size(), 32);
/// assert_eq!(mh.digest(), &digest_bytes[2..]);
/// ```
#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialOrd)]
pub struct Multihash<const S: usize> {
    /// The code of the Multihash.
    code: u64,
    /// The actual size of the digest in bytes (not the allocated size).
    size: u8,
    /// The digest.
    #[cfg_attr(feature = "serde-codec", serde(with = "BigArray"))]
    digest: [u8; S],
}

impl<const S: usize> Default for Multihash<S> {
    fn default() -> Self {
        Self {
            code: 0,
            size: 0,
            digest: [0; S],
        }
    }
}

impl<const S: usize> Multihash<S> {
    /// Wraps the digest in a multihash.
    pub fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
        if input_digest.len() > S {
            return Err(Error::InvalidSize(input_digest.len() as _));
        }
        let size = input_digest.len();
        let mut digest = [0; S];
        digest[..size].copy_from_slice(input_digest);
        Ok(Self {
            code,
            size: size as u8,
            digest,
        })
    }

    /// Returns the code of the multihash.
    pub fn code(&self) -> u64 {
        self.code
    }

    /// Returns the size of the digest.
    pub fn size(&self) -> u8 {
        self.size
    }

    /// Returns the digest.
    pub fn digest(&self) -> &[u8] {
        &self.digest[..self.size as usize]
    }

    /// Reads a multihash from a byte stream.
    pub fn read<R: io::Read>(r: R) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let (code, size, digest) = read_multihash(r)?;
        Ok(Self { code, size, digest })
    }

    /// Parses a multihash from a bytes.
    ///
    /// You need to make sure the passed in bytes have the correct length. The digest length
    /// needs to match the `size` value of the multihash.
    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let result = Self::read(&mut bytes)?;
        // There were more bytes supplied than read
        if !bytes.is_empty() {
            return Err(Error::InvalidSize(bytes.len().try_into().expect(
                "Currently the maximum size is 255, therefore always fits into usize",
            )));
        }

        Ok(result)
    }

    /// Writes a multihash to a byte stream.
    pub fn write<W: io::Write>(&self, w: W) -> Result<(), Error> {
        write_multihash(w, self.code(), self.size(), self.digest())
    }

    #[cfg(feature = "alloc")]
    /// Returns the bytes of a multihash.
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(self.size().into());
        self.write(&mut bytes)
            .expect("writing to a vec should never fail");
        bytes
    }
}

// Don't hash the whole allocated space, but just the actual digest
#[allow(clippy::derive_hash_xor_eq)]
impl<const S: usize> core::hash::Hash for Multihash<S> {
    fn hash<T: core::hash::Hasher>(&self, state: &mut T) {
        self.code.hash(state);
        self.digest().hash(state);
    }
}

#[cfg(feature = "alloc")]
impl<const S: usize> From<Multihash<S>> for Vec<u8> {
    fn from(multihash: Multihash<S>) -> Self {
        multihash.to_bytes()
    }
}

impl<const A: usize, const B: usize> PartialEq<Multihash<B>> for Multihash<A> {
    fn eq(&self, other: &Multihash<B>) -> bool {
        // NOTE: there's no need to explicitly check the sizes, that's implicit in the digest.
        self.code == other.code && self.digest() == other.digest()
    }
}

#[cfg(feature = "scale-codec")]
impl<const S: usize> parity_scale_codec::Encode for Multihash<S> {
    fn encode_to<EncOut: parity_scale_codec::Output + ?Sized>(&self, dest: &mut EncOut) {
        self.code.encode_to(dest);
        self.size.encode_to(dest);
        // **NOTE** We write the digest directly to dest, since we have known the size of digest.
        //
        // We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
        // For a valid multihash, the length of digest must equal to `size`.
        // Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
        dest.write(self.digest());
    }
}

#[cfg(feature = "scale-codec")]
impl<const S: usize> parity_scale_codec::EncodeLike for Multihash<S> {}

#[cfg(feature = "scale-codec")]
impl<const S: usize> parity_scale_codec::Decode for Multihash<S> {
    fn decode<DecIn: parity_scale_codec::Input>(
        input: &mut DecIn,
    ) -> Result<Self, parity_scale_codec::Error> {
        let mut mh = Multihash {
            code: parity_scale_codec::Decode::decode(input)?,
            size: parity_scale_codec::Decode::decode(input)?,
            digest: [0; S],
        };
        if mh.size as usize > S {
            return Err(parity_scale_codec::Error::from("invalid size"));
        }
        // For a valid multihash, the length of digest must equal to the size.
        input.read(&mut mh.digest[..mh.size as usize])?;
        Ok(mh)
    }
}

/// Writes the multihash to a byte stream.
pub fn write_multihash<W>(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result<(), Error>
where
    W: io::Write,
{
    let mut code_buf = varint_encode::u64_buffer();
    let code = varint_encode::u64(code, &mut code_buf);

    let mut size_buf = varint_encode::u8_buffer();
    let size = varint_encode::u8(size, &mut size_buf);

    w.write_all(code)?;
    w.write_all(size)?;
    w.write_all(digest)?;
    Ok(())
}

/// Reads a multihash from a byte stream that contains a full multihash (code, size and the digest)
///
/// Returns the code, size and the digest. The size is the actual size and not the
/// maximum/allocated size of the digest.
///
/// Currently the maximum size for a digest is 255 bytes.
pub fn read_multihash<R, const S: usize>(mut r: R) -> Result<(u64, u8, [u8; S]), Error>
where
    R: io::Read,
{
    let code = read_u64(&mut r)?;
    let size = read_u64(&mut r)?;

    if size > S as u64 || size > u8::MAX as u64 {
        return Err(Error::InvalidSize(size));
    }

    let mut digest = [0; S];
    r.read_exact(&mut digest[..size as usize])?;
    Ok((code, size as u8, digest))
}

#[cfg(feature = "std")]
pub(crate) use unsigned_varint::io::read_u64;

/// Reads 64 bits from a byte array into a u64
/// Adapted from unsigned-varint's generated read_u64 function at
/// https://github.com/paritytech/unsigned-varint/blob/master/src/io.rs
#[cfg(not(feature = "std"))]
pub(crate) fn read_u64<R: io::Read>(mut r: R) -> Result<u64, Error> {
    use unsigned_varint::decode;
    let mut b = varint_encode::u64_buffer();
    for i in 0..b.len() {
        let n = r.read(&mut (b[i..i + 1]))?;
        if n == 0 {
            return Err(Error::Varint(decode::Error::Insufficient));
        } else if decode::is_last(b[i]) {
            return Ok(decode::u64(&b[..=i]).unwrap().0);
        }
    }
    Err(Error::Varint(decode::Error::Overflow))
}

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

    #[test]
    fn roundtrip() {
        let hash = Code::Sha2_256.digest(b"hello world");
        let mut buf = [0u8; 35];
        hash.write(&mut buf[..]).unwrap();
        let hash2 = Multihash::<32>::read(&buf[..]).unwrap();
        assert_eq!(hash, hash2);
    }

    #[test]
    #[cfg(feature = "scale-codec")]
    fn test_scale() {
        use crate::{Hasher, Sha2_256};
        use parity_scale_codec::{Decode, Encode};

        let mh1 = Multihash::<32>::wrap(
            Code::Sha2_256.into(),
            Sha2_256::digest(b"hello world").as_ref(),
        )
        .unwrap();
        // println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest());
        let mh1_bytes = mh1.encode();
        // println!("Multihash<32>: {}", hex::encode(&mh1_bytes));
        let mh2: Multihash<32> = Decode::decode(&mut &mh1_bytes[..]).unwrap();
        assert_eq!(mh1, mh2);

        let mh3: Multihash<64> = Code::Sha2_256.digest(b"hello world");
        // println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest());
        let mh3_bytes = mh3.encode();
        // println!("Multihash<64>: {}", hex::encode(&mh3_bytes));
        let mh4: Multihash<64> = Decode::decode(&mut &mh3_bytes[..]).unwrap();
        assert_eq!(mh3, mh4);

        assert_eq!(mh1_bytes, mh3_bytes);
    }

    #[test]
    #[cfg(feature = "serde-codec")]
    fn test_serde() {
        let mh = Multihash::<32>::default();
        let bytes = serde_json::to_string(&mh).unwrap();
        let mh2: Multihash<32> = serde_json::from_str(&bytes).unwrap();
        assert_eq!(mh, mh2);
    }

    #[test]
    fn test_eq_sizes() {
        let mh1 = Multihash::<32>::default();
        let mh2 = Multihash::<64>::default();
        assert_eq!(mh1, mh2);
    }
}
<commit_msg>add support for resizing multihashes<commit_after>use crate::hasher::Digest;
use crate::Error;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::convert::TryFrom;

use core::convert::TryInto;
use core::fmt::Debug;
#[cfg(feature = "serde-codec")]
use serde_big_array::BigArray;

use unsigned_varint::encode as varint_encode;

#[cfg(feature = "std")]
use std::io;

#[cfg(not(feature = "std"))]
use core2::io;

/// Trait that implements hashing.
///
/// It is usually implemented by a custom code table enum that derives the [`Multihash` derive].
///
/// [`Multihash` derive]: crate::derive
pub trait MultihashDigest<const S: usize>:
    TryFrom<u64> + Into<u64> + Send + Sync + Unpin + Copy + Eq + Debug + 'static
{
    /// Calculate the hash of some input data.
    ///
    /// # Example
    ///
    /// ```
    /// // `Code` implements `MultihashDigest`
    /// use multihash::{Code, MultihashDigest};
    ///
    /// let hash = Code::Sha3_256.digest(b"Hello world!");
    /// println!("{:02x?}", hash);
    /// ```
    fn digest(&self, input: &[u8]) -> Multihash<S>;

    /// Create a multihash from an existing [`Digest`].
    ///
    /// # Example
    ///
    /// ```
    /// use multihash::{Code, MultihashDigest, Sha3_256, StatefulHasher};
    ///
    /// let mut hasher = Sha3_256::default();
    /// hasher.update(b"Hello world!");
    /// let hash = Code::multihash_from_digest(&hasher.finalize());
    /// println!("{:02x?}", hash);
    /// ```
    #[allow(clippy::needless_lifetimes)]
    fn multihash_from_digest<'a, D, const DIGEST_SIZE: usize>(digest: &'a D) -> Multihash<S>
    where
        D: Digest<DIGEST_SIZE>,
        Self: From<&'a D>;
}

/// A Multihash instance that only supports the basic functionality and no hashing.
///
/// With this Multihash implementation you can operate on Multihashes in a generic way, but
/// no hasher implementation is associated with the code.
///
/// # Example
///
/// ```
/// use multihash::Multihash;
///
/// const Sha3_256: u64 = 0x16;
/// let digest_bytes = [
///     0x16, 0x20, 0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04, 0x09, 0x99, 0xaa, 0xc8, 0x9e,
///     0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c, 0x3b, 0xfb,
///     0xf2, 0x4e, 0x39, 0x38,
/// ];
/// let mh = Multihash::from_bytes(&digest_bytes).unwrap();
/// assert_eq!(mh.code(), Sha3_256);
/// assert_eq!(mh.size(), 32);
/// assert_eq!(mh.digest(), &digest_bytes[2..]);
/// ```
#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialOrd)]
pub struct Multihash<const S: usize> {
    /// The code of the Multihash.
    code: u64,
    /// The actual size of the digest in bytes (not the allocated size).
    size: u8,
    /// The digest.
    #[cfg_attr(feature = "serde-codec", serde(with = "BigArray"))]
    digest: [u8; S],
}

impl<const S: usize> Default for Multihash<S> {
    fn default() -> Self {
        Self {
            code: 0,
            size: 0,
            digest: [0; S],
        }
    }
}

impl<const S: usize> Multihash<S> {
    /// Wraps the digest in a multihash.
    pub fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
        if input_digest.len() > S {
            return Err(Error::InvalidSize(input_digest.len() as _));
        }
        let size = input_digest.len();
        let mut digest = [0; S];
        digest[..size].copy_from_slice(input_digest);
        Ok(Self {
            code,
            size: size as u8,
            digest,
        })
    }

    /// Returns the code of the multihash.
    pub fn code(&self) -> u64 {
        self.code
    }

    /// Returns the size of the digest.
    pub fn size(&self) -> u8 {
        self.size
    }

    /// Returns the digest.
    pub fn digest(&self) -> &[u8] {
        &self.digest[..self.size as usize]
    }

    /// Reads a multihash from a byte stream.
    pub fn read<R: io::Read>(r: R) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let (code, size, digest) = read_multihash(r)?;
        Ok(Self { code, size, digest })
    }

    /// Parses a multihash from a bytes.
    ///
    /// You need to make sure the passed in bytes have the correct length. The digest length
    /// needs to match the `size` value of the multihash.
    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let result = Self::read(&mut bytes)?;
        // There were more bytes supplied than read
        if !bytes.is_empty() {
            return Err(Error::InvalidSize(bytes.len().try_into().expect(
                "Currently the maximum size is 255, therefore always fits into usize",
            )));
        }

        Ok(result)
    }

    /// Writes a multihash to a byte stream.
    pub fn write<W: io::Write>(&self, w: W) -> Result<(), Error> {
        write_multihash(w, self.code(), self.size(), self.digest())
    }

    #[cfg(feature = "alloc")]
    /// Returns the bytes of a multihash.
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(self.size().into());
        self.write(&mut bytes)
            .expect("writing to a vec should never fail");
        bytes
    }

    /// Truncates the multihash to the given size. It's up to the caller to ensure that the new size
    /// is safe to use.
    ///
    /// If the new size is larger than the current size, this method does nothing.
    ///
    /// ```
    /// use multihash::{Code, MultihashDigest};
    ///
    /// let hash = Code::Sha3_256.digest(b"Hello world!").truncate(20);
    /// ```
    pub fn truncate(&self, size: u8) -> Self {
        let mut mh = *self;
        mh.size = mh.size.min(size);
        mh
    }

    /// Resizes the backing multihash buffer. If the multihash doesn't fit, this function fails and
    /// does not truncate. If you need to truncate, call that first.
    ///
    /// ```
    /// use multihash::{Code, MultihashDigest, MultihashGeneric};
    ///
    /// let hash = Code::Sha3_256.digest(b"Hello world!");
    /// let large_hash: MultihashGeneric<32> = hash.resize().unwrap();
    /// ```
    pub fn resize<const R: usize>(&self) -> Result<Multihash<R>, Error> {
        let size = self.size as usize;
        if size > R {
            return Err(Error::InvalidSize(self.size as u64));
        }
        let mut mh = Multihash {
            code: self.code,
            size: self.size,
            digest: [0; R],
        };
        mh.digest[..size].copy_from_slice(&self.digest[..size]);
        Ok(mh)
    }
}

// Don't hash the whole allocated space, but just the actual digest
#[allow(clippy::derive_hash_xor_eq)]
impl<const S: usize> core::hash::Hash for Multihash<S> {
    fn hash<T: core::hash::Hasher>(&self, state: &mut T) {
        self.code.hash(state);
        self.digest().hash(state);
    }
}

#[cfg(feature = "alloc")]
impl<const S: usize> From<Multihash<S>> for Vec<u8> {
    fn from(multihash: Multihash<S>) -> Self {
        multihash.to_bytes()
    }
}

impl<const A: usize, const B: usize> PartialEq<Multihash<B>> for Multihash<A> {
    fn eq(&self, other: &Multihash<B>) -> bool {
        // NOTE: there's no need to explicitly check the sizes, that's implicit in the digest.
        self.code == other.code && self.digest() == other.digest()
    }
}

#[cfg(feature = "scale-codec")]
impl<const S: usize> parity_scale_codec::Encode for Multihash<S> {
    fn encode_to<EncOut: parity_scale_codec::Output + ?Sized>(&self, dest: &mut EncOut) {
        self.code.encode_to(dest);
        self.size.encode_to(dest);
        // **NOTE** We write the digest directly to dest, since we have known the size of digest.
        //
        // We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
        // For a valid multihash, the length of digest must equal to `size`.
        // Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
        dest.write(self.digest());
    }
}

#[cfg(feature = "scale-codec")]
impl<const S: usize> parity_scale_codec::EncodeLike for Multihash<S> {}

#[cfg(feature = "scale-codec")]
impl<const S: usize> parity_scale_codec::Decode for Multihash<S> {
    fn decode<DecIn: parity_scale_codec::Input>(
        input: &mut DecIn,
    ) -> Result<Self, parity_scale_codec::Error> {
        let mut mh = Multihash {
            code: parity_scale_codec::Decode::decode(input)?,
            size: parity_scale_codec::Decode::decode(input)?,
            digest: [0; S],
        };
        if mh.size as usize > S {
            return Err(parity_scale_codec::Error::from("invalid size"));
        }
        // For a valid multihash, the length of digest must equal to the size.
        input.read(&mut mh.digest[..mh.size as usize])?;
        Ok(mh)
    }
}

/// Writes the multihash to a byte stream.
pub fn write_multihash<W>(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result<(), Error>
where
    W: io::Write,
{
    let mut code_buf = varint_encode::u64_buffer();
    let code = varint_encode::u64(code, &mut code_buf);

    let mut size_buf = varint_encode::u8_buffer();
    let size = varint_encode::u8(size, &mut size_buf);

    w.write_all(code)?;
    w.write_all(size)?;
    w.write_all(digest)?;
    Ok(())
}

/// Reads a multihash from a byte stream that contains a full multihash (code, size and the digest)
///
/// Returns the code, size and the digest. The size is the actual size and not the
/// maximum/allocated size of the digest.
///
/// Currently the maximum size for a digest is 255 bytes.
pub fn read_multihash<R, const S: usize>(mut r: R) -> Result<(u64, u8, [u8; S]), Error>
where
    R: io::Read,
{
    let code = read_u64(&mut r)?;
    let size = read_u64(&mut r)?;

    if size > S as u64 || size > u8::MAX as u64 {
        return Err(Error::InvalidSize(size));
    }

    let mut digest = [0; S];
    r.read_exact(&mut digest[..size as usize])?;
    Ok((code, size as u8, digest))
}

#[cfg(feature = "std")]
pub(crate) use unsigned_varint::io::read_u64;

/// Reads 64 bits from a byte array into a u64
/// Adapted from unsigned-varint's generated read_u64 function at
/// https://github.com/paritytech/unsigned-varint/blob/master/src/io.rs
#[cfg(not(feature = "std"))]
pub(crate) fn read_u64<R: io::Read>(mut r: R) -> Result<u64, Error> {
    use unsigned_varint::decode;
    let mut b = varint_encode::u64_buffer();
    for i in 0..b.len() {
        let n = r.read(&mut (b[i..i + 1]))?;
        if n == 0 {
            return Err(Error::Varint(decode::Error::Insufficient));
        } else if decode::is_last(b[i]) {
            return Ok(decode::u64(&b[..=i]).unwrap().0);
        }
    }
    Err(Error::Varint(decode::Error::Overflow))
}

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

    #[test]
    fn roundtrip() {
        let hash = Code::Sha2_256.digest(b"hello world");
        let mut buf = [0u8; 35];
        hash.write(&mut buf[..]).unwrap();
        let hash2 = Multihash::<32>::read(&buf[..]).unwrap();
        assert_eq!(hash, hash2);
    }

    #[test]
    fn test_truncate_down() {
        let hash = Code::Sha2_256.digest(b"hello world");
        let small = hash.truncate(20);
        assert_eq!(small.size(), 20);
    }

    #[test]
    fn test_truncate_up() {
        let hash = Code::Sha2_256.digest(b"hello world");
        let small = hash.truncate(100);
        assert_eq!(small.size(), 32);
    }

    #[test]
    fn test_resize_fits() {
        let hash = Code::Sha2_256.digest(b"hello world");
        let _: Multihash<32> = hash.resize().unwrap();
    }

    #[test]
    fn test_resize_up() {
        let hash = Code::Sha2_256.digest(b"hello world");
        let _: Multihash<100> = hash.resize().unwrap();
    }

    #[test]
    fn test_resize_truncate() {
        let hash = Code::Sha2_256.digest(b"hello world");
        hash.resize::<20>().unwrap_err();
    }

    #[test]
    #[cfg(feature = "scale-codec")]
    fn test_scale() {
        use crate::{Hasher, Sha2_256};
        use parity_scale_codec::{Decode, Encode};

        let mh1 = Multihash::<32>::wrap(
            Code::Sha2_256.into(),
            Sha2_256::digest(b"hello world").as_ref(),
        )
        .unwrap();
        // println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest());
        let mh1_bytes = mh1.encode();
        // println!("Multihash<32>: {}", hex::encode(&mh1_bytes));
        let mh2: Multihash<32> = Decode::decode(&mut &mh1_bytes[..]).unwrap();
        assert_eq!(mh1, mh2);

        let mh3: Multihash<64> = Code::Sha2_256.digest(b"hello world");
        // println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest());
        let mh3_bytes = mh3.encode();
        // println!("Multihash<64>: {}", hex::encode(&mh3_bytes));
        let mh4: Multihash<64> = Decode::decode(&mut &mh3_bytes[..]).unwrap();
        assert_eq!(mh3, mh4);

        assert_eq!(mh1_bytes, mh3_bytes);
    }

    #[test]
    #[cfg(feature = "serde-codec")]
    fn test_serde() {
        let mh = Multihash::<32>::default();
        let bytes = serde_json::to_string(&mh).unwrap();
        let mh2: Multihash<32> = serde_json::from_str(&bytes).unwrap();
        assert_eq!(mh, mh2);
    }

    #[test]
    fn test_eq_sizes() {
        let mh1 = Multihash::<32>::default();
        let mh2 = Multihash::<64>::default();
        assert_eq!(mh1, mh2);
    }
}
<|endoftext|>


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

extern crate getopts;
extern crate test;

use test::{AutoColor, TestOpts, run_tests_console, TestDesc, TestDescAndFn, DynTestFn, DynTestName};
use test::ShouldFail;
use getopts::{getopts, reqopt};
use std::{os, str};
use std::old_io::fs;
use std::old_io::Reader;
use std::old_io::process::{Command, Ignored, CreatePipe, InheritFd, ExitStatus};
use std::old_path::Path;
use std::thunk::Thunk;

#[derive(Clone)]
struct Config {
    source_dir: String,
    filter: Option<String>
}

fn main() {
    let args = os::args();
    let config = parse_config(args.into_iter().collect());
    let opts = test_options(config.clone());
    let tests = find_tests(config);
    match run_tests_console(&opts, tests) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

fn parse_config(args: Vec<String>) -> Config {
    let args = args.tail();
    let opts = vec!(reqopt("s", "source-dir", "source-dir", "source-dir"));
    let matches = match getopts(args, opts.as_slice()) {
      Ok(m) => m,
      Err(f) => panic!(format!("{}", f))
    };

    Config {
        source_dir: matches.opt_str("source-dir").unwrap(),
        filter: matches.free.first().map(|s| s.clone())
    }
}

fn test_options(config: Config) -> TestOpts {
    TestOpts {
        filter: config.filter,
        run_ignored: false,
        run_tests: true,
        run_benchmarks: false,
        logfile: None,
        nocapture: false,
        color: AutoColor,
    }
}

fn find_tests(config: Config) -> Vec<TestDescAndFn> {
    let files_res = fs::readdir(&Path::new(config.source_dir));
    let mut files = match files_res {
        Ok(files) => files,
        _ => panic!("Error reading directory."),
    };
    files.retain(|file| file.extension_str() == Some("html") );
    return files.iter().map(|file| make_test(format!("{}", file.display()))).collect();
}

fn make_test(file: String) -> TestDescAndFn {
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(file.clone()),
            ignore: false,
            should_fail: ShouldFail::No,
        },
        testfn: DynTestFn(Thunk::new(move || { run_test(file) }))
    }
}

fn run_test(file: String) {
    let path = os::make_absolute(&Path::new(file)).unwrap();
    // FIXME (#1094): not the right way to transform a path
    let infile = format!("file://{}", path.display());
    let stdout = CreatePipe(false, true);
    let stderr = InheritFd(2);
    let args = ["-z", "-f", infile.as_slice()];

    let mut prc = match Command::new(os::self_exe_path().unwrap().join("servo"))
        .args(args.as_slice())
        .stdin(Ignored)
        .stdout(stdout)
        .stderr(stderr)
        .spawn()
    {
        Ok(p) => p,
        _ => panic!("Unable to spawn process."),
    };
    let mut output = Vec::new();
    loop {
        let byte = prc.stdout.as_mut().unwrap().read_byte();
        match byte {
            Ok(byte) => {
                print!("{}", byte as char);
                output.push(byte);
            }
            _ => break
        }
    }

    let out = str::from_utf8(output.as_slice());
    let lines: Vec<&str> = out.unwrap().split('\n').collect();
    for &line in lines.iter() {
        if line.contains("TEST-UNEXPECTED-FAIL") {
            panic!(line.to_string());
        }
    }

    let retval = prc.wait();
    if retval != Ok(ExitStatus(0)) {
        panic!("Servo exited with non-zero status {:?}", retval);
    }
}
<commit_msg>auto merge of #5174 : ehegnes/servo/issue-5159, r=Manishearth<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(collections, core, io, os, path, rustc_private, std_misc, test)]

extern crate getopts;
extern crate test;

use test::{AutoColor, TestOpts, run_tests_console, TestDesc, TestDescAndFn, DynTestFn, DynTestName};
use test::ShouldFail;
use getopts::{getopts, reqopt};
use std::{os, str, env};
use std::old_io::fs;
use std::old_io::Reader;
use std::old_io::process::{Command, Ignored, CreatePipe, InheritFd, ExitStatus};
use std::old_path::Path;
use std::thunk::Thunk;

#[derive(Clone)]
struct Config {
    source_dir: String,
    filter: Option<String>
}

fn main() {
    let args = env::args();
    let config = parse_config(args.map(|x| x.into_string().unwrap()).collect());
    let opts = test_options(config.clone());
    let tests = find_tests(config);
    match run_tests_console(&opts, tests) {
        Ok(false) => env::set_exit_status(1), // tests failed
        Err(_) => env::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

fn parse_config(args: Vec<String>) -> Config {
    let args = args.tail();
    let opts = vec!(reqopt("s", "source-dir", "source-dir", "source-dir"));
    let matches = match getopts(args, opts.as_slice()) {
      Ok(m) => m,
      Err(f) => panic!(format!("{}", f))
    };

    Config {
        source_dir: matches.opt_str("source-dir").unwrap(),
        filter: matches.free.first().map(|s| s.clone())
    }
}

fn test_options(config: Config) -> TestOpts {
    TestOpts {
        filter: config.filter,
        run_ignored: false,
        run_tests: true,
        run_benchmarks: false,
        logfile: None,
        nocapture: false,
        color: AutoColor,
    }
}

fn find_tests(config: Config) -> Vec<TestDescAndFn> {
    let files_res = fs::readdir(&Path::new(config.source_dir));
    let mut files = match files_res {
        Ok(files) => files,
        _ => panic!("Error reading directory."),
    };
    files.retain(|file| file.extension_str() == Some("html") );
    return files.iter().map(|file| make_test(format!("{}", file.display()))).collect();
}

fn make_test(file: String) -> TestDescAndFn {
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(file.clone()),
            ignore: false,
            should_fail: ShouldFail::No,
        },
        testfn: DynTestFn(Thunk::new(move || { run_test(file) }))
    }
}

fn run_test(file: String) {
    let path = os::make_absolute(&Path::new(file)).unwrap();
    // FIXME (#1094): not the right way to transform a path
    let infile = format!("file://{}", path.display());
    let stdout = CreatePipe(false, true);
    let stderr = InheritFd(2);
    let args = ["-z", "-f", infile.as_slice()];

    let mut prc = match Command::new(os::self_exe_path().unwrap().join("servo"))
        .args(args.as_slice())
        .stdin(Ignored)
        .stdout(stdout)
        .stderr(stderr)
        .spawn()
    {
        Ok(p) => p,
        _ => panic!("Unable to spawn process."),
    };
    let mut output = Vec::new();
    loop {
        let byte = prc.stdout.as_mut().unwrap().read_byte();
        match byte {
            Ok(byte) => {
                print!("{}", byte as char);
                output.push(byte);
            }
            _ => break
        }
    }

    let out = str::from_utf8(output.as_slice());
    let lines: Vec<&str> = out.unwrap().split('\n').collect();
    for &line in lines.iter() {
        if line.contains("TEST-UNEXPECTED-FAIL") {
            panic!(line.to_string());
        }
    }

    let retval = prc.wait();
    if retval != Ok(ExitStatus(0)) {
        panic!("Servo exited with non-zero status {:?}", retval);
    }
}
<|endoftext|>


<commit_before>extern crate chainbuf;
extern crate native;
#[cfg(feature="nix")] extern crate nix;

#[cfg(test)]
mod test {
    #[cfg(feature="nix")]
    mod test_writev {
        use chainbuf::Chain;
        use nix::unistd::{pipe, close};
        use native::io::FileDesc;
        use std::rand::{task_rng, Rng};

        #[test]
        fn test_write_to_fd_works() {
            // Run this test with some pipes so we don't have to mess around with
            // opening or closing files.
            let mut chain = Chain::new();

            let mut to_write = Vec::with_capacity(16 * 128);
            for _ in range(0u, 16) {
                let s:String = task_rng().gen_ascii_chars().take(128).collect();
                let b = s.as_bytes();
                chain.append_bytes(b);
                to_write.extend(b.iter().map(|x| x.clone()));
            }
            let cl = chain.len();

            let pipe_res = pipe();
            assert!(pipe_res.is_ok());
            let (reader, writer) = pipe_res.ok().unwrap();
            let mut reader = FileDesc::new(reader, true);
            // write all data
            let write_res = chain.write_to_fd(writer, None, None);
            assert!(write_res.is_ok());
            let written = write_res.ok().unwrap();
            // written all data
            assert_eq!(to_write.len(), written);
            // written all that has been stored
            assert_eq!(written, cl);
            // chain has been drained
            assert_eq!(chain.len(), 0);
            let mut read_buf = Vec::from_elem(128 * 16, 0u8);
            let read_res = reader.inner_read(read_buf.as_mut_slice());
            assert!(read_res.is_ok());
            let read = read_res.ok().unwrap() as uint;
            // Check we have read as much as we written
            assert_eq!(read, written);
            assert_eq!(to_write.as_slice(), read_buf.as_slice());
            let _ = close(writer);
        }

    }

}
<commit_msg>[compat] rtio was removed, use nix-rust's routines for tests<commit_after>extern crate chainbuf;
#[cfg(feature="nix")] extern crate nix;

#[cfg(test)]
mod test {
    #[cfg(feature="nix")]
    mod test_writev {
        use chainbuf::Chain;
        use nix::unistd::{pipe, close, read};
        use std::rand::{task_rng, Rng};

        #[test]
        fn test_write_to_fd_works() {
            // Run this test with some pipes so we don't have to mess around with
            // opening or closing files.
            let mut chain = Chain::new();

            let mut to_write = Vec::with_capacity(16 * 128);
            for _ in range(0u, 16) {
                let s:String = task_rng().gen_ascii_chars().take(128).collect();
                let b = s.as_bytes();
                chain.append_bytes(b);
                to_write.extend(b.iter().map(|x| x.clone()));
            }
            let cl = chain.len();

            let pipe_res = pipe();
            assert!(pipe_res.is_ok());
            let (reader, writer) = pipe_res.ok().unwrap();
            // write all data
            let write_res = chain.write_to_fd(writer, None, None);
            assert!(write_res.is_ok());
            let written = write_res.ok().unwrap();
            // written all data
            assert_eq!(to_write.len(), written);
            // written all that has been stored
            assert_eq!(written, cl);
            // chain has been drained
            assert_eq!(chain.len(), 0);
            let mut read_buf = Vec::from_elem(128 * 16, 0u8);
            let read_res = read(reader, read_buf.as_mut_slice());
            assert!(read_res.is_ok());
            let read = read_res.ok().unwrap() as uint;
            // Check we have read as much as we written
            assert_eq!(read, written);
            assert_eq!(to_write.as_slice(), read_buf.as_slice());
            let _ = close(writer);
            let _ = close(reader);
        }

    }

}
<|endoftext|>


<commit_before>use std::fs::{File, create_dir_all};
use std::io::Read;
use std::path::PathBuf;
use std::process::Command;

fn main() {
    if let Ok(mut linguas_file) = File::open(&PathBuf::from("po").join("LINGUAS")) {
        let mut linguas = String::new();
        linguas_file
            .read_to_string(&mut linguas)
            .expect("Couldn't read po/LINGUAS as string");

        for lingua in linguas.lines() {
            let mo_path = PathBuf::from("target")
                .join("locale")
                .join(lingua)
                .join("LC_MESSAGES");
            create_dir_all(&mo_path).unwrap();

            let mut msgfmt = Command::new("msgfmt");
            msgfmt.arg(format!("--output-file={}",
                    mo_path.join("media-toc.mo")
                        .to_str()
                        .unwrap()
                ))
                .arg("--directory=po")
                .arg(format!("{}.po", lingua));
            let msgfmt_status = msgfmt.status()
                .expect("Failed to invoke `msgfmt`");

            if !msgfmt_status.success() {
                panic!(format!("Failed to generate mo file for lingua {}\n{:?}", lingua, msgfmt));
            }
        }
    }
}
<commit_msg>i18n: don't fail if `msgfmt` is not available<commit_after>use std::fs::{File, create_dir_all};
use std::io::{ErrorKind, Read};
use std::path::PathBuf;
use std::process::Command;

fn main() {
    if let Ok(mut linguas_file) = File::open(&PathBuf::from("po").join("LINGUAS")) {
        let mut linguas = String::new();
        linguas_file
            .read_to_string(&mut linguas)
            .expect("Couldn't read po/LINGUAS as string");

        for lingua in linguas.lines() {
            let mo_path = PathBuf::from("target")
                .join("locale")
                .join(lingua)
                .join("LC_MESSAGES");
            create_dir_all(&mo_path).unwrap();

            let mut msgfmt = Command::new("msgfmt");
            msgfmt.arg(format!("--output-file={}",
                    mo_path.join("media-toc.mo")
                        .to_str()
                        .unwrap()
                ))
                .arg("--directory=po")
                .arg(format!("{}.po", lingua));

            match msgfmt.status() {
                Ok(status) => if !status.success() {
                    panic!(format!("Failed to generate mo file for lingua {}\n{:?}",
                        lingua,
                        msgfmt,
                    ));
                }
                Err(ref error) => match error.kind() {
                    ErrorKind::NotFound => {
                        eprintln!("Can't generate translations: command `msgfmt` not available");
                        return;
                    }
                    _ => panic!("Error invoking `msgfmt`: {}", error),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate gcc;

use std::fs;
use std::io;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::ffi::OsString;

trait CommandExt {
    fn execute(&mut self) -> io::Result<()>;
}

impl CommandExt for Command {
    /// Execute the command and return an error if it exited with a failure status.
    fn execute(&mut self) -> io::Result<()> {
        let status = try!(self.status());
        if status.success() {
            Ok(())
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
            \t{:?}\n\
            did not run successfully.", self)))
        }
    }
}

/// The command to build lua, with switches for different OSes.
fn build_lua(tooling: &gcc::Tool, dir: &Path) -> io::Result<()> {
    // calculate the Lua platform name
    let platform = match env::var("TARGET").unwrap().split('-').nth(2).unwrap() {
        "windows" => "mingw",
        "macos" => "macosx",
        "linux" => "linux",
        "freebsd" => "freebsd",
        "dragonfly" => "bsd",
        // fall back to the "generic" system
        _ => "generic",
    };

    // build the CC and MYCFLAGS parameters
    let mut cc = OsString::from("CC=");
    cc.push(tooling.path());
    let mut cflags = OsString::from("MYCFLAGS=");
    for arg in tooling.args() {
        cflags.push(arg);
        cflags.push(" ");
    }

    // call the makefile
    let mut command = Command::new("make");
    for &(ref key, ref val) in tooling.env() {
        command.env(key, val);
    }
    command.current_dir(dir)
        .arg(platform)
        .arg(cc)
        .arg(cflags)
        .execute()
}

/// The command to fetch a URL (e.g. with wget) specialized for different
/// OSes.
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos")))]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("wget").arg(url).current_dir(cwd).execute()
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("fetch").arg(url).current_dir(cwd).execute()
}

#[cfg(target_os = "macos")]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("curl").arg("-O").arg(url).current_dir(cwd).execute()
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() -> io::Result<()> {
    let lua_version = match env::var_os("LUA_VERSION") {
        Some(lua_version) => lua_version,
        None => From::from("5.3.0"),
    };
    let lua_version = lua_version.to_str().unwrap();
    let lua_dir = format!("lua-{}", lua_version);
    let lua_tarball = format!("{}.tar.gz", lua_dir);
    let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let tooling = gcc::Config::new().get_compiler();

    // Ensure the presence of liblua.a
    if !fs::metadata(build_dir.join(&format!("{}/src/liblua.a", lua_dir))).is_ok() {
        try!(fs::create_dir_all(&build_dir));

        // Download lua if it hasn't been already
        if !fs::metadata(build_dir.join(&lua_tarball)).is_ok() {
            match env::var("LUA_LOCAL_SOURCE") {
                Ok(lua_source_path) => {
                    try!(Command::new("cp")
                         .arg(&PathBuf::from(lua_source_path).join(&lua_tarball))
                         .arg(".")
                         .current_dir(&build_dir)
                         .execute());
                }
                Err(_) => {
                    try!(fetch_in_dir(&format!(
                        "http://www.lua.org/ftp/{}", lua_tarball), &build_dir));
                }
            }
            try!(Command::new("tar")
                .arg("xzf")
                .arg(&lua_tarball)
                .current_dir(&build_dir)
                .execute());
        }
        // Compile lua
        try!(build_lua(&tooling, &build_dir.join(&lua_dir)));
    }

    // Ensure the presence of glue.rs
    if !fs::metadata(build_dir.join("glue.rs")).is_ok() {
        // Compile glue.c
        let glue = build_dir.join("glue");
        try!(Command::new("gcc")
            .arg("-I").arg(build_dir.join(&format!("{}/src", lua_dir)))
            .arg("src/glue/glue.c")
            .arg("-o").arg(&glue)
            .execute());
        try!(Command::new(glue)
            .arg(build_dir.join("glue.rs"))
            .execute());
    }

    // Output build information
    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}/{}/src", build_dir.to_str().unwrap(), lua_dir);

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<commit_msg>Use Env::var_os not Env::var.<commit_after>extern crate gcc;

use std::fs;
use std::io;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::ffi::OsString;

trait CommandExt {
    fn execute(&mut self) -> io::Result<()>;
}

impl CommandExt for Command {
    /// Execute the command and return an error if it exited with a failure status.
    fn execute(&mut self) -> io::Result<()> {
        let status = try!(self.status());
        if status.success() {
            Ok(())
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
            \t{:?}\n\
            did not run successfully.", self)))
        }
    }
}

/// The command to build lua, with switches for different OSes.
fn build_lua(tooling: &gcc::Tool, dir: &Path) -> io::Result<()> {
    // calculate the Lua platform name
    let platform = match env::var("TARGET").unwrap().split('-').nth(2).unwrap() {
        "windows" => "mingw",
        "macos" => "macosx",
        "linux" => "linux",
        "freebsd" => "freebsd",
        "dragonfly" => "bsd",
        // fall back to the "generic" system
        _ => "generic",
    };

    // build the CC and MYCFLAGS parameters
    let mut cc = OsString::from("CC=");
    cc.push(tooling.path());
    let mut cflags = OsString::from("MYCFLAGS=");
    for arg in tooling.args() {
        cflags.push(arg);
        cflags.push(" ");
    }

    // call the makefile
    let mut command = Command::new("make");
    for &(ref key, ref val) in tooling.env() {
        command.env(key, val);
    }
    command.current_dir(dir)
        .arg(platform)
        .arg(cc)
        .arg(cflags)
        .execute()
}

/// The command to fetch a URL (e.g. with wget) specialized for different
/// OSes.
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos")))]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("wget").arg(url).current_dir(cwd).execute()
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("fetch").arg(url).current_dir(cwd).execute()
}

#[cfg(target_os = "macos")]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("curl").arg("-O").arg(url).current_dir(cwd).execute()
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() -> io::Result<()> {
    let lua_version = match env::var_os("LUA_VERSION") {
        Some(lua_version) => lua_version,
        None => From::from("5.3.0"),
    };
    let lua_version = lua_version.to_str().unwrap();
    let lua_dir = format!("lua-{}", lua_version);
    let lua_tarball = format!("{}.tar.gz", lua_dir);
    let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let tooling = gcc::Config::new().get_compiler();

    // Ensure the presence of liblua.a
    if !fs::metadata(build_dir.join(&format!("{}/src/liblua.a", lua_dir))).is_ok() {
        try!(fs::create_dir_all(&build_dir));

        // Download lua if it hasn't been already
        if !fs::metadata(build_dir.join(&lua_tarball)).is_ok() {
            match env::var_os("LUA_LOCAL_SOURCE") {
                Some(lua_source_path) => {
                    try!(Command::new("cp")
                         .arg(&PathBuf::from(lua_source_path).join(&lua_tarball))
                         .arg(".")
                         .current_dir(&build_dir)
                         .execute());
                }
                None => {
                    try!(fetch_in_dir(&format!(
                        "http://www.lua.org/ftp/{}", lua_tarball), &build_dir));
                }
            }
            try!(Command::new("tar")
                .arg("xzf")
                .arg(&lua_tarball)
                .current_dir(&build_dir)
                .execute());
        }
        // Compile lua
        try!(build_lua(&tooling, &build_dir.join(&lua_dir)));
    }

    // Ensure the presence of glue.rs
    if !fs::metadata(build_dir.join("glue.rs")).is_ok() {
        // Compile glue.c
        let glue = build_dir.join("glue");
        try!(Command::new("gcc")
            .arg("-I").arg(build_dir.join(&format!("{}/src", lua_dir)))
            .arg("src/glue/glue.c")
            .arg("-o").arg(&glue)
            .execute());
        try!(Command::new(glue)
            .arg(build_dir.join("glue.rs"))
            .execute());
    }

    // Output build information
    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}/{}/src", build_dir.to_str().unwrap(), lua_dir);

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<|endoftext|>


<commit_before>// spell-checker:ignore (vars) krate

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

pub fn main() {
    if let Ok(profile) = env::var("PROFILE") {
        println!("cargo:rustc-cfg=build={:?}", profile);
    }

    let env_feature_prefix: &str = "CARGO_FEATURE_";
    let feature_prefix: &str = "feat_";
    let override_prefix: &str = "uu_";

    let out_dir = env::var("OUT_DIR").unwrap();

    let mut crates = Vec::new();
    for (key, val) in env::vars() {
        if val == "1" && key.starts_with(env_feature_prefix) {
            let krate = key[env_feature_prefix.len()..].to_lowercase();
            match krate.as_ref() {
                "default" | "macos" | "unix" | "windows" => continue, // common/standard feature names
                "nightly" | "test_unimplemented" => continue,         // crate-local custom features
                "test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test'
                s if s.starts_with(feature_prefix) => continue, // crate feature sets
                _ => {}             // util feature name
            }
            crates.push(krate.to_string());
        }
    }
    crates.sort();

    let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();

    mf.write_all(
        "type UtilityMap = HashMap<&'static str, fn(Vec<String>) -> i32>;\n\
        \n\
        fn util_map() -> UtilityMap {\n\
        \tlet mut map: UtilityMap = HashMap::new();\n\
        "
        .as_bytes(),
    )
    .unwrap();

    for krate in crates {
        match krate.as_ref() {
            k if k.starts_with(override_prefix) => mf
                .write_all(
                    format!(
                        "\tmap.insert(\"{k}\", {krate}::uumain);\n",
                        k = krate[override_prefix.len()..].to_string(),
                        krate = krate
                    )
                    .as_bytes(),
                )
                .unwrap(),
            "false" | "true" => mf
                .write_all(
                    format!(
                        "\tmap.insert(\"{krate}\", r#{krate}::uumain);\n",
                        krate = krate
                    )
                    .as_bytes(),
                )
                .unwrap(),
            "hashsum" => mf
                .write_all(
                    format!(
                        "\
                        \tmap.insert(\"{krate}\", {krate}::uumain);\n\
                        \t\tmap.insert(\"md5sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha1sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha224sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha256sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha384sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha512sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3-224sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3-256sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3-384sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3-512sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"shake128sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"shake256sum\", {krate}::uumain);\n\
                        ",
                        krate = krate
                    )
                    .as_bytes(),
                )
                .unwrap(),
            _ => mf
                .write_all(
                    format!(
                        "\tmap.insert(\"{krate}\", {krate}::uumain);\n",
                        krate = krate
                    )
                    .as_bytes(),
                )
                .unwrap(),
        }
    }

    mf.write_all(b"map\n}\n").unwrap();

    mf.flush().unwrap();
}
<commit_msg>maint/build ~ (build.rs) support new tests directory organization<commit_after>// spell-checker:ignore (vars) krate

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

pub fn main() {
    // println!("cargo:warning=Running build.rs");

    if let Ok(profile) = env::var("PROFILE") {
        println!("cargo:rustc-cfg=build={:?}", profile);
    }

    let env_feature_prefix: &str = "CARGO_FEATURE_";
    let feature_prefix: &str = "feat_";
    let override_prefix: &str = "uu_";

    let out_dir = env::var("OUT_DIR").unwrap();
    // println!("cargo:warning=out_dir={}", out_dir);
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap().replace("\\", "/");
    // println!("cargo:warning=manifest_dir={}", manifest_dir);
    let util_tests_dir = format!("{}/tests/by-util", manifest_dir);
    // println!("cargo:warning=util_tests_dir={}", util_tests_dir);

    let mut crates = Vec::new();
    for (key, val) in env::vars() {
        if val == "1" && key.starts_with(env_feature_prefix) {
            let krate = key[env_feature_prefix.len()..].to_lowercase();
            match krate.as_ref() {
                "default" | "macos" | "unix" | "windows" => continue, // common/standard feature names
                "nightly" | "test_unimplemented" => continue,         // crate-local custom features
                "test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test'
                s if s.starts_with(feature_prefix) => continue, // crate feature sets
                _ => {}             // util feature name
            }
            crates.push(krate.to_string());
        }
    }
    crates.sort();

    let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();
    let mut tf = File::create(Path::new(&out_dir).join("test_modules.rs")).unwrap();

    mf.write_all(
        "type UtilityMap = HashMap<&'static str, fn(Vec<String>) -> i32>;\n\
        \n\
        fn util_map() -> UtilityMap {\n\
        \tlet mut map: UtilityMap = HashMap::new();\n\
        "
        .as_bytes(),
    )
    .unwrap();

    for krate in crates {
        match krate.as_ref() {
            k if k.starts_with(override_prefix) => {
                mf.write_all(
                    format!(
                        "\tmap.insert(\"{k}\", {krate}::uumain);\n",
                        k = krate[override_prefix.len()..].to_string(),
                        krate = krate
                    )
                    .as_bytes(),
                )
                .unwrap();
                tf.write_all(
                    format!(
                        "#[path=\"{dir}/test_{k}.rs\"]\nmod test_{k};\n",
                        k = krate[override_prefix.len()..].to_string(),
                        dir = util_tests_dir,
                    )
                    .as_bytes(),
                )
                .unwrap()
            }
            "false" | "true" => {
                mf.write_all(
                    format!(
                        "\tmap.insert(\"{krate}\", r#{krate}::uumain);\n",
                        krate = krate
                    )
                    .as_bytes(),
                )
                .unwrap();
                tf.write_all(
                    format!(
                        "#[path=\"{dir}/test_{krate}.rs\"]\nmod test_{krate};\n",
                        krate = krate,
                        dir = util_tests_dir,
                    )
                    .as_bytes(),
                )
                .unwrap()
            }
            "hashsum" => {
                mf.write_all(
                    format!(
                        "\
                        \tmap.insert(\"{krate}\", {krate}::uumain);\n\
                        \t\tmap.insert(\"md5sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha1sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha224sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha256sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha384sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha512sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3-224sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3-256sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3-384sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"sha3-512sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"shake128sum\", {krate}::uumain);\n\
                        \t\tmap.insert(\"shake256sum\", {krate}::uumain);\n\
                        ",
                        krate = krate
                    )
                    .as_bytes(),
                )
                .unwrap();
                tf.write_all(
                    format!(
                        "#[path=\"{dir}/test_{krate}.rs\"]\nmod test_{krate};\n",
                        krate = krate,
                        dir = util_tests_dir,
                    )
                    .as_bytes(),
                )
                .unwrap()
            }
            _ => {
                mf.write_all(
                    format!(
                        "\tmap.insert(\"{krate}\", {krate}::uumain);\n",
                        krate = krate
                    )
                    .as_bytes(),
                )
                .unwrap();
                tf.write_all(
                    format!(
                        "#[path=\"{dir}/test_{krate}.rs\"]\nmod test_{krate};\n",
                        krate = krate,
                        dir = util_tests_dir,
                    )
                    .as_bytes(),
                )
                .unwrap()
            }
        }
    }

    mf.write_all(b"map\n}\n").unwrap();

    mf.flush().unwrap();
    tf.flush().unwrap();
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::path::Path;

fn main() {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("words.rs");

    let list_paths = [
        "words/small/adjectives.txt",
        "words/small/adverbs.txt",
        "words/small/names.txt",
        "words/medium/adjectives.txt",
        "words/medium/adverbs.txt",
        "words/medium/names.txt",
        "words/large/adjectives.txt",
        "words/large/adverbs.txt",
        "words/large/names.txt",
    ];

    let mut lines: Vec<String> = vec![];

    for list_path in list_paths {
        println!("cargo:rerun-if-changed={list_path}");
        let list_raw = fs::read_to_string(list_path).unwrap();
        let list = list_raw.split_whitespace().collect::<Vec<_>>();
        let list_path_parts = list_path.split(&['/', '.']).collect::<Vec<_>>();
        let [_, list_size, list_name, _] = list_path_parts[..] else { panic!() };
        lines.push(format!("const {list_name}_{list_size}: [&str; _] = ["));
        lines.extend(list.iter().map(|word| format!("    \"{word}\",")));
        lines.push("];".into());
    }

    fs::write(&dest_path, lines.join("\n")).unwrap();
    println!("cargo:rerun-if-changed=build.rs");
}
<commit_msg>Organise into modules<commit_after>use std::env;
use std::fs;
use std::path::Path;

fn main() {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("words.rs");

    let mut lines: Vec<String> = vec![];

    let list_sizes = ["small", "medium", "large"];
    let list_names = ["adjectives", "adverbs", "names"];

    for list_size in list_sizes {
        lines.push(format!("pub mod {list_size} {{"));
        for list_name in list_names {
            let list_path = format!("words/{list_size}/{list_name}.txt");
            println!("cargo:rerun-if-changed={list_path}");
            let list_raw = fs::read_to_string(list_path).unwrap();
            let list = list_raw.split_whitespace().collect::<Vec<_>>();
            lines.push(format!(
                "  pub const {}: [&str; {}] = [",
                list_name.to_uppercase(),
                list.len()
            ));
            lines.extend(list.iter().map(|word| format!("    \"{word}\",")));
            lines.push(format!("  ];"));
        }
        lines.push(format!("}}"));
    }

    fs::write(&dest_path, lines.join("\n")).unwrap();
    println!("cargo:rerun-if-changed=build.rs");
}
<|endoftext|>


<commit_before>extern crate "pkg-config" as pkg_config;

fn main() {
    pkg_config::find_library("gobject-2.0").unwrap();
}
<commit_msg>Require gobject 2.36<commit_after>extern crate "pkg-config" as pkg_config;

use pkg_config::Config;

fn main() {
    Config::new().atleast_version("2.36").find("gobject-2.0").unwrap();
}
<|endoftext|>


<commit_before>use proc_macro2::{TokenStream, TokenTree};
use quote::{format_ident, quote, ToTokens};
use std::iter::once;
use syn::parse::{Nothing, Parse, ParseStream};
use syn::{Attribute, Error, Field, Ident, Index, LitInt, LitStr, Result, Token};

pub struct Display {
    pub fmt: LitStr,
    pub args: TokenStream,
}

impl Parse for Display {
    fn parse(input: ParseStream) -> Result<Self> {
        let fmt: LitStr = input.parse()?;

        let mut args = TokenStream::new();
        let mut last_is_comma = false;
        while !input.is_empty() {
            if last_is_comma && input.peek(Token![.]) {
                if input.peek2(Ident) {
                    input.parse::<Token![.]>()?;
                    last_is_comma = false;
                    continue;
                }
                if input.peek2(LitInt) {
                    input.parse::<Token![.]>()?;
                    let int: Index = input.parse()?;
                    let ident = format_ident!("_{}", int.index, span = int.span);
                    args.extend(once(TokenTree::Ident(ident)));
                    last_is_comma = false;
                    continue;
                }
            }
            last_is_comma = input.peek(Token![,]);
            let token: TokenTree = input.parse()?;
            args.extend(once(token));
        }

        let mut display = Display { fmt, args };
        display.expand_shorthand();
        Ok(display)
    }
}

impl ToTokens for Display {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let fmt = &self.fmt;
        let args = &self.args;
        tokens.extend(quote! {
            write!(formatter, #fmt #args)
        });
    }
}

pub fn is_source(field: &Field) -> Result<bool> {
    for attr in &field.attrs {
        if attr.path.is_ident("source") {
            syn::parse2::<Nothing>(attr.tokens.clone())?;
            return Ok(true);
        }
    }
    Ok(false)
}

pub fn display(attrs: &[Attribute]) -> Result<Option<Display>> {
    let mut display = None;

    for attr in attrs {
        if attr.path.is_ident("error") {
            if display.is_some() {
                return Err(Error::new_spanned(
                    attr,
                    "only one #[error(...)] attribute is allowed",
                ));
            }
            display = Some(attr.parse_args()?);
        }
    }

    Ok(display)
}
<commit_msg>Extract the fmt args parser to a function<commit_after>use proc_macro2::{TokenStream, TokenTree};
use quote::{format_ident, quote, ToTokens};
use std::iter::once;
use syn::parse::{Nothing, Parse, ParseStream};
use syn::{Attribute, Error, Field, Ident, Index, LitInt, LitStr, Result, Token};

pub struct Display {
    pub fmt: LitStr,
    pub args: TokenStream,
}

impl Parse for Display {
    fn parse(input: ParseStream) -> Result<Self> {
        let fmt: LitStr = input.parse()?;
        let args = input.call(parse_token_expr)?;
        let mut display = Display { fmt, args };
        display.expand_shorthand();
        Ok(display)
    }
}

fn parse_token_expr(input: ParseStream) -> Result<TokenStream> {
    let mut tokens = TokenStream::new();
    let mut last_is_comma = false;
    while !input.is_empty() {
        if last_is_comma && input.peek(Token![.]) {
            if input.peek2(Ident) {
                input.parse::<Token![.]>()?;
                last_is_comma = false;
                continue;
            }
            if input.peek2(LitInt) {
                input.parse::<Token![.]>()?;
                let int: Index = input.parse()?;
                let ident = format_ident!("_{}", int.index, span = int.span);
                tokens.extend(once(TokenTree::Ident(ident)));
                last_is_comma = false;
                continue;
            }
        }
        last_is_comma = input.peek(Token![,]);
        let token: TokenTree = input.parse()?;
        tokens.extend(once(token));
    }
    Ok(tokens)
}

impl ToTokens for Display {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let fmt = &self.fmt;
        let args = &self.args;
        tokens.extend(quote! {
            write!(formatter, #fmt #args)
        });
    }
}

pub fn is_source(field: &Field) -> Result<bool> {
    for attr in &field.attrs {
        if attr.path.is_ident("source") {
            syn::parse2::<Nothing>(attr.tokens.clone())?;
            return Ok(true);
        }
    }
    Ok(false)
}

pub fn display(attrs: &[Attribute]) -> Result<Option<Display>> {
    let mut display = None;

    for attr in attrs {
        if attr.path.is_ident("error") {
            if display.is_some() {
                return Err(Error::new_spanned(
                    attr,
                    "only one #[error(...)] attribute is allowed",
                ));
            }
            display = Some(attr.parse_args()?);
        }
    }

    Ok(display)
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

mod from_str;
// mod relabel;

pub mod cli;

/// Variable or terminal symbol in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum VarT<T> {
    /// `Var(i, j)` represents the `j`th component of the `i`th successor.
    /// Indexing starts from `0`.
    Var(usize, usize),
    T(T),
}

/// Composition function in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Composition<T> {
    pub composition: Vec<Vec<VarT<T>>>,
}

impl<T> From<Vec<Vec<VarT<T>>>> for Composition<T> {
    fn from(encapsulated_value: Vec<Vec<VarT<T>>>) -> Self {
        Composition { composition: encapsulated_value }
    }
}

/// Rule of a weighted MCFG.
#[derive(Debug, PartialOrd, Ord, Clone)]
pub struct PMCFGRule<N, T, W> {
    pub head: N,
    pub tail: Vec<N>,
    pub composition: Composition<T>,
    pub weight: W,
}

impl<N, T, W> PMCFGRule<N, T, W>
    where T: Clone,
          W: Clone,
{
    pub fn map_nonterminals<F, M>(&self, f: F) -> PMCFGRule<M, T, W>
        where F: Fn(&N) -> M,
    {
        PMCFGRule {
            head: f(&self.head),
            tail: self.tail.iter().map(f).collect(),
            composition: self.composition.clone(),
            weight: self.weight.clone()
        }
    }
}

/// A weighted MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct PMCFG<N, T, W> {
    pub _dummy: PhantomData<T>,
    pub initial: Vec<N>,
    pub rules: Vec<PMCFGRule<N, T, W>>,
}

impl<N: Hash, T: Hash, W> Hash for PMCFGRule<N, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.head.hash(state);
        self.tail.hash(state);
        self.composition.hash(state);
    }
}

impl<N: PartialEq, T: PartialEq, W> PartialEq for PMCFGRule<N, T, W> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head && self.tail == other.tail && self.composition == other.composition
    }
}

impl<N: Eq, T: Eq, W> Eq for PMCFGRule<N, T, W> {}

impl<T: fmt::Display> fmt::Display for Composition<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter0 = self.composition.iter().peekable();
        let mut iter1;

        buffer.push_str("[");
        while let Some(proj) = iter0.next() {
            iter1 = proj.into_iter().peekable();
            buffer.push_str("[");
            while let Some(vart) = iter1.next() {
                buffer.push_str(format!("{}", vart).as_str());
                if iter1.peek().is_some() {
                    buffer.push_str(", ");
                }
            }
            buffer.push_str("]");
            if iter0.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]");

        write!(f, "{}", buffer)
    }
}

impl<T: fmt::Display> fmt::Display for VarT<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VarT::Var(i, j) => {
                write!(f, "Var {} {}", i, j)
            },
            VarT::T(ref x) => {
                write!(f, "T \"{}\"", x)
            },
        }
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFGRule<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
           let mut buffer = "".to_string();

           let mut iter = self.tail.iter().peekable();

           buffer.push_str("(");
           while let Some(nt) = iter.next() {
               buffer.push_str(format!("\"{}\"", nt).as_str());
               if iter.peek().is_some() {
                   buffer.push_str(", ");
               }
           }
           buffer.push_str(")");

        write!(f, "\"{}\" → {} {}  # {}", self.head, self.composition, buffer, self.weight)
        // write!(f, "\"{}\" → {}", self.head, self.composition)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFG<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter = self.initial.iter().peekable();

        buffer.push_str("initial: [");
        while let Some(nt) = iter.next() {
            buffer.push_str(format!("\"{}\"", nt).as_str());
            if iter.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]\n\n");

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

pub fn evaluate<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>) -> Composition<T> {
    evaluate_pos(term_map, vec![])
}

pub fn evaluate_pos<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>, address: Vec<usize>) -> Composition<T> {
    let unexpanded_composition = &term_map.get(&address).unwrap().composition;
    let mut expanded_nonterminals: BTreeMap<_, Vec<Vec<VarT<T>>>> = BTreeMap::new();
    let mut expanded_composition = Vec::new();

    for component in unexpanded_composition {
        let mut expanded_component = Vec::new();

        for variable in component {
            match variable {
                &VarT::Var(num_nonter, num_compon) => {
                    if !expanded_nonterminals.contains_key(&num_nonter) {
                        let mut child_address = address.clone();
                        child_address.push(num_nonter);
                        let nonter_compos = evaluate_pos(term_map, child_address).composition;
                        expanded_nonterminals.insert(num_nonter, nonter_compos);
                    }

                    let nonter_compos = expanded_nonterminals.get(&num_nonter).unwrap();

                    if let Some(compon) = nonter_compos.get(num_compon) {
                        for terminal in compon {
                            expanded_component.push(terminal.clone());
                        }
                    } else {
                        panic!("{:?}: use of {}-th component of nonterminal {} that has only {} components!",
                               unexpanded_composition, num_compon, num_nonter, nonter_compos.len());
                    }

                },
                &VarT::T(ref terminal) => {
                    expanded_component.push(VarT::T(terminal.clone()));
                },
            }
        }

        expanded_composition.push(expanded_component);
    }

    Composition::from(expanded_composition)
}
<commit_msg>src/pmcfg/mod.rs: added tests for evaluate()<commit_after>use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

mod from_str;
// mod relabel;

pub mod cli;

/// Variable or terminal symbol in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum VarT<T> {
    /// `Var(i, j)` represents the `j`th component of the `i`th successor.
    /// Indexing starts from `0`.
    Var(usize, usize),
    T(T),
}

/// Composition function in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Composition<T> {
    pub composition: Vec<Vec<VarT<T>>>,
}

impl<T> From<Vec<Vec<VarT<T>>>> for Composition<T> {
    fn from(encapsulated_value: Vec<Vec<VarT<T>>>) -> Self {
        Composition { composition: encapsulated_value }
    }
}

/// Rule of a weighted MCFG.
#[derive(Debug, PartialOrd, Ord, Clone)]
pub struct PMCFGRule<N, T, W> {
    pub head: N,
    pub tail: Vec<N>,
    pub composition: Composition<T>,
    pub weight: W,
}

impl<N, T, W> PMCFGRule<N, T, W>
    where T: Clone,
          W: Clone,
{
    pub fn map_nonterminals<F, M>(&self, f: F) -> PMCFGRule<M, T, W>
        where F: Fn(&N) -> M,
    {
        PMCFGRule {
            head: f(&self.head),
            tail: self.tail.iter().map(f).collect(),
            composition: self.composition.clone(),
            weight: self.weight.clone()
        }
    }
}

/// A weighted MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct PMCFG<N, T, W> {
    pub _dummy: PhantomData<T>,
    pub initial: Vec<N>,
    pub rules: Vec<PMCFGRule<N, T, W>>,
}

impl<N: Hash, T: Hash, W> Hash for PMCFGRule<N, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.head.hash(state);
        self.tail.hash(state);
        self.composition.hash(state);
    }
}

impl<N: PartialEq, T: PartialEq, W> PartialEq for PMCFGRule<N, T, W> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head && self.tail == other.tail && self.composition == other.composition
    }
}

impl<N: Eq, T: Eq, W> Eq for PMCFGRule<N, T, W> {}

impl<T: fmt::Display> fmt::Display for Composition<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter0 = self.composition.iter().peekable();
        let mut iter1;

        buffer.push_str("[");
        while let Some(proj) = iter0.next() {
            iter1 = proj.into_iter().peekable();
            buffer.push_str("[");
            while let Some(vart) = iter1.next() {
                buffer.push_str(format!("{}", vart).as_str());
                if iter1.peek().is_some() {
                    buffer.push_str(", ");
                }
            }
            buffer.push_str("]");
            if iter0.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]");

        write!(f, "{}", buffer)
    }
}

impl<T: fmt::Display> fmt::Display for VarT<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VarT::Var(i, j) => {
                write!(f, "Var {} {}", i, j)
            },
            VarT::T(ref x) => {
                write!(f, "T \"{}\"", x)
            },
        }
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFGRule<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
           let mut buffer = "".to_string();

           let mut iter = self.tail.iter().peekable();

           buffer.push_str("(");
           while let Some(nt) = iter.next() {
               buffer.push_str(format!("\"{}\"", nt).as_str());
               if iter.peek().is_some() {
                   buffer.push_str(", ");
               }
           }
           buffer.push_str(")");

        write!(f, "\"{}\" → {} {}  # {}", self.head, self.composition, buffer, self.weight)
        // write!(f, "\"{}\" → {}", self.head, self.composition)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFG<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter = self.initial.iter().peekable();

        buffer.push_str("initial: [");
        while let Some(nt) = iter.next() {
            buffer.push_str(format!("\"{}\"", nt).as_str());
            if iter.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]\n\n");

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

pub fn evaluate<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>) -> Composition<T> {
    evaluate_pos(term_map, vec![])
}

pub fn evaluate_pos<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>, address: Vec<usize>) -> Composition<T> {
    let unexpanded_composition = &term_map.get(&address).unwrap().composition;
    let mut expanded_nonterminals: BTreeMap<_, Vec<Vec<VarT<T>>>> = BTreeMap::new();
    let mut expanded_composition = Vec::new();

    for component in unexpanded_composition {
        let mut expanded_component = Vec::new();

        for variable in component {
            match variable {
                &VarT::Var(num_nonter, num_compon) => {
                    if !expanded_nonterminals.contains_key(&num_nonter) {
                        let mut child_address = address.clone();
                        child_address.push(num_nonter);
                        let nonter_compos = evaluate_pos(term_map, child_address).composition;
                        expanded_nonterminals.insert(num_nonter, nonter_compos);
                    }

                    let nonter_compos = expanded_nonterminals.get(&num_nonter).unwrap();

                    if let Some(compon) = nonter_compos.get(num_compon) {
                        for terminal in compon {
                            expanded_component.push(terminal.clone());
                        }
                    } else {
                        panic!("{:?}: use of {}-th component of nonterminal {} that has only {} components!",
                               unexpanded_composition, num_compon, num_nonter, nonter_compos.len());
                    }

                },
                &VarT::T(ref terminal) => {
                    expanded_component.push(VarT::T(terminal.clone()));
                },
            }
        }

        expanded_composition.push(expanded_component);
    }

    Composition::from(expanded_composition)
}

#[cfg(test)]
mod tests {
    use super::*;
    use VarT::{Var, T};

    #[test]
    fn test_evaluate() {
        let mut term_map: BTreeMap<Vec<usize>, _> = BTreeMap::new();
        term_map.insert(vec![], Composition::from(vec![
                vec![Var(0,0), Var(1,0), Var(0,1), Var(1,1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
                vec![Var(1,0), Var(0,0)],
                vec![Var(2,0), Var(0,1)]
        ]));
        term_map.insert(vec![0,0], Composition::from(vec![
                vec![Var(1,0), Var(0,0)],
                vec![Var(2,0), Var(0,1)]
        ]));
        term_map.insert(vec![0,0,0], Composition::from(vec![
                vec![],
                vec![]
        ]));
        term_map.insert(vec![0,0,1], Composition::from(vec![
                vec![T('a')]
        ]));
        term_map.insert(vec![0,0,2], Composition::from(vec![
                vec![T('c')]
        ]));
        term_map.insert(vec![0,1], Composition::from(vec![
                vec![T('a')]
        ]));
        term_map.insert(vec![0,2], Composition::from(vec![
                vec![T('c')]
        ]));
        term_map.insert(vec![1], Composition::from(vec![
                vec![Var(1,0), Var(0,0)],
                vec![Var(2,0), Var(0,1)]
        ]));
        term_map.insert(vec![1,0], Composition::from(vec![
                vec![],
                vec![]
        ]));
        term_map.insert(vec![1,1], Composition::from(vec![
                vec![T('b')]
        ]));
        term_map.insert(vec![1,2], Composition::from(vec![
                vec![T('d')]
        ]));

        let expanded_compos = Composition::from(vec![
            vec![T('a'), T('a'), T('b'), T('c'), T('c'), T('d')]
        ]);

        assert_eq!(expanded_compos, evaluate(&term_map));
    }

    #[test]
    #[should_panic(expected =
        "[[Var(0, 0), Var(0, 1)]]: use of 1-th component of nonterminal 0 that has only 1 components!"
    )]
    fn test_evaluate_invalid_composition() {
        let mut term_map: BTreeMap<Vec<usize>, _> = BTreeMap::new();
        term_map.insert(vec![], Composition::from(vec![
                vec![Var(0,0), Var(0,1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
                vec![T('a')]
        ]));

        evaluate(&term_map);
    }
}
<|endoftext|>


<commit_before>
use qvariant::*;
use types::*;
use qurl::*;

extern "C" {
    fn dos_qapplication_create();
    fn dos_qapplication_exec();
    fn dos_qapplication_quit();
    fn dos_qapplication_delete();

    fn dos_qqmlapplicationengine_create() -> DosQmlApplicationEngine;
    fn dos_qqmlapplicationengine_load(vptr: DosQmlApplicationEngine, filename: DosCStr);
    fn dos_qqmlapplicationengine_load_url(vptr: DosQmlApplicationEngine, url: DosQUrl);
    fn dos_qqmlapplicationengine_load_data(vptr: DosQmlApplicationEngine, data: DosCStr);
    fn dos_qqmlapplicationengine_add_import_path(vptr: DosQmlApplicationEngine, path: DosCStr);
    fn dos_qqmlapplicationengine_context(vptr: DosQmlApplicationEngine) -> DosQQmlContext;
    fn dos_qqmlapplicationengine_delete(vptr: DosQmlApplicationEngine);

    fn dos_qqmlcontext_setcontextproperty(vptr: DosQQmlContext,
                                          name: DosCStr,
                                          value: DosQVariant);

}

/// Provides an entry point for building QML applications from Rust
pub struct QmlEngine {
    ptr: DosQmlApplicationEngine,
    stored: Vec<QVariant>,
}

impl QmlEngine {
    /// Creates a QML context of a non-headless application
    pub fn new() -> Self {
        unsafe {
            dos_qapplication_create();
            QmlEngine {
                ptr: dos_qqmlapplicationengine_create(),
                stored: Vec::new(),
            }
        }
    }

    /// Loads a file as a qml file
    pub fn load_file(&mut self, path: &str) {
        let path_raw = ::std::env::current_dir().unwrap().join(path);
        let path = if cfg!(windows) {
            format!("file:///{}", path_raw.display())
        } else {
            format!("file://{}", path_raw.display())
        };
        unsafe { dos_qqmlapplicationengine_load_url(self.ptr, construct_qurl(&path)) }
    }

    /// Loads qml from a specified url (`file://`, `qrc://`, `http://`)
    pub fn load_url(&mut self, uri: &str) {
        unsafe { dos_qqmlapplicationengine_load_url(self.ptr, construct_qurl(uri)) }
    }

    /// Adds a path to the QML import path
    /// On an "import ModuleName" call QML will additionally search this path for the matching module.
    pub fn add_import_path(&mut self, path: &str) {
        unsafe { dos_qqmlapplicationengine_add_import_path(self.ptr, stoptr(path)) }
    }

    /// Loads a string as a qml file
    pub fn load_data(&mut self, data: &str) {
        unsafe { dos_qqmlapplicationengine_load_data(self.ptr, stoptr(data)) }
    }

    /// Launches the application
    pub fn exec(&mut self) {
        unsafe {
            dos_qapplication_exec();
        }
    }
    /// Closes the application
    pub fn quit(&mut self) {
        unsafe {
            dos_qapplication_quit();
        }
    }

    /// Sets a property for this QML context
    ///
    /// This variant stores qvariant, so it is removed, only when this QmlEngine is removed.
    pub fn set_and_store_property<T: Into<QVariant>>(&mut self, name: &str, value: T) {
        let val = value.into();
        unsafe {
            let context = dos_qqmlapplicationengine_context(self.ptr);
            dos_qqmlcontext_setcontextproperty(context, stoptr(name), get_private_variant(&val));
        }
        self.stored.push(val);
    }

    /// Sets a property for this QML context
    pub fn set_property(&mut self, name: &str, value: &QVariant) {
        unsafe {
            let context = dos_qqmlapplicationengine_context(self.ptr);
            dos_qqmlcontext_setcontextproperty(context, stoptr(name), get_private_variant(value));
        }
    }
}

use utils::*;

impl Default for QmlEngine {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for QmlEngine {
    fn drop(&mut self) {
        unsafe {
            dos_qapplication_quit();
            dos_qqmlapplicationengine_delete(self.ptr);
            dos_qapplication_delete();
        }
    }
}
<commit_msg>Use QGuiApplication, not QApplication, in qmlengine.rs<commit_after>
use qvariant::*;
use types::*;
use qurl::*;

extern "C" {
    fn dos_qguiapplication_create();
    fn dos_qguiapplication_exec();
    fn dos_qguiapplication_quit();
    fn dos_qguiapplication_delete();

    fn dos_qqmlapplicationengine_create() -> DosQmlApplicationEngine;
    fn dos_qqmlapplicationengine_load(vptr: DosQmlApplicationEngine, filename: DosCStr);
    fn dos_qqmlapplicationengine_load_url(vptr: DosQmlApplicationEngine, url: DosQUrl);
    fn dos_qqmlapplicationengine_load_data(vptr: DosQmlApplicationEngine, data: DosCStr);
    fn dos_qqmlapplicationengine_add_import_path(vptr: DosQmlApplicationEngine, path: DosCStr);
    fn dos_qqmlapplicationengine_context(vptr: DosQmlApplicationEngine) -> DosQQmlContext;
    fn dos_qqmlapplicationengine_delete(vptr: DosQmlApplicationEngine);

    fn dos_qqmlcontext_setcontextproperty(vptr: DosQQmlContext,
                                          name: DosCStr,
                                          value: DosQVariant);

}

/// Provides an entry point for building QML applications from Rust
pub struct QmlEngine {
    ptr: DosQmlApplicationEngine,
    stored: Vec<QVariant>,
}

impl QmlEngine {
    /// Creates a QML context of a non-headless application
    pub fn new() -> Self {
        unsafe {
            dos_qguiapplication_create();
            QmlEngine {
                ptr: dos_qqmlapplicationengine_create(),
                stored: Vec::new(),
            }
        }
    }

    /// Loads a file as a qml file
    pub fn load_file(&mut self, path: &str) {
        let path_raw = ::std::env::current_dir().unwrap().join(path);
        let path = if cfg!(windows) {
            format!("file:///{}", path_raw.display())
        } else {
            format!("file://{}", path_raw.display())
        };
        unsafe { dos_qqmlapplicationengine_load_url(self.ptr, construct_qurl(&path)) }
    }

    /// Loads qml from a specified url (`file://`, `qrc://`, `http://`)
    pub fn load_url(&mut self, uri: &str) {
        unsafe { dos_qqmlapplicationengine_load_url(self.ptr, construct_qurl(uri)) }
    }

    /// Adds a path to the QML import path
    /// On an "import ModuleName" call QML will additionally search this path for the matching module.
    pub fn add_import_path(&mut self, path: &str) {
        unsafe { dos_qqmlapplicationengine_add_import_path(self.ptr, stoptr(path)) }
    }

    /// Loads a string as a qml file
    pub fn load_data(&mut self, data: &str) {
        unsafe { dos_qqmlapplicationengine_load_data(self.ptr, stoptr(data)) }
    }

    /// Launches the application
    pub fn exec(&mut self) {
        unsafe {
            dos_qguiapplication_exec();
        }
    }
    /// Closes the application
    pub fn quit(&mut self) {
        unsafe {
            dos_qguiapplication_quit();
        }
    }

    /// Sets a property for this QML context
    ///
    /// This variant stores qvariant, so it is removed, only when this QmlEngine is removed.
    pub fn set_and_store_property<T: Into<QVariant>>(&mut self, name: &str, value: T) {
        let val = value.into();
        unsafe {
            let context = dos_qqmlapplicationengine_context(self.ptr);
            dos_qqmlcontext_setcontextproperty(context, stoptr(name), get_private_variant(&val));
        }
        self.stored.push(val);
    }

    /// Sets a property for this QML context
    pub fn set_property(&mut self, name: &str, value: &QVariant) {
        unsafe {
            let context = dos_qqmlapplicationengine_context(self.ptr);
            dos_qqmlcontext_setcontextproperty(context, stoptr(name), get_private_variant(value));
        }
    }
}

use utils::*;

impl Default for QmlEngine {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for QmlEngine {
    fn drop(&mut self) {
        unsafe {
            dos_qguiapplication_quit();
            dos_qqmlapplicationengine_delete(self.ptr);
            dos_qguiapplication_delete();
        }
    }
}
<|endoftext|>