<commit_before>use crate::convert::*;
use core::hash::Hasher;

/// A `Hasher` for hashing an arbitrary stream of bytes.
///
/// Instances of [`AHasher`] represent state that is updated while hashing data.
///
/// Each method updates the internal state based on the new data provided. Once
/// all of the data has been provided, the resulting hash can be obtained by calling
/// `finish()`
///
/// [Clone] is also provided in case you wish to calculate hashes for two different items that
/// start with the same data.
///
#[derive(Debug, Clone)]
pub struct AHasher {
    buffer: [u64; 2],
    key: u128,
}

impl AHasher {
    /// Creates a new hasher keyed to the provided keys.
    /// # Example
    ///
    /// ```
    /// use std::hash::Hasher;
    /// use ahash::AHasher;
    ///
    /// let mut hasher = AHasher::new_with_keys(123, 456);
    ///
    /// hasher.write_u32(1989);
    /// hasher.write_u8(11);
    /// hasher.write_u8(9);
    /// hasher.write(b"Huh?");
    ///
    /// println!("Hash is {:x}!", hasher.finish());
    /// ```
    #[inline]
    pub fn new_with_keys(key0: u64, key1: u64) -> Self {
        Self {
            buffer: [key0, key1],
            key: [key1, key0].convert(),
        }
    }

    #[cfg(test)]
    pub(crate) fn test_with_keys(key1: u64, key2: u64) -> AHasher {
        use crate::scramble_keys;
        let (k1, k2) = scramble_keys(key1, key2);
        AHasher {
            buffer: [k1, k2],
            key: [k2, k1].convert(),
        }
    }

    #[inline(always)]
    fn hash_in(&mut self, new_value: u128) {
        self.buffer = aeshashx2(self.buffer.convert(), new_value, self.key).convert();
    }

    #[inline(always)]
    fn hash_in_2(&mut self, v1: u128, v2: u128) {
        let updated = aeshash(self.buffer.convert(), v1);
        self.buffer = aeshashx2(updated, v2, updated).convert();
    }
}

#[inline(never)]
#[no_mangle]
fn hash_test_aes(input: &[u8]) -> u64 {
    let mut a = AHasher::new_with_keys(67, 87);
    a.write(input);
    a.finish()
}

/// Provides methods to hash all of the primitive types.
impl Hasher for AHasher {
    #[inline]
    fn write_u8(&mut self, i: u8) {
        self.write_u128(i as u128);
    }

    #[inline]
    fn write_u16(&mut self, i: u16) {
        self.write_u128(i as u128);
    }

    #[inline]
    fn write_u32(&mut self, i: u32) {
        self.write_u128(i as u128);
    }

    #[inline]
    fn write_u128(&mut self, i: u128) {
        self.hash_in(i);
    }

    #[inline]
    fn write_usize(&mut self, i: usize) {
        self.write_u64(i as u64);
    }

    #[inline]
    fn write_u64(&mut self, i: u64) {
        self.write_u128(i as u128);
    }

    #[inline]
    fn write(&mut self, input: &[u8]) {
        let mut data = input;
        let length = data.len() as u64;
        //This will be scrambled by the first AES round in any branch.
        self.buffer[1] = self.buffer[1].wrapping_add(length);
        //A 'binary search' on sizes reduces the number of comparisons.
        if data.len() <= 8 {
            let value: [u64; 2] = if data.len() >= 2 {
                if data.len() >= 4 {
                    //len 4-8
                    [data.read_u32().0 as u64, data.read_last_u32() as u64]
                } else {
                    //len 2-3
                    [data.read_u16().0 as u64, data[data.len() - 1] as u64]
                }
            } else {
                if data.len() > 0 {
                    [data[0] as u64, 0]
                } else {
                    [0, 0]
                }
            };
            self.hash_in(value.convert());
        } else {
            if data.len() > 32 {
                if data.len() > 64 {
                    let tail = data.read_last_u128x4();
                    let mut par_block: u128 = self.buffer.convert();
                    while data.len() > 64 {
                        let (blocks, rest) = data.read_u128x4();
                        data = rest;
                        self.hash_in_2(blocks[0], blocks[1]);
                        par_block = aeshash(par_block, blocks[2]);
                        par_block = aeshashx2(par_block, blocks[3], par_block);
                    }
                    self.hash_in_2(tail[0], tail[1]);
                    par_block = aeshash(par_block, tail[2]);
                    par_block = aeshashx2(par_block, tail[3], par_block);
                    self.hash_in(par_block);
                } else {
                    //len 33-64
                    let (head, _) = data.read_u128x2();
                    let tail = data.read_last_u128x2();
                    self.hash_in_2(head[0], head[1]);
                    self.hash_in_2(tail[0], tail[1]);
                }
            } else {
                if data.len() > 16 {
                    //len 17-32
                    self.hash_in_2(data.read_u128().0, data.read_last_u128());
                } else {
                    //len 9-16
                    let value: [u64; 2] = [data.read_u64().0, data.read_last_u64()];
                    self.hash_in(value.convert());
                }
            }
        }
    }
    #[inline]
    fn finish(&self) -> u64 {
        let result: [u64; 2] = aeshash(self.buffer.convert(), self.key).convert();
        result[0] //.wrapping_add(result[1])
    }
}

#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))]
#[inline(always)]
fn aeshash(value: u128, xor: u128) -> u128 {
    #[cfg(target_arch = "x86")]
    use core::arch::x86::*;
    #[cfg(target_arch = "x86_64")]
    use core::arch::x86_64::*;
    use core::mem::transmute;
    unsafe {
        let value = transmute(value);
        transmute(_mm_aesdec_si128(value, transmute(xor)))
    }
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))]
#[inline(always)]
fn aeshashx2(value: u128, k1: u128, k2: u128) -> u128 {
    #[cfg(target_arch = "x86")]
    use core::arch::x86::*;
    #[cfg(target_arch = "x86_64")]
    use core::arch::x86_64::*;
    use core::mem::transmute;
    unsafe {
        let value = transmute(value);
        let value = _mm_aesdec_si128(value, transmute(k1));
        transmute(_mm_aesdec_si128(value, transmute(k2)))
    }
}

#[cfg(test)]
mod tests {
    use crate::aes_hash::*;
    use crate::convert::Convert;
    use std::collections::HashMap;
    use std::hash::BuildHasherDefault;

    #[cfg(feature = "compile-time-rng")]
    #[test]
    fn test_builder() {
        let mut map = HashMap::<u32, u64, BuildHasherDefault<AHasher>>::default();
        map.insert(1, 3);
    }

    #[cfg(feature = "compile-time-rng")]
    #[test]
    fn test_default() {
        let hasher_a = AHasher::default();
        assert_ne!(0, hasher_a.buffer[0]);
        assert_ne!(0, hasher_a.buffer[1]);
        assert_ne!(hasher_a.buffer[0], hasher_a.buffer[1]);
        let hasher_b = AHasher::default();
        assert_eq!(hasher_a.buffer[0], hasher_b.buffer[0]);
        assert_eq!(hasher_a.buffer[1], hasher_b.buffer[1]);
    }

    #[test]
    fn test_hash() {
        let mut result: [u64; 2] = [0x6c62272e07bb0142, 0x62b821756295c58d];
        let value: [u64; 2] = [1 << 32, 0xFEDCBA9876543210];
        result = aeshash(value.convert(), result.convert()).convert();
        result = aeshash(result.convert(), result.convert()).convert();
        let mut result2: [u64; 2] = [0x6c62272e07bb0142, 0x62b821756295c58d];
        let value2: [u64; 2] = [1, 0xFEDCBA9876543210];
        result2 = aeshash(value2.convert(), result2.convert()).convert();
        result2 = aeshash(result2.convert(), result.convert()).convert();
        let result: [u8; 16] = result.convert();
        let result2: [u8; 16] = result2.convert();
        assert_ne!(hex::encode(result), hex::encode(result2));
    }

    #[test]
    fn test_conversion() {
        let input: &[u8] = "dddddddd".as_bytes();
        let bytes: u64 = as_array!(input, 8).convert();
        assert_eq!(bytes, 0x6464646464646464);
    }
}
<commit_msg>fix import statment<commit_after>use crate::convert::*;
use core::hash::Hasher;

/// A `Hasher` for hashing an arbitrary stream of bytes.
///
/// Instances of [`AHasher`] represent state that is updated while hashing data.
///
/// Each method updates the internal state based on the new data provided. Once
/// all of the data has been provided, the resulting hash can be obtained by calling
/// `finish()`
///
/// [Clone] is also provided in case you wish to calculate hashes for two different items that
/// start with the same data.
///
#[derive(Debug, Clone)]
pub struct AHasher {
    buffer: [u64; 2],
    key: u128,
}

impl AHasher {
    /// Creates a new hasher keyed to the provided keys.
    /// # Example
    ///
    /// ```
    /// use std::hash::Hasher;
    /// use ahash::AHasher;
    ///
    /// let mut hasher = AHasher::new_with_keys(123, 456);
    ///
    /// hasher.write_u32(1989);
    /// hasher.write_u8(11);
    /// hasher.write_u8(9);
    /// hasher.write(b"Huh?");
    ///
    /// println!("Hash is {:x}!", hasher.finish());
    /// ```
    #[inline]
    pub fn new_with_keys(key0: u64, key1: u64) -> Self {
        Self {
            buffer: [key0, key1],
            key: [key1, key0].convert(),
        }
    }

    #[cfg(test)]
    pub(crate) fn test_with_keys(key1: u64, key2: u64) -> AHasher {
        use crate::random_state::scramble_keys;
        let (k1, k2) = scramble_keys(key1, key2);
        AHasher {
            buffer: [k1, k2],
            key: [k2, k1].convert(),
        }
    }

    #[inline(always)]
    fn hash_in(&mut self, new_value: u128) {
        self.buffer = aeshashx2(self.buffer.convert(), new_value, self.key).convert();
    }

    #[inline(always)]
    fn hash_in_2(&mut self, v1: u128, v2: u128) {
        let updated = aeshash(self.buffer.convert(), v1);
        self.buffer = aeshashx2(updated, v2, updated).convert();
    }
}

#[inline(never)]
#[no_mangle]
fn hash_test_aes(input: &[u8]) -> u64 {
    let mut a = AHasher::new_with_keys(67, 87);
    a.write(input);
    a.finish()
}

/// Provides methods to hash all of the primitive types.
impl Hasher for AHasher {
    #[inline]
    fn write_u8(&mut self, i: u8) {
        self.write_u128(i as u128);
    }

    #[inline]
    fn write_u16(&mut self, i: u16) {
        self.write_u128(i as u128);
    }

    #[inline]
    fn write_u32(&mut self, i: u32) {
        self.write_u128(i as u128);
    }

    #[inline]
    fn write_u128(&mut self, i: u128) {
        self.hash_in(i);
    }

    #[inline]
    fn write_usize(&mut self, i: usize) {
        self.write_u64(i as u64);
    }

    #[inline]
    fn write_u64(&mut self, i: u64) {
        self.write_u128(i as u128);
    }

    #[inline]
    fn write(&mut self, input: &[u8]) {
        let mut data = input;
        let length = data.len() as u64;
        //This will be scrambled by the first AES round in any branch.
        self.buffer[1] = self.buffer[1].wrapping_add(length);
        //A 'binary search' on sizes reduces the number of comparisons.
        if data.len() <= 8 {
            let value: [u64; 2] = if data.len() >= 2 {
                if data.len() >= 4 {
                    //len 4-8
                    [data.read_u32().0 as u64, data.read_last_u32() as u64]
                } else {
                    //len 2-3
                    [data.read_u16().0 as u64, data[data.len() - 1] as u64]
                }
            } else {
                if data.len() > 0 {
                    [data[0] as u64, 0]
                } else {
                    [0, 0]
                }
            };
            self.hash_in(value.convert());
        } else {
            if data.len() > 32 {
                if data.len() > 64 {
                    let tail = data.read_last_u128x4();
                    let mut par_block: u128 = self.buffer.convert();
                    while data.len() > 64 {
                        let (blocks, rest) = data.read_u128x4();
                        data = rest;
                        self.hash_in_2(blocks[0], blocks[1]);
                        par_block = aeshash(par_block, blocks[2]);
                        par_block = aeshashx2(par_block, blocks[3], par_block);
                    }
                    self.hash_in_2(tail[0], tail[1]);
                    par_block = aeshash(par_block, tail[2]);
                    par_block = aeshashx2(par_block, tail[3], par_block);
                    self.hash_in(par_block);
                } else {
                    //len 33-64
                    let (head, _) = data.read_u128x2();
                    let tail = data.read_last_u128x2();
                    self.hash_in_2(head[0], head[1]);
                    self.hash_in_2(tail[0], tail[1]);
                }
            } else {
                if data.len() > 16 {
                    //len 17-32
                    self.hash_in_2(data.read_u128().0, data.read_last_u128());
                } else {
                    //len 9-16
                    let value: [u64; 2] = [data.read_u64().0, data.read_last_u64()];
                    self.hash_in(value.convert());
                }
            }
        }
    }
    #[inline]
    fn finish(&self) -> u64 {
        let result: [u64; 2] = aeshash(self.buffer.convert(), self.key).convert();
        result[0] //.wrapping_add(result[1])
    }
}

#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))]
#[inline(always)]
fn aeshash(value: u128, xor: u128) -> u128 {
    #[cfg(target_arch = "x86")]
    use core::arch::x86::*;
    #[cfg(target_arch = "x86_64")]
    use core::arch::x86_64::*;
    use core::mem::transmute;
    unsafe {
        let value = transmute(value);
        transmute(_mm_aesdec_si128(value, transmute(xor)))
    }
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))]
#[inline(always)]
fn aeshashx2(value: u128, k1: u128, k2: u128) -> u128 {
    #[cfg(target_arch = "x86")]
    use core::arch::x86::*;
    #[cfg(target_arch = "x86_64")]
    use core::arch::x86_64::*;
    use core::mem::transmute;
    unsafe {
        let value = transmute(value);
        let value = _mm_aesdec_si128(value, transmute(k1));
        transmute(_mm_aesdec_si128(value, transmute(k2)))
    }
}

#[cfg(test)]
mod tests {
    use crate::aes_hash::*;
    use crate::convert::Convert;
    use std::collections::HashMap;
    use std::hash::BuildHasherDefault;

    #[cfg(feature = "compile-time-rng")]
    #[test]
    fn test_builder() {
        let mut map = HashMap::<u32, u64, BuildHasherDefault<AHasher>>::default();
        map.insert(1, 3);
    }

    #[cfg(feature = "compile-time-rng")]
    #[test]
    fn test_default() {
        let hasher_a = AHasher::default();
        assert_ne!(0, hasher_a.buffer[0]);
        assert_ne!(0, hasher_a.buffer[1]);
        assert_ne!(hasher_a.buffer[0], hasher_a.buffer[1]);
        let hasher_b = AHasher::default();
        assert_eq!(hasher_a.buffer[0], hasher_b.buffer[0]);
        assert_eq!(hasher_a.buffer[1], hasher_b.buffer[1]);
    }

    #[test]
    fn test_hash() {
        let mut result: [u64; 2] = [0x6c62272e07bb0142, 0x62b821756295c58d];
        let value: [u64; 2] = [1 << 32, 0xFEDCBA9876543210];
        result = aeshash(value.convert(), result.convert()).convert();
        result = aeshash(result.convert(), result.convert()).convert();
        let mut result2: [u64; 2] = [0x6c62272e07bb0142, 0x62b821756295c58d];
        let value2: [u64; 2] = [1, 0xFEDCBA9876543210];
        result2 = aeshash(value2.convert(), result2.convert()).convert();
        result2 = aeshash(result2.convert(), result.convert()).convert();
        let result: [u8; 16] = result.convert();
        let result2: [u8; 16] = result2.convert();
        assert_ne!(hex::encode(result), hex::encode(result2));
    }

    #[test]
    fn test_conversion() {
        let input: &[u8] = "dddddddd".as_bytes();
        let bytes: u64 = as_array!(input, 8).convert();
        assert_eq!(bytes, 0x6464646464646464);
    }
}
<|endoftext|>


<commit_before>use cookbook::blake3::blake3_progress;
use cookbook::recipe::{Recipe, SourceRecipe, BuildRecipe};
use cookbook::sha256::sha256_progress;
use std::{
    env,
    fs,
    io::Write,
    path::{Path, PathBuf},
    process::{self, Command, Stdio},
};
use termion::{color, style};

fn create_dir_clean(dir: &Path) -> Result<(), String> {
    if dir.is_dir() {
        // Remove previous directory
        fs::remove_dir_all(&dir).map_err(|err| format!(
            "failed to remove '{}': {}\n{:?}",
            dir.display(),
            err,
            err
        ))?;
    }
    // directory
    fs::create_dir(&dir).map_err(|err| format!(
        "failed to create '{}': {}\n{:?}",
        dir.display(),
        err,
        err
    ))
}

fn rename(src: &Path, dst: &Path) -> Result<(), String> {
    fs::rename(src, dst).map_err(|err| format!(
        "failed to rename '{}' to '{}': {}\n{:?}",
        src.display(),
        dst.display(),
        err,
        err
    ))
}

fn run_command(mut command: process::Command) -> Result<(), String> {
    let status = command.status().map_err(|err| format!(
        "failed to run {:?}: {}\n{:#?}",
        command,
        err,
        err
    ))?;

    if ! status.success() {
        return Err(format!(
            "failed to run {:?}: exited with status {}",
            command,
            status
        ));
    }

    Ok(())
}

fn fetch(recipe_dir: &Path, source: &SourceRecipe) -> Result<PathBuf, String> {
    let source_dir = recipe_dir.join("source");
    match source {
        SourceRecipe::Git { git, upstream, branch, rev } => {
            if ! source_dir.is_dir() {
                // Create source.tmp
                let source_dir_tmp = recipe_dir.join("source.tmp");
                create_dir_clean(&source_dir_tmp)?;

                // Clone the repository to source.tmp
                let mut command = Command::new("git");
                command.arg("clone").arg("--recursive").arg(&git);
                if let Some(branch) = branch {
                    command.arg("--branch").arg(&branch);
                }
                command.arg(&source_dir);
                run_command(command)?;

                // Move source.tmp to source atomically
                rename(&source_dir_tmp, &source_dir)?;
            } else {
                // Reset origin
                let mut command = Command::new("git");
                command.arg("-C").arg(&source_dir);
                command.arg("remote").arg("set-url").arg("origin").arg(&git);
                run_command(command)?;

                // Fetch origin
                let mut command = Command::new("git");
                command.arg("-C").arg(&source_dir);
                command.arg("fetch").arg("origin");
                run_command(command)?;
            }

            if let Some(upstream) = upstream {
                //TODO: set upstream URL
                // git remote set-url upstream "$GIT_UPSTREAM" &> /dev/null ||
                // git remote add upstream "$GIT_UPSTREAM"
                // git fetch upstream
            }

            if let Some(rev) = rev {
                // Check out specified revision
                let mut command = Command::new("git");
                command.arg("-C").arg(&source_dir);
                command.arg("checkout").arg(&rev);
                run_command(command)?;
            } else {
                //TODO: complicated stuff to check and reset branch to origin
                // ORIGIN_BRANCH="$(git branch --remotes | grep '^  origin/HEAD -> ' | cut -d ' ' -f 5-)"
                // if [ -n "$BRANCH" ]
                // then
                //     ORIGIN_BRANCH="origin/$BRANCH"
                // fi
                //
                // if [ "$(git rev-parse HEAD)" != "$(git rev-parse $ORIGIN_BRANCH)" ]
                // then
                //     git checkout -B "$(echo "$ORIGIN_BRANCH" | cut -d / -f 2-)" "$ORIGIN_BRANCH"
                // fi
            }

            // Sync submodules URL
            let mut command = Command::new("git");
            command.arg("-C").arg(&source_dir);
            command.arg("submodule").arg("sync").arg("--recursive");
            run_command(command)?;

            // Update submodules
            let mut command = Command::new("git");
            command.arg("-C").arg(&source_dir);
            command.arg("submodule").arg("update").arg("--init").arg("--recursive");
            run_command(command)?;
        },
        SourceRecipe::Tar { tar, blake3, sha256, patches } => {
            if ! source_dir.is_dir() {
                // Download tar
                //TODO: replace wget
                let source_tar = recipe_dir.join("source.tar");
                if ! source_tar.is_file() {
                    let source_tar_tmp = recipe_dir.join("source.tar.tmp");

                    let mut command = Command::new("wget");
                    command.arg(&tar);
                    command.arg("-O").arg(&source_tar_tmp);
                    run_command(command)?;

                    // Move source.tar.tmp to source.tar atomically
                    rename(&source_tar_tmp, &source_tar)?;
                }

                if let Some(blake3) = blake3 {
                    //TODO
                    // Calculate blake3
                    let source_tar_blake3 = blake3_progress(&source_tar).map_err(|err| format!(
                        "failed to calculate blake3 of '{}': {}\n{:?}",
                        source_tar.display(),
                        err,
                        err
                    ))?;

                    // Check if it matches recipe
                    if &source_tar_blake3 != blake3 {
                        return Err(format!(
                            "calculated blake3 '{}' does not match recipe blake3 '{}'",
                            source_tar_blake3,
                            blake3
                        ));
                    }
                }

                if let Some(sha256) = sha256 {
                    // Calculate sha256
                    let source_tar_sha256 = sha256_progress(&source_tar).map_err(|err| format!(
                        "failed to calculate sha256 of '{}': {}\n{:?}",
                        source_tar.display(),
                        err,
                        err
                    ))?;

                    // Check if it matches recipe
                    if &source_tar_sha256 != sha256 {
                        return Err(format!(
                            "calculated sha256 '{}' does not match recipe sha256 '{}'",
                            source_tar_sha256,
                            sha256
                        ));
                    }
                }

                // Create source.tmp
                let source_dir_tmp = recipe_dir.join("source.tmp");
                create_dir_clean(&source_dir_tmp)?;

                // Extract tar to source.tmp
                //TODO: use tar crate (how to deal with compression?)
                let mut command = Command::new("tar");
                command.arg("--extract");
                command.arg("--verbose");
                command.arg("--file").arg(&source_tar);
                command.arg("--directory").arg(&source_dir_tmp);
                command.arg("--strip-components").arg("1");
                run_command(command)?;

                // Apply patches
                for patch_name in patches {
                    let patch_file = recipe_dir.join(&patch_name);
                    if ! patch_file.is_file() {
                        return Err(format!(
                            "failed to find patch file '{}'",
                            patch_file.display()
                        ));
                    }

                    let patch = fs::read_to_string(&patch_file).map_err(|err| format!(
                        "failed to read patch file '{}': {}\n{:#?}",
                        patch_file.display(),
                        err,
                        err
                    ))?;

                    let mut command = Command::new("patch");
                    command.arg("--directory").arg(&source_dir_tmp);
                    command.arg("--strip=1");
                    command.stdin(Stdio::piped());

                    let mut child = command.spawn().map_err(|err| format!(
                        "failed to spawn {:?}: {}\n{:#?}",
                        command,
                        err,
                        err
                    ))?;

                    if let Some(ref mut stdin) = child.stdin {
                        stdin.write_all(patch.as_bytes()).map_err(|err| format!(
                            "failed to write stdin of {:?}: {}\n{:#?}",
                            command,
                            err,
                            err
                        ))?;
                    } else {
                        return Err(format!(
                            "failed to find stdin of {:?}",
                            command
                        ));
                    }

                    let status = child.wait().map_err(|err| format!(
                        "failed to run {:?}: {}\n{:#?}",
                        command,
                        err,
                        err
                    ))?;

                    if ! status.success() {
                        return Err(format!(
                            "failed to run {:?}: exited with status {}",
                            command,
                            status
                        ));
                    }

                    run_command(command)?;
                }

                // Move source.tmp to source atomically
                rename(&source_dir_tmp, &source_dir)?;
            }
        }
    }

    Ok(source_dir)
}

fn cook(recipe_name: &str) -> Result<(), String> {
    //TODO: sanitize recipe name?
    let recipe_dir = Path::new("recipes").join(recipe_name);
    if ! recipe_dir.is_dir() {
        return Err(format!(
            "failed to find recipe directory '{}'",
            recipe_dir.display()
        ));
    }

    let recipe_file = recipe_dir.join("recipe.toml");
    if ! recipe_file.is_file() {
        return Err(format!(
            "failed to find recipe file '{}'",
            recipe_file.display()
        ));
    }

    let recipe_toml = fs::read_to_string(&recipe_file).map_err(|err| format!(
        "failed to read recipe file '{}': {}\n{:#?}",
        recipe_file.display(),
        err,
        err
    ))?;

    let recipe: Recipe = toml::from_str(&recipe_toml).map_err(|err| format!(
        "failed to parse recipe file '{}': {}\n{:#?}",
        recipe_file.display(),
        err,
        err
    ))?;

    let source_dir = fetch(&recipe_dir, &recipe.source).map_err(|err| format!(
        "failed to fetch: {}",
        err
    ))?;

    Ok(())
}

fn main() {
    let mut matching = true;
    let mut quiet = false;
    let mut recipe_names = Vec::new();
    for arg in env::args().skip(1) {
        match arg.as_str() {
            "--" if matching => matching = false,
            "-q" | "--quiet" if matching => quiet = true,
            _ => recipe_names.push(arg),
        }
    }

    for recipe_name in recipe_names.iter() {
        if ! quiet {
            eprintln!(
                "{}{}cook - {}{}{}",
                style::Bold,
                color::Fg(color::AnsiValue(215)),
                recipe_name,
                color::Fg(color::Reset),
                style::Reset,
            );
        }

        match cook(recipe_name) {
            Ok(()) => {
                if ! quiet {
                    eprintln!(
                        "{}{}cook - {} - successful{}{}",
                        style::Bold,
                        color::Fg(color::AnsiValue(46)),
                        recipe_name,
                        color::Fg(color::Reset),
                        style::Reset,
                    );
                }
            },
            Err(err) => {
                eprintln!(
                    "{}{}cook - {} - error:{}{} {}",
                    style::Bold,
                    color::Fg(color::AnsiValue(196)),
                    recipe_name,
                    color::Fg(color::Reset),
                    style::Reset,
                    err,
                );
                process::exit(1);
            }
        }
    }
}
<commit_msg>Remove duplicate execution of patch command<commit_after>use cookbook::blake3::blake3_progress;
use cookbook::recipe::{Recipe, SourceRecipe, BuildRecipe};
use cookbook::sha256::sha256_progress;
use std::{
    env,
    fs,
    io::Write,
    path::{Path, PathBuf},
    process::{self, Command, Stdio},
};
use termion::{color, style};

fn create_dir_clean(dir: &Path) -> Result<(), String> {
    if dir.is_dir() {
        // Remove previous directory
        fs::remove_dir_all(&dir).map_err(|err| format!(
            "failed to remove '{}': {}\n{:?}",
            dir.display(),
            err,
            err
        ))?;
    }
    // directory
    fs::create_dir(&dir).map_err(|err| format!(
        "failed to create '{}': {}\n{:?}",
        dir.display(),
        err,
        err
    ))
}

fn rename(src: &Path, dst: &Path) -> Result<(), String> {
    fs::rename(src, dst).map_err(|err| format!(
        "failed to rename '{}' to '{}': {}\n{:?}",
        src.display(),
        dst.display(),
        err,
        err
    ))
}

fn run_command(mut command: process::Command) -> Result<(), String> {
    let status = command.status().map_err(|err| format!(
        "failed to run {:?}: {}\n{:#?}",
        command,
        err,
        err
    ))?;

    if ! status.success() {
        return Err(format!(
            "failed to run {:?}: exited with status {}",
            command,
            status
        ));
    }

    Ok(())
}

fn fetch(recipe_dir: &Path, source: &SourceRecipe) -> Result<PathBuf, String> {
    let source_dir = recipe_dir.join("source");
    match source {
        SourceRecipe::Git { git, upstream, branch, rev } => {
            if ! source_dir.is_dir() {
                // Create source.tmp
                let source_dir_tmp = recipe_dir.join("source.tmp");
                create_dir_clean(&source_dir_tmp)?;

                // Clone the repository to source.tmp
                let mut command = Command::new("git");
                command.arg("clone").arg("--recursive").arg(&git);
                if let Some(branch) = branch {
                    command.arg("--branch").arg(&branch);
                }
                command.arg(&source_dir);
                run_command(command)?;

                // Move source.tmp to source atomically
                rename(&source_dir_tmp, &source_dir)?;
            } else {
                // Reset origin
                let mut command = Command::new("git");
                command.arg("-C").arg(&source_dir);
                command.arg("remote").arg("set-url").arg("origin").arg(&git);
                run_command(command)?;

                // Fetch origin
                let mut command = Command::new("git");
                command.arg("-C").arg(&source_dir);
                command.arg("fetch").arg("origin");
                run_command(command)?;
            }

            if let Some(upstream) = upstream {
                //TODO: set upstream URL
                // git remote set-url upstream "$GIT_UPSTREAM" &> /dev/null ||
                // git remote add upstream "$GIT_UPSTREAM"
                // git fetch upstream
            }

            if let Some(rev) = rev {
                // Check out specified revision
                let mut command = Command::new("git");
                command.arg("-C").arg(&source_dir);
                command.arg("checkout").arg(&rev);
                run_command(command)?;
            } else {
                //TODO: complicated stuff to check and reset branch to origin
                // ORIGIN_BRANCH="$(git branch --remotes | grep '^  origin/HEAD -> ' | cut -d ' ' -f 5-)"
                // if [ -n "$BRANCH" ]
                // then
                //     ORIGIN_BRANCH="origin/$BRANCH"
                // fi
                //
                // if [ "$(git rev-parse HEAD)" != "$(git rev-parse $ORIGIN_BRANCH)" ]
                // then
                //     git checkout -B "$(echo "$ORIGIN_BRANCH" | cut -d / -f 2-)" "$ORIGIN_BRANCH"
                // fi
            }

            // Sync submodules URL
            let mut command = Command::new("git");
            command.arg("-C").arg(&source_dir);
            command.arg("submodule").arg("sync").arg("--recursive");
            run_command(command)?;

            // Update submodules
            let mut command = Command::new("git");
            command.arg("-C").arg(&source_dir);
            command.arg("submodule").arg("update").arg("--init").arg("--recursive");
            run_command(command)?;
        },
        SourceRecipe::Tar { tar, blake3, sha256, patches } => {
            if ! source_dir.is_dir() {
                // Download tar
                //TODO: replace wget
                let source_tar = recipe_dir.join("source.tar");
                if ! source_tar.is_file() {
                    let source_tar_tmp = recipe_dir.join("source.tar.tmp");

                    let mut command = Command::new("wget");
                    command.arg(&tar);
                    command.arg("-O").arg(&source_tar_tmp);
                    run_command(command)?;

                    // Move source.tar.tmp to source.tar atomically
                    rename(&source_tar_tmp, &source_tar)?;
                }

                if let Some(blake3) = blake3 {
                    //TODO
                    // Calculate blake3
                    let source_tar_blake3 = blake3_progress(&source_tar).map_err(|err| format!(
                        "failed to calculate blake3 of '{}': {}\n{:?}",
                        source_tar.display(),
                        err,
                        err
                    ))?;

                    // Check if it matches recipe
                    if &source_tar_blake3 != blake3 {
                        return Err(format!(
                            "calculated blake3 '{}' does not match recipe blake3 '{}'",
                            source_tar_blake3,
                            blake3
                        ));
                    }
                }

                if let Some(sha256) = sha256 {
                    // Calculate sha256
                    let source_tar_sha256 = sha256_progress(&source_tar).map_err(|err| format!(
                        "failed to calculate sha256 of '{}': {}\n{:?}",
                        source_tar.display(),
                        err,
                        err
                    ))?;

                    // Check if it matches recipe
                    if &source_tar_sha256 != sha256 {
                        return Err(format!(
                            "calculated sha256 '{}' does not match recipe sha256 '{}'",
                            source_tar_sha256,
                            sha256
                        ));
                    }
                }

                // Create source.tmp
                let source_dir_tmp = recipe_dir.join("source.tmp");
                create_dir_clean(&source_dir_tmp)?;

                // Extract tar to source.tmp
                //TODO: use tar crate (how to deal with compression?)
                let mut command = Command::new("tar");
                command.arg("--extract");
                command.arg("--verbose");
                command.arg("--file").arg(&source_tar);
                command.arg("--directory").arg(&source_dir_tmp);
                command.arg("--strip-components").arg("1");
                run_command(command)?;

                // Apply patches
                for patch_name in patches {
                    let patch_file = recipe_dir.join(&patch_name);
                    if ! patch_file.is_file() {
                        return Err(format!(
                            "failed to find patch file '{}'",
                            patch_file.display()
                        ));
                    }

                    let patch = fs::read_to_string(&patch_file).map_err(|err| format!(
                        "failed to read patch file '{}': {}\n{:#?}",
                        patch_file.display(),
                        err,
                        err
                    ))?;

                    let mut command = Command::new("patch");
                    command.arg("--directory").arg(&source_dir_tmp);
                    command.arg("--strip=1");
                    command.stdin(Stdio::piped());

                    let mut child = command.spawn().map_err(|err| format!(
                        "failed to spawn {:?}: {}\n{:#?}",
                        command,
                        err,
                        err
                    ))?;

                    if let Some(ref mut stdin) = child.stdin {
                        stdin.write_all(patch.as_bytes()).map_err(|err| format!(
                            "failed to write stdin of {:?}: {}\n{:#?}",
                            command,
                            err,
                            err
                        ))?;
                    } else {
                        return Err(format!(
                            "failed to find stdin of {:?}",
                            command
                        ));
                    }

                    let status = child.wait().map_err(|err| format!(
                        "failed to run {:?}: {}\n{:#?}",
                        command,
                        err,
                        err
                    ))?;

                    if ! status.success() {
                        return Err(format!(
                            "failed to run {:?}: exited with status {}",
                            command,
                            status
                        ));
                    }
                }

                // Move source.tmp to source atomically
                rename(&source_dir_tmp, &source_dir)?;
            }
        }
    }

    Ok(source_dir)
}

fn cook(recipe_name: &str) -> Result<(), String> {
    //TODO: sanitize recipe name?
    let recipe_dir = Path::new("recipes").join(recipe_name);
    if ! recipe_dir.is_dir() {
        return Err(format!(
            "failed to find recipe directory '{}'",
            recipe_dir.display()
        ));
    }

    let recipe_file = recipe_dir.join("recipe.toml");
    if ! recipe_file.is_file() {
        return Err(format!(
            "failed to find recipe file '{}'",
            recipe_file.display()
        ));
    }

    let recipe_toml = fs::read_to_string(&recipe_file).map_err(|err| format!(
        "failed to read recipe file '{}': {}\n{:#?}",
        recipe_file.display(),
        err,
        err
    ))?;

    let recipe: Recipe = toml::from_str(&recipe_toml).map_err(|err| format!(
        "failed to parse recipe file '{}': {}\n{:#?}",
        recipe_file.display(),
        err,
        err
    ))?;

    let source_dir = fetch(&recipe_dir, &recipe.source).map_err(|err| format!(
        "failed to fetch: {}",
        err
    ))?;

    Ok(())
}

fn main() {
    let mut matching = true;
    let mut quiet = false;
    let mut recipe_names = Vec::new();
    for arg in env::args().skip(1) {
        match arg.as_str() {
            "--" if matching => matching = false,
            "-q" | "--quiet" if matching => quiet = true,
            _ => recipe_names.push(arg),
        }
    }

    for recipe_name in recipe_names.iter() {
        if ! quiet {
            eprintln!(
                "{}{}cook - {}{}{}",
                style::Bold,
                color::Fg(color::AnsiValue(215)),
                recipe_name,
                color::Fg(color::Reset),
                style::Reset,
            );
        }

        match cook(recipe_name) {
            Ok(()) => {
                if ! quiet {
                    eprintln!(
                        "{}{}cook - {} - successful{}{}",
                        style::Bold,
                        color::Fg(color::AnsiValue(46)),
                        recipe_name,
                        color::Fg(color::Reset),
                        style::Reset,
                    );
                }
            },
            Err(err) => {
                eprintln!(
                    "{}{}cook - {} - error:{}{} {}",
                    style::Bold,
                    color::Fg(color::AnsiValue(196)),
                    recipe_name,
                    color::Fg(color::Reset),
                    style::Reset,
                    err,
                );
                process::exit(1);
            }
        }
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]
extern crate extra;

use std::io;
use std::io::{Write, BufRead, BufReader, Stderr};
use std::env;
use std::fs::File;
use std::path::Path;
use std::error::Error;
use std::process::exit;

use extra::option::OptionalExt;
use extra::io::WriteExt;

static MAN_PAGE: &'static str = /* @MANSTART{grep} */ r#"
NAME
    grep - print lines matching a pattern

SYNOPSIS
    grep [-h | --help] [-n --line-number] PATTERN [FILE...]

DESCRIPTION
    grep searches the named input FILEs for lines containing a match to the given PATTERN. If no
    files are specified, grep searches the standard input. grep prints the matching lines.

OPTIONS
    -h
    --help
        Print this manual page.

    -v
    --invert-match
        Invert matching.

    -n
    --line-number
        Prefix each line of output with the line number of the match.
"#; /* @MANEND */

#[derive(Copy, Clone)]
struct Flags {
    line_numbers: bool,
    invert_match: bool,
}

impl Flags {
    fn new() -> Flags {
        Flags { line_numbers: false, invert_match: false }
    }
}

fn main() {
    let args = env::args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    let stdin = io::stdin();
    let stdin = stdin.lock();

    let mut flags = Flags::new();
    let mut pattern = String::new();
    let mut files = Vec::with_capacity(args.len());
    for arg in args {
        if arg.starts_with("-") {
            match arg.as_str() {
                "-h" | "--help" => {
                    stdout.writeln(MAN_PAGE.as_bytes()).try(&mut stderr);
                },
                "-n" | "--line-number" => flags.line_numbers = true,
                "-v" | "--invert-match" => flags.invert_match = true,
                _ => {
                    stderr.write(b"Unknown option: ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.write(b"\n").try(&mut stderr);
                    let _ = stderr.flush();
                    exit(1);
                }
            }
        } else if pattern.is_empty() {
            pattern = arg.clone();
        } else {
            match File::open(&Path::new(&arg)) {
                Ok(f) => files.push(f),
                Err(e) => {
                    stderr.write(b"Error opening ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.write(b": ").try(&mut stderr);
                    stderr.write(e.description().as_bytes()).try(&mut stderr);
                    stderr.write(b"\n").try(&mut stderr);
                    let _ = stderr.flush();
                    exit(1);
                }
            }
        }
    }

    if pattern.is_empty() {
        stderr.write_all(b"You must provide a pattern\n").try(&mut stderr);
        exit(1);
    }

    if files.is_empty() {
        do_simple_search(BufReader::new(stdin), &pattern, &mut stdout, &mut stderr, flags);
    } else {
        for f in files {
            do_simple_search(BufReader::new(f), &pattern, &mut stdout, &mut stderr, flags);
        }
    }
}

fn do_simple_search<T: BufRead, O: Write + WriteExt>(reader: T, pattern: &str, out: &mut O, stderr: &mut Stderr, flags: Flags) {
    let mut line_num = 0;
    for result in reader.lines() {
        line_num += 1;
        if let Ok(line) = result {
            let is_match = if flags.invert_match {
                !line.contains(pattern)
            } else {
                line.contains(pattern)
            };
            if is_match {
                if flags.line_numbers {
                    out.write_all((line_num.to_string() + ": ").as_bytes()).try(stderr);
                }
                out.writeln(line.as_bytes()).try(stderr);
            }
        }
    }
}
<commit_msg>Implement -c flag to grep<commit_after>#![deny(warnings)]
extern crate extra;

use std::io;
use std::io::{Write, BufRead, BufReader, Stderr};
use std::env;
use std::fs::File;
use std::path::Path;
use std::error::Error;
use std::process::exit;

use extra::option::OptionalExt;
use extra::io::WriteExt;

static MAN_PAGE: &'static str = /* @MANSTART{grep} */ r#"
NAME
    grep - print lines matching a pattern

SYNOPSIS
    grep [-h | --help] [-n --line-number] PATTERN [FILE...]

DESCRIPTION
    grep searches the named input FILEs for lines containing a match to the given PATTERN. If no
    files are specified, grep searches the standard input. grep prints the matching lines.

OPTIONS
    -h
    --help
        Print this manual page.

    -v
    --invert-match
        Invert matching.

    -c
    --count
        Print count of matching lines, instead of those lines.

    -n
    --line-number
        Prefix each line of output with the line number of the match.
"#; /* @MANEND */

#[derive(Copy, Clone)]
struct Flags {
    line_numbers: bool,
    invert_match: bool,
    count: bool,
}

impl Flags {
    fn new() -> Flags {
        Flags { line_numbers: false, invert_match: false, count: false }
    }
}

fn main() {
    let args = env::args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    let stdin = io::stdin();
    let stdin = stdin.lock();

    let mut flags = Flags::new();
    let mut pattern = String::new();
    let mut files = Vec::with_capacity(args.len());
    for arg in args {
        if arg.starts_with("-") {
            match arg.as_str() {
                "-h" | "--help" => {
                    stdout.writeln(MAN_PAGE.as_bytes()).try(&mut stderr);
                },
                "-n" | "--line-number" => flags.line_numbers = true,
                "-v" | "--invert-match" => flags.invert_match = true,
                "-c" | "--count" => flags.count = true,
                _ => {
                    stderr.write(b"Unknown option: ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.write(b"\n").try(&mut stderr);
                    let _ = stderr.flush();
                    exit(1);
                }
            }
        } else if pattern.is_empty() {
            pattern = arg.clone();
        } else {
            match File::open(&Path::new(&arg)) {
                Ok(f) => files.push(f),
                Err(e) => {
                    stderr.write(b"Error opening ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.write(b": ").try(&mut stderr);
                    stderr.write(e.description().as_bytes()).try(&mut stderr);
                    stderr.write(b"\n").try(&mut stderr);
                    let _ = stderr.flush();
                    exit(1);
                }
            }
        }
    }

    if pattern.is_empty() {
        stderr.write_all(b"You must provide a pattern\n").try(&mut stderr);
        exit(1);
    }

    if files.is_empty() {
        do_simple_search(BufReader::new(stdin), &pattern, &mut stdout, &mut stderr, flags);
    } else {
        for f in files {
            do_simple_search(BufReader::new(f), &pattern, &mut stdout, &mut stderr, flags);
        }
    }
}

fn do_simple_search<T: BufRead, O: Write + WriteExt>(reader: T, pattern: &str, out: &mut O, stderr: &mut Stderr, flags: Flags) {
    let mut line_num = 0;
    let mut count = 0;
    for result in reader.lines() {
        line_num += 1;
        if let Ok(line) = result {
            let is_match = if flags.invert_match {
                !line.contains(pattern)
            } else {
                line.contains(pattern)
            };
            if is_match && flags.count {
                count += 1;
            } else if is_match {
                if flags.line_numbers {
                    out.write_all((line_num.to_string() + ": ").as_bytes()).try(stderr);
                }
                out.writeln(line.as_bytes()).try(stderr);
            }
        }
    }

    if flags.count {
        println!("{}", count);
    }
}
<|endoftext|>


<commit_before>
extern crate mprovision;
extern crate docopt;
#[macro_use(version)]
extern crate version;
extern crate chrono;

use std::io::{self, Write};
use std::process;
use std::path::Path;
use docopt::Docopt;

const USAGE: &'static str = "
mprovision
A tool that helps iOS developers to manage mobileprovision files.

Usage:
  mprovision search <text> [<directory>]
  mprovision remove <uuid> [<directory>]
  mprovision show-xml <uuid> [<directory>]
  mprovision show-expired --days <days> [<directory>]
  mprovision remove-expired [<directory>]
  mprovision (-h | --help)
  mprovision --version

Options:
  -h --help     Show this help message.
  --version     Show version.
";

fn main() {
    let args = Docopt::new(USAGE)
        .and_then(|d| d.version(Some(format!("mprovision {}", version!()))).parse())
        .unwrap_or_else(|e| e.exit());

    match Command::from_args(&args).execute() {
        Ok(_) => process::exit(0),
        Err(e) => {
            writeln!(&mut io::stderr(), "{}", e).unwrap();
            process::exit(1);
        }
    }
}

struct Command<'a> {
    args: &'a ::docopt::ArgvMap,
}

impl<'a> Command<'a> {
    fn from_args(args: &'a ::docopt::ArgvMap) -> Self {
        Command { args: args }
    }

    fn execute(&self) -> Result<(), String> {
        if self.args.get_bool("search") {
            search(self.args)
        } else if self.args.get_bool("remove") {
            remove(self.args)
        } else if self.args.get_bool("show-xml") {
            show_xml(self.args)
        } else if self.args.get_bool("show-expired") {
            show_expired(self.args)
        } else if self.args.get_bool("remove-expired") {
            remove_expired(self.args)
        } else {
            Err("Command is not implemented".to_string())
        }
    }
}

fn search(args: &::docopt::ArgvMap) -> Result<(), String> {
    let text = args.get_str("<text>");
    if text.is_empty() {
        return Err("<text> should not be empty.".to_owned());
    }

    mprovision::with_dir(directory(args), |dir| mprovision::search(dir, text))
        .and_then(|info| {
            if info.profiles.is_empty() {
                println!("Nothing found for '{}'", text);
            } else {
                for profile in &info.profiles {
                    println!("\n{}", profile.description());
                }
                println!("\nFound {} of {}", info.profiles.len(), info.total);
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn remove(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }

    mprovision::with_dir(directory(args), |dir| mprovision::remove(dir, uuid))
        .and_then(|_| Ok(println!("'{}' was removed", uuid)))
        .map_err(|e| e.to_string())
}

fn show_xml(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }

    mprovision::with_dir(directory(args), |dir| mprovision::xml(dir, uuid))
        .and_then(|xml| Ok(println!("{}", xml)))
        .map_err(|e| e.to_string())
}

fn show_expired(args: &::docopt::ArgvMap) -> Result<(), String> {
    use chrono::*;

    let mut date = UTC::now();
    if let Some(days) = args.get_str("<days>").parse::<i64>().ok() {
        if days < 0 || days > 365 {
            return Err("<days> should be between 0 and 365".to_owned());
        }
        date = date + Duration::days(days);
    }

    mprovision::with_dir(directory(args),
                         |dir| mprovision::expired_profiles(dir, date))
        .and_then(|info| {
            if info.profiles.is_empty() {
                println!("All provisioning profiles are valid");
            } else {
                for profile in &info.profiles {
                    println!("\n{}", profile.description());
                }
                println!("\nFound {} of {}", info.profiles.len(), info.total);
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn remove_expired(args: &::docopt::ArgvMap) -> Result<(), String> {
    use chrono::*;

    mprovision::with_dir(directory(args),
                         |dir| mprovision::expired_profiles(dir, UTC::now()))
        .and_then(|info| {
            if info.profiles.is_empty() {
                println!("All provisioning profiles are valid");
            } else {
                let mut errors_counter = 0;
                let mut removals_counter = 0;
                for profile in info.profiles {
                    match std::fs::remove_file(&profile.path) {
                        Ok(_) => {
                            removals_counter += 1;
                            println!("'{}' was removed", profile.uuid)
                        }
                        Err(e) => {
                            errors_counter += 1;
                            println!("Error while trying to remove '{}'", profile.uuid);
                            println!("{}", e);
                        }
                    }
                }
                println!("Removed {} of {}", removals_counter, info.total);
                if errors_counter > 0 {
                    println!("Errors: {}", errors_counter);
                }
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn directory(args: &::docopt::ArgvMap) -> Option<&Path> {
    let dir_name = args.get_str("<directory>");
    if dir_name.is_empty() {
        None
    } else {
        Some(dir_name.as_ref())
    }
}
<commit_msg>Make short alias for the 'mprovision' module.<commit_after>
extern crate mprovision;
extern crate docopt;
#[macro_use(version)]
extern crate version;
extern crate chrono;

use std::io::{self, Write};
use std::process;
use std::path::Path;
use docopt::Docopt;
use mprovision as mp;

const USAGE: &'static str = "
mprovision
A tool that helps iOS developers to manage mobileprovision files.

Usage:
  mprovision search <text> [<directory>]
  mprovision remove <uuid> [<directory>]
  mprovision show-xml <uuid> [<directory>]
  mprovision show-expired --days <days> [<directory>]
  mprovision remove-expired [<directory>]
  mprovision (-h | --help)
  mprovision --version

Options:
  -h --help     Show this help message.
  --version     Show version.
";

fn main() {
    let args = Docopt::new(USAGE)
        .and_then(|d| d.version(Some(format!("mprovision {}", version!()))).parse())
        .unwrap_or_else(|e| e.exit());

    match Command::from_args(&args).execute() {
        Ok(_) => process::exit(0),
        Err(e) => {
            writeln!(&mut io::stderr(), "{}", e).unwrap();
            process::exit(1);
        }
    }
}

struct Command<'a> {
    args: &'a ::docopt::ArgvMap,
}

impl<'a> Command<'a> {
    fn from_args(args: &'a ::docopt::ArgvMap) -> Self {
        Command { args: args }
    }

    fn execute(&self) -> Result<(), String> {
        if self.args.get_bool("search") {
            search(self.args)
        } else if self.args.get_bool("remove") {
            remove(self.args)
        } else if self.args.get_bool("show-xml") {
            show_xml(self.args)
        } else if self.args.get_bool("show-expired") {
            show_expired(self.args)
        } else if self.args.get_bool("remove-expired") {
            remove_expired(self.args)
        } else {
            Err("Command is not implemented".to_string())
        }
    }
}

fn search(args: &::docopt::ArgvMap) -> Result<(), String> {
    let text = args.get_str("<text>");
    if text.is_empty() {
        return Err("<text> should not be empty.".to_owned());
    }

    mp::with_dir(directory(args), |dir| mp::search(dir, text))
        .and_then(|info| {
            if info.profiles.is_empty() {
                println!("Nothing found for '{}'", text);
            } else {
                for profile in &info.profiles {
                    println!("\n{}", profile.description());
                }
                println!("\nFound {} of {}", info.profiles.len(), info.total);
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn remove(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }

    mp::with_dir(directory(args), |dir| mp::remove(dir, uuid))
        .and_then(|_| Ok(println!("'{}' was removed", uuid)))
        .map_err(|e| e.to_string())
}

fn show_xml(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }

    mp::with_dir(directory(args), |dir| mp::xml(dir, uuid))
        .and_then(|xml| Ok(println!("{}", xml)))
        .map_err(|e| e.to_string())
}

fn show_expired(args: &::docopt::ArgvMap) -> Result<(), String> {
    use chrono::*;

    let mut date = UTC::now();
    if let Some(days) = args.get_str("<days>").parse::<i64>().ok() {
        if days < 0 || days > 365 {
            return Err("<days> should be between 0 and 365".to_owned());
        }
        date = date + Duration::days(days);
    }

    mp::with_dir(directory(args), |dir| mp::expired_profiles(dir, date))
        .and_then(|info| {
            if info.profiles.is_empty() {
                println!("All provisioning profiles are valid");
            } else {
                for profile in &info.profiles {
                    println!("\n{}", profile.description());
                }
                println!("\nFound {} of {}", info.profiles.len(), info.total);
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn remove_expired(args: &::docopt::ArgvMap) -> Result<(), String> {
    use chrono::*;

    mp::with_dir(directory(args), |dir| mp::expired_profiles(dir, UTC::now()))
        .and_then(|info| {
            if info.profiles.is_empty() {
                println!("All provisioning profiles are valid");
            } else {
                let mut errors_counter = 0;
                let mut removals_counter = 0;
                for profile in info.profiles {
                    match std::fs::remove_file(&profile.path) {
                        Ok(_) => {
                            removals_counter += 1;
                            println!("'{}' was removed", profile.uuid)
                        }
                        Err(e) => {
                            errors_counter += 1;
                            println!("Error while trying to remove '{}'", profile.uuid);
                            println!("{}", e);
                        }
                    }
                }
                println!("Removed {} of {}", removals_counter, info.total);
                if errors_counter > 0 {
                    println!("Errors: {}", errors_counter);
                }
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn directory(args: &::docopt::ArgvMap) -> Option<&Path> {
    let dir_name = args.get_str("<directory>");
    if dir_name.is_empty() {
        None
    } else {
        Some(dir_name.as_ref())
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate maplit;
extern crate correlation;

use correlation::{config, conditions, Correlator, Message};
use correlation::action::message;
use correlation::action::ActionHandlers;
use std::thread;

struct Printer;

impl message::ActionHandler for Printer {
    fn handle(&mut self, _: message::ExecResult) {
        println!("I'm the message handler");
    }
}

#[allow(dead_code)]
fn main() {
    let patterns = vec!["1".to_string(), "2".to_string(), "3".to_string()];
    let condition = conditions::Builder::new(100).patterns(patterns)
                                                .first_opens(true)
                                                .last_closes(true)
                                                .build();
    let actions = vec![ message::Action::new().into() ];
    let contexts = vec!{
        config::ContextBuilder::new(condition.clone()).actions(actions.clone()).build(),
        config::ContextBuilder::new(condition.clone()).actions(actions.clone()).build(),
        config::ContextBuilder::new(condition.clone()).actions(actions.clone()).build(),
    };
    let handlers = ActionHandlers::new(Box::new(Printer));
    let mut correlator = Correlator::new(contexts, handlers);
    let _ = correlator.push_message(Message::new("1".to_string()));
    thread::sleep_ms(20);
    let _ = correlator.push_message(Message::new("2".to_string()));
    thread::sleep_ms(80);
    let _ = correlator.push_message(Message::new("3".to_string()));
    thread::sleep_ms(50);
    let _ = correlator.stop();
}
<commit_msg>test: remove the sleeping<commit_after>#[macro_use]
extern crate maplit;
extern crate correlation;

use correlation::{config, conditions, Correlator, Message};
use correlation::action::message;
use correlation::action::ActionHandlers;
use std::thread;

struct Printer;

impl message::ActionHandler for Printer {
    fn handle(&mut self, _: message::ExecResult) {
        println!("I'm the message handler");
    }
}

#[allow(dead_code)]
fn main() {
    let patterns = vec!["1".to_string(), "2".to_string(), "3".to_string()];
    let condition = conditions::Builder::new(100).patterns(patterns)
                                                .first_opens(true)
                                                .last_closes(true)
                                                .build();
    let actions = vec![ message::Action::new().into() ];
    let contexts = vec!{
        config::ContextBuilder::new(condition.clone()).actions(actions.clone()).build(),
        config::ContextBuilder::new(condition.clone()).actions(actions.clone()).build(),
        config::ContextBuilder::new(condition.clone()).actions(actions.clone()).build(),
    };
    let handlers = ActionHandlers::new(Box::new(Printer));
    let mut correlator = Correlator::new(contexts, handlers);
    let _ = correlator.push_message(Message::new("1".to_string()));
    thread::sleep_ms(20);
    let _ = correlator.push_message(Message::new("2".to_string()));
    thread::sleep_ms(80);
    let _ = correlator.push_message(Message::new("3".to_string()));
    let r = correlator.stop();
    println!("{:?}", r);
}
<|endoftext|>


<commit_before>use libc::c_long;

// a raw Core Foundation reference. It may or may not have been
// CFRetain'ed, depending on whether it was obtained via ownership or
// borrow semantics.
trait AbstractCFTypeRef {
    pure fn as_type_ref(&self) -> CFTypeRef;
    static pure fn type_id() -> CFTypeID;
}

pub type Boolean = u8;

pub type CFIndex = c_long;
pub type CFOptionFlags = u32;
pub struct CFRange {
    location: CFIndex,
    length: CFIndex
}

fn CFRangeMake(off: CFIndex, len: CFIndex) -> CFRange {
    CFRange { location: off, length: len }
}

struct __CFAllocator { private: () }
pub type CFAllocatorRef = *__CFAllocator;

struct __CFNull { private: () }
pub type CFNullRef = *__CFNull;

pub type CFHashCode = libc::c_ulong;
pub type CFTypeID = libc::c_ulong;

struct __CFType { private: () }
pub type CFTypeRef = *__CFType;

pub impl CFTypeRef : AbstractCFTypeRef {
    pure fn as_type_ref(&self) -> CFTypeRef { *self }
    // this can't be used, because CFType is the supertype and has no type id.
    static pure fn type_id() -> CFTypeID { fail; }
}

pub pure fn downcast<T:AbstractCFTypeRef>(r: CFTypeRef) -> T unsafe {
    assert CFGetTypeID(r) == type_id::<T>();
    cast::transmute(r)
}

struct CFWrapper<T:Copy AbstractCFTypeRef, PlaceholderType1, PlaceholderType2> {
    obj: T,

    drop {
        unsafe {
            assert CFGetRetainCount(cast::transmute(self.obj)) > 0 as CFIndex;
            // sadly, cannot use obj.as_type_ref() here, because drop
            // cannot make virtual method calls using trait
            // types. Instead, just transmute the bugger.
            CFRelease(cast::transmute(self.obj))
        }
    }
}

pub type CFType = CFWrapper<CFTypeRef, (), ()>;

pub impl<T:Copy AbstractCFTypeRef, E1, E2>
    CFWrapper<T, E1, E2> {
    pure fn borrow_ref(&self) -> &self/T {
        &self.obj
    }

    pure fn borrow_type_ref() -> &self/CFTypeRef unsafe {
        cast::transmute(&self.obj)
    }

    // Use this when following Core Foundation's "Create" rule; i.e., the wrapper assumes ownership.
    // The object has already been retained, so we need not increment the retain count ourself.
    static pure fn wrap_owned(some_ref: T) -> CFWrapper<T,E1,E2> {
        // N.B. we can't make any assertions about retain count here,
        // because returned things are only guaranteed to be already
        // retained. Strings, for example, could be interned.
        CFWrapper { obj: some_ref }
    }

    // Use this when following Core Foundation's "Get" rule. The wrapper does not have ownership.
    // Twe need to increment object's the retain count so it isn't freed out from under our noses.
    static pure fn wrap_shared(some_ref: T) -> CFWrapper<T,E1,E2> {
        unsafe { CFRetain(some_ref.as_type_ref()); }
        CFWrapper { obj: some_ref }
    }

    // Unwraps the wrapper, returning the underlying AbstractCFType.
    static fn unwrap(wrapper: CFWrapper<T,E1,E2>) -> T {
        wrapper.obj
    }

    static fn to_CFType(wrapper: CFWrapper<T,E1,E2>) -> CFType unsafe {
        cast::transmute(move wrapper)
    }

    static fn from_CFType(wrapper: CFType) -> CFWrapper<T,E1,E2> unsafe {
        assert wrapper.type_id() == type_id::<T>();
        cast::transmute(move wrapper)
    }

    static fn clone(wrapper: &CFWrapper<T,E1,E2>) -> CFWrapper<T,E1,E2> {
        CFWrapper::wrap_shared(*wrapper.borrow_ref())
    }

    pure fn retain_count() -> CFIndex unsafe {
        CFGetRetainCount(*self.borrow_type_ref())
    }

    pure fn type_id() -> CFTypeID unsafe {
        CFGetTypeID(*self.borrow_type_ref())
    }

    pure fn show() unsafe {
        CFShow(*self.borrow_type_ref());
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFBase.h
     */

    /* CFAllocator Reference */
    // N.B. Many CFAllocator functions and constants are omitted here.
    const kCFAllocatorDefault: CFAllocatorRef;
    const kCFAllocatorSystemDefault: CFAllocatorRef;
    const kCFAllocatorMalloc: CFAllocatorRef;
    const kCFAllocatorMallocZone: CFAllocatorRef;
    const kCFAllocatorNull: CFAllocatorRef;
    const kCFAllocatorUseContext: CFAllocatorRef;

    /* CFNull Reference */

    const kCFNull: CFNullRef;

    /* CFType Reference */

    //fn CFCopyDescription
    //fn CFCopyTypeIDDescription
    //fn CFEqual
    //fn CFGetAllocator
    fn CFGetRetainCount(cf: CFTypeRef) -> CFIndex;
    fn CFGetTypeID(cf: CFTypeRef) -> CFTypeID;
    fn CFHash(cf: CFTypeRef) -> CFHashCode;
    //fn CFMakeCollectable
    fn CFRelease(cf: CFTypeRef);
    fn CFRetain(cf: CFTypeRef) -> CFTypeRef;
    fn CFShow(obj: CFTypeRef);

    /* Base Utilities Reference */
    // N.B. Some things missing here.
}
<commit_msg>Update for language churn<commit_after>use libc::c_long;

// a raw Core Foundation reference. It may or may not have been
// CFRetain'ed, depending on whether it was obtained via ownership or
// borrow semantics.
pub trait AbstractCFTypeRef {
    pure fn as_type_ref(&self) -> CFTypeRef;
    static pure fn type_id() -> CFTypeID;
}

pub type Boolean = u8;

pub type CFIndex = c_long;
pub type CFOptionFlags = u32;
pub struct CFRange {
    location: CFIndex,
    length: CFIndex
}

pub fn CFRangeMake(off: CFIndex, len: CFIndex) -> CFRange {
    CFRange { location: off, length: len }
}

struct __CFAllocator { private: () }
pub type CFAllocatorRef = *__CFAllocator;

struct __CFNull { private: () }
pub type CFNullRef = *__CFNull;

pub type CFHashCode = libc::c_ulong;
pub type CFTypeID = libc::c_ulong;

struct __CFType { private: () }
pub type CFTypeRef = *__CFType;

pub impl CFTypeRef : AbstractCFTypeRef {
    pure fn as_type_ref(&self) -> CFTypeRef { *self }
    // this can't be used, because CFType is the supertype and has no type id.
    static pure fn type_id() -> CFTypeID { fail; }
}

pub pure fn downcast<T:AbstractCFTypeRef>(r: CFTypeRef) -> T unsafe {
    assert CFGetTypeID(r) == type_id::<T>();
    cast::transmute(r)
}

pub struct CFWrapper<T:Copy AbstractCFTypeRef, PlaceholderType1, PlaceholderType2> {
    obj: T,

    drop {
        unsafe {
            assert CFGetRetainCount(cast::transmute(self.obj)) > 0 as CFIndex;
            // sadly, cannot use obj.as_type_ref() here, because drop
            // cannot make virtual method calls using trait
            // types. Instead, just transmute the bugger.
            CFRelease(cast::transmute(self.obj))
        }
    }
}

pub type CFType = CFWrapper<CFTypeRef, (), ()>;

pub impl<T:Copy AbstractCFTypeRef, E1, E2>
    CFWrapper<T, E1, E2> {
    pure fn borrow_ref(&self) -> &self/T {
        &self.obj
    }

    pure fn borrow_type_ref() -> &self/CFTypeRef unsafe {
        cast::transmute(&self.obj)
    }

    // Use this when following Core Foundation's "Create" rule; i.e., the wrapper assumes ownership.
    // The object has already been retained, so we need not increment the retain count ourself.
    static pure fn wrap_owned(some_ref: T) -> CFWrapper<T,E1,E2> {
        // N.B. we can't make any assertions about retain count here,
        // because returned things are only guaranteed to be already
        // retained. Strings, for example, could be interned.
        CFWrapper { obj: some_ref }
    }

    // Use this when following Core Foundation's "Get" rule. The wrapper does not have ownership.
    // Twe need to increment object's the retain count so it isn't freed out from under our noses.
    static pure fn wrap_shared(some_ref: T) -> CFWrapper<T,E1,E2> {
        unsafe { CFRetain(some_ref.as_type_ref()); }
        CFWrapper { obj: some_ref }
    }

    // Unwraps the wrapper, returning the underlying AbstractCFType.
    static fn unwrap(wrapper: CFWrapper<T,E1,E2>) -> T {
        wrapper.obj
    }

    static fn to_CFType(wrapper: CFWrapper<T,E1,E2>) -> CFType unsafe {
        cast::transmute(move wrapper)
    }

    static fn from_CFType(wrapper: CFType) -> CFWrapper<T,E1,E2> unsafe {
        assert wrapper.type_id() == type_id::<T>();
        cast::transmute(move wrapper)
    }

    static fn clone(wrapper: &CFWrapper<T,E1,E2>) -> CFWrapper<T,E1,E2> {
        CFWrapper::wrap_shared(*wrapper.borrow_ref())
    }

    pure fn retain_count() -> CFIndex unsafe {
        CFGetRetainCount(*self.borrow_type_ref())
    }

    pure fn type_id() -> CFTypeID unsafe {
        CFGetTypeID(*self.borrow_type_ref())
    }

    pure fn show() unsafe {
        CFShow(*self.borrow_type_ref());
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFBase.h
     */

    /* CFAllocator Reference */
    // N.B. Many CFAllocator functions and constants are omitted here.
    pub const kCFAllocatorDefault: CFAllocatorRef;
    pub const kCFAllocatorSystemDefault: CFAllocatorRef;
    pub const kCFAllocatorMalloc: CFAllocatorRef;
    pub const kCFAllocatorMallocZone: CFAllocatorRef;
    pub const kCFAllocatorNull: CFAllocatorRef;
    pub const kCFAllocatorUseContext: CFAllocatorRef;

    /* CFNull Reference */

    pub const kCFNull: CFNullRef;

    /* CFType Reference */

    //fn CFCopyDescription
    //fn CFCopyTypeIDDescription
    //fn CFEqual
    //fn CFGetAllocator
    pub fn CFGetRetainCount(cf: CFTypeRef) -> CFIndex;
    pub fn CFGetTypeID(cf: CFTypeRef) -> CFTypeID;
    pub fn CFHash(cf: CFTypeRef) -> CFHashCode;
    //fn CFMakeCollectable
    pub fn CFRelease(cf: CFTypeRef);
    pub fn CFRetain(cf: CFTypeRef) -> CFTypeRef;
    pub fn CFShow(obj: CFTypeRef);

    /* Base Utilities Reference */
    // N.B. Some things missing here.
}
<|endoftext|>


<commit_before>use std::{fmt, cmp};

mod tests;

#[derive(Debug, PartialEq)]
pub struct Card {
    pub value: Value,
    pub suit: Suit
}

impl fmt::Display for Card {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.value, self.suit)
    }
}

impl cmp::Ord for Card {
    fn cmp(&self, other: &Card) -> cmp::Ordering {
        self.value.cmp(&other.value)
    }
}

impl cmp::Eq for Card { }

impl cmp::PartialOrd for Card {
    fn partial_cmp(&self, other: &Card) -> Option<cmp::Ordering> {
        self.value.partial_cmp(&other.value)
    }
}

impl Card {
    pub fn new(value: Value, suit: Suit) -> Card {
        Card {
            value: value,
            suit: suit
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum Value {
    None    =   0,
    Two     =   2,
    Three   =   3,
    Four    =   4,
    Five    =   5,
    Six     =   6,
    Seven   =   7,
    Eight   =   8,
    Nine    =   9,
    Ten     =   10,
    Jack    =   11,
    Queen   =   12,
    King    =   13,
    Ace     =   14
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match *self {
            Value::Two      =>  "2",
            Value::Three    =>  "3",
            Value::Four     =>  "4",
            Value::Five     =>  "5",
            Value::Six      =>  "6",
            Value::Seven    =>  "7",
            Value::Eight    =>  "8",
            Value::Nine     =>  "9",
            Value::Ten      =>  "T",
            Value::Jack     =>  "J",
            Value::Queen    =>  "Q",
            Value::King     =>  "K",
            Value::Ace      =>  "A",
            Value::None     =>  "-"
        };
        write!(f, "{}", value)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Suit {
    None,
    Diamonds,
    Clubs,
    Hearts,
    Spades
}

impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let suit = match *self {
            Suit::Diamonds  =>  "♢",
            Suit::Clubs     =>  "♣",
            Suit::Hearts    =>  "♡",
            Suit::Spades    =>  "♠",
            Suit::None      =>  "-"
        };
        write!(f, "{}", suit)
    }
}
<commit_msg>Added an AceLow card to simplify evaluating and comparing straights<commit_after>use std::{fmt, cmp};

mod tests;

#[derive(Debug, PartialEq)]
pub struct Card {
    pub value: Value,
    pub suit: Suit
}

impl fmt::Display for Card {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.value, self.suit)
    }
}

impl cmp::Ord for Card {
    fn cmp(&self, other: &Card) -> cmp::Ordering {
        self.value.cmp(&other.value)
    }
}

impl cmp::Eq for Card { }

impl cmp::PartialOrd for Card {
    fn partial_cmp(&self, other: &Card) -> Option<cmp::Ordering> {
        self.value.partial_cmp(&other.value)
    }
}

impl Card {
    pub fn new(value: Value, suit: Suit) -> Card {
        Card {
            value: value,
            suit: suit
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum Value {
    None    =   0,
    AceLow  =   1,
    Two     =   2,
    Three   =   3,
    Four    =   4,
    Five    =   5,
    Six     =   6,
    Seven   =   7,
    Eight   =   8,
    Nine    =   9,
    Ten     =   10,
    Jack    =   11,
    Queen   =   12,
    King    =   13,
    Ace     =   14
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match *self {
            Value::AceLow   =>  "1",
            Value::Two      =>  "2",
            Value::Three    =>  "3",
            Value::Four     =>  "4",
            Value::Five     =>  "5",
            Value::Six      =>  "6",
            Value::Seven    =>  "7",
            Value::Eight    =>  "8",
            Value::Nine     =>  "9",
            Value::Ten      =>  "T",
            Value::Jack     =>  "J",
            Value::Queen    =>  "Q",
            Value::King     =>  "K",
            Value::Ace      =>  "A",
            Value::None     =>  "-"
        };
        write!(f, "{}", value)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Suit {
    None,
    Diamonds,
    Clubs,
    Hearts,
    Spades
}

impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let suit = match *self {
            Suit::Diamonds  =>  "♢",
            Suit::Clubs     =>  "♣",
            Suit::Hearts    =>  "♡",
            Suit::Spades    =>  "♠",
            Suit::None      =>  "-"
        };
        write!(f, "{}", suit)
    }
}
<|endoftext|>


<commit_before>// Implements the `zakopane checksum` subcommand.

use crate::structs::ZakopaneError;

const MAX_TASKS: usize = 8;

struct ChecksumWithPath {
    checksum: String,
    path: String,
}

impl ChecksumWithPath {
    pub fn new(c: String, p: String) -> Self {
        Self {
            checksum: c,
            path: p,
        }
    }
}

type ChecksumResult = Result<ChecksumWithPath, ZakopaneError>;
type ChecksumTaskJoinHandle = tokio::task::JoinHandle<Vec<ChecksumWithPath>>;

struct ChecksumTaskDispatcherData {
    // Rate-limiter for spawned checksum tasks.
    semaphore: std::sync::Arc<tokio::sync::Semaphore>,

    // Counts total number of spawned checksum tasks.
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
}

impl ChecksumTaskDispatcherData {
    pub fn new(
        spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
        sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    ) -> Self {
        Self {
            semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(MAX_TASKS)),
            spawn_counter,
            sender,
        }
    }
}

// Sends a checksum task result downstream to the collector task.
fn checksum_task_send_result(
    result: ChecksumResult,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
) {
    if let Err(e) = sender.blocking_send(result) {
        eprintln!("BUG: Sender::send() failed: {}", e);
    }
}

// Separated from `checksum_task()` to ensure the `add_permits()` call is
// always hit.
fn checksum_task_impl(path: std::path::PathBuf, sender: tokio::sync::mpsc::Sender<ChecksumResult>) {
    let contents = match crate::helpers::ingest_file(path.to_str().unwrap()) {
        Ok(contents) => contents,
        Err(e) => return checksum_task_send_result(Err(e), sender),
    };
    let checksum = crypto_hash::hex_digest(crypto_hash::Algorithm::SHA256, contents.as_ref());
    checksum_task_send_result(
        Ok(ChecksumWithPath::new(
            checksum,
            String::from(path.to_str().unwrap()),
        )),
        sender,
    );
}

fn checksum_task(
    path: std::path::PathBuf,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    semaphore_clone: std::sync::Arc<tokio::sync::Semaphore>,
) {
    checksum_task_impl(path, sender);

    // See comment in `ChecksumTaskManager::spawn_task()`.
    semaphore_clone.add_permits(1);
}

async fn collector_task(
    mut receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
) -> Vec<ChecksumWithPath> {
    let mut sums: Vec<ChecksumWithPath> = Vec::new();
    let mut errors: usize = 0;
    loop {
        while let Some(result) = receiver.recv().await {
            match result {
                Ok(digest_with_path) => sums.push(digest_with_path),
                Err(_) => errors += 1,
            }
        }
        if sums.len() + errors == spawn_counter.load(std::sync::atomic::Ordering::SeqCst) {
            return sums;
        }
    }
}

// Spawns the collector task that listens for checksum results
// provided by the checksum tasks.
async fn spawn_collector() -> (ChecksumTaskDispatcherData, ChecksumTaskJoinHandle) {
    let (sender, receiver) = tokio::sync::mpsc::channel(MAX_TASKS);
    let spawn_counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let spawn_counter_clone = spawn_counter.clone();
    let join_handle =
        tokio::task::spawn(async move { collector_task(receiver, spawn_counter_clone).await });
    (
        ChecksumTaskDispatcherData::new(spawn_counter, sender),
        join_handle,
    )
}

async fn spawn_checksum_tasks(context: ChecksumTaskDispatcherData) {
    // TODO(j39m): Fix the hardcoded path.
    let walk_iter = walkdir::WalkDir::new("/home/kalvin/Documents/unsorted/").into_iter();
    for entry in walk_iter.filter_entry(|e| {
        !e.file_name()
            .to_str()
            .map(|path| path.starts_with(".")) // XXX(j39m)
            .unwrap_or(false)
    }) {
        if let Ok(direntry) = entry {
            let path = direntry.into_path();
            if !path.is_file() {
                continue;
            }

            context
                .spawn_counter
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let permit = context.semaphore.acquire().await.unwrap();

            // Icky workaround for not being able to pass `SemaphorePermit`
            // directly into a spawned task. Note that new permits are added
            // later in the checksum task.
            //
            // See also: https://github.com/tokio-rs/tokio/issues/1998
            permit.forget();

            let sender = context.sender.clone();
            let semaphore_clone = context.semaphore.clone();
            tokio::task::spawn_blocking(move || checksum_task(path, sender, semaphore_clone))
                .await
                .unwrap();
        }
    }
}

fn pretty_format_checksums(checksums: Vec<ChecksumWithPath>) -> String {
    let mut buffer: Vec<String> = Vec::new();
    for digest_line in checksums {
        buffer.push(format!("{}  ./{}", digest_line.checksum, digest_line.path));
    }
    buffer.join("\n")
}

async fn checksum_impl() -> String {
    let (dispatcher_data, join_handle) = spawn_collector().await;
    spawn_checksum_tasks(dispatcher_data).await;
    let mut checksums: Vec<ChecksumWithPath> = join_handle.await.unwrap();
    checksums.sort_by(|a, b| a.path.cmp(&b.path));
    pretty_format_checksums(checksums)
}

pub fn checksum() -> String {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl())
}
<commit_msg>checksum: Use PathBuf in ChecksumWithPath<commit_after>// Implements the `zakopane checksum` subcommand.

use crate::structs::ZakopaneError;

const MAX_TASKS: usize = 8;

struct ChecksumWithPath {
    checksum: String,
    path: std::path::PathBuf,
}

impl ChecksumWithPath {
    pub fn new(checksum: String, path: std::path::PathBuf) -> Self {
        Self { checksum, path }
    }
}

type ChecksumResult = Result<ChecksumWithPath, ZakopaneError>;
type ChecksumTaskJoinHandle = tokio::task::JoinHandle<Vec<ChecksumWithPath>>;

struct ChecksumTaskDispatcherData {
    // Rate-limiter for spawned checksum tasks.
    semaphore: std::sync::Arc<tokio::sync::Semaphore>,

    // Counts total number of spawned checksum tasks.
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
}

impl ChecksumTaskDispatcherData {
    pub fn new(
        spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
        sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    ) -> Self {
        Self {
            semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(MAX_TASKS)),
            spawn_counter,
            sender,
        }
    }
}

// Sends a checksum task result downstream to the collector task.
fn checksum_task_send_result(
    result: ChecksumResult,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
) {
    if let Err(e) = sender.blocking_send(result) {
        eprintln!("BUG: Sender::send() failed: {}", e);
    }
}

// Separated from `checksum_task()` to ensure the `add_permits()` call is
// always hit.
fn checksum_task_impl(path: std::path::PathBuf, sender: tokio::sync::mpsc::Sender<ChecksumResult>) {
    let contents = match crate::helpers::ingest_file(path.to_str().unwrap()) {
        Ok(contents) => contents,
        Err(e) => return checksum_task_send_result(Err(e), sender),
    };
    let checksum = crypto_hash::hex_digest(crypto_hash::Algorithm::SHA256, contents.as_ref());
    checksum_task_send_result(Ok(ChecksumWithPath::new(checksum, path)), sender);
}

fn checksum_task(
    path: std::path::PathBuf,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    semaphore_clone: std::sync::Arc<tokio::sync::Semaphore>,
) {
    checksum_task_impl(path, sender);

    // See comment in `ChecksumTaskManager::spawn_task()`.
    semaphore_clone.add_permits(1);
}

async fn collector_task(
    mut receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
) -> Vec<ChecksumWithPath> {
    let mut sums: Vec<ChecksumWithPath> = Vec::new();
    let mut errors: usize = 0;
    loop {
        while let Some(result) = receiver.recv().await {
            match result {
                Ok(digest_with_path) => sums.push(digest_with_path),
                Err(_) => errors += 1,
            }
        }
        if sums.len() + errors == spawn_counter.load(std::sync::atomic::Ordering::SeqCst) {
            return sums;
        }
    }
}

// Spawns the collector task that listens for checksum results
// provided by the checksum tasks.
async fn spawn_collector() -> (ChecksumTaskDispatcherData, ChecksumTaskJoinHandle) {
    let (sender, receiver) = tokio::sync::mpsc::channel(MAX_TASKS);
    let spawn_counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let spawn_counter_clone = spawn_counter.clone();
    let join_handle =
        tokio::task::spawn(async move { collector_task(receiver, spawn_counter_clone).await });
    (
        ChecksumTaskDispatcherData::new(spawn_counter, sender),
        join_handle,
    )
}

async fn spawn_checksum_tasks(context: ChecksumTaskDispatcherData) {
    // TODO(j39m): Fix the hardcoded path.
    let walk_iter = walkdir::WalkDir::new("/home/kalvin/Documents/unsorted/").into_iter();
    for entry in walk_iter.filter_entry(|e| {
        !e.file_name()
            .to_str()
            .map(|path| path.starts_with(".")) // XXX(j39m)
            .unwrap_or(false)
    }) {
        if let Ok(direntry) = entry {
            let path = direntry.into_path();
            if !path.is_file() {
                continue;
            }

            context
                .spawn_counter
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let permit = context.semaphore.acquire().await.unwrap();

            // Icky workaround for not being able to pass `SemaphorePermit`
            // directly into a spawned task. Note that new permits are added
            // later in the checksum task.
            //
            // See also: https://github.com/tokio-rs/tokio/issues/1998
            permit.forget();

            let sender = context.sender.clone();
            let semaphore_clone = context.semaphore.clone();
            tokio::task::spawn_blocking(move || checksum_task(path, sender, semaphore_clone))
                .await
                .unwrap();
        }
    }
}

fn pretty_format_checksums(checksums: Vec<ChecksumWithPath>) -> String {
    let mut buffer: Vec<String> = Vec::new();
    for digest_line in checksums {
        buffer.push(format!(
            "{}  ./{}",
            digest_line.checksum,
            digest_line.path.to_str().unwrap()
        ));
    }
    buffer.join("\n")
}

async fn checksum_impl() -> String {
    let (dispatcher_data, join_handle) = spawn_collector().await;
    spawn_checksum_tasks(dispatcher_data).await;
    let mut checksums: Vec<ChecksumWithPath> = join_handle.await.unwrap();
    checksums.sort_by(|a, b| a.path.cmp(&b.path));
    pretty_format_checksums(checksums)
}

pub fn checksum() -> String {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl())
}
<|endoftext|>


<commit_before>use std::slice;

use binding::vm::init;
use class::any_object::AnyObject;
use types::{Argc, Value};

pub struct VM;

impl VM {
    pub fn init() {
        init();
    }

    pub fn parse_arguments(argc: Argc, arguments: *const AnyObject) -> Vec<AnyObject> {
        unsafe {
            slice::from_raw_parts(arguments, argc as usize).to_vec()
        }
    }

    pub fn parse_itself(itself: Value) -> AnyObject {
        AnyObject::from(itself)
    }
}
<commit_msg>Remove VM::parse_itself()<commit_after>use std::slice;

use binding::vm::init;
use class::any_object::AnyObject;
use types::{Argc, Value};

pub struct VM;

impl VM {
    pub fn init() {
        init();
    }

    pub fn parse_arguments(argc: Argc, arguments: *const AnyObject) -> Vec<AnyObject> {
        unsafe {
            slice::from_raw_parts(arguments, argc as usize).to_vec()
        }
    }
}
<|endoftext|>


<commit_before>//! The main rustup commandline application
//!
//! The rustup binary is a chimera, changing its behavior based on the
//! name of the binary. This is used most prominently to enable
//! rustup's tool 'proxies' - that is, rustup itself and the rustup
//! proxies are the same binary; when the binary is called 'rustup' or
//! 'rustup.exe' rustup behaves like the rustup commandline
//! application; when it is called 'rustc' it behaves as a proxy to
//! 'rustc'.
//!
//! This scheme is further used to distingush the rustup installer,
//! called 'rustup-init' which is again just the rustup binary under a
//! different name.

#![recursion_limit = "1024"]

#[macro_use]
mod log;
mod common;
mod download_tracker;
#[allow(deprecated)] // WORKAROUND https://github.com/rust-lang-nursery/error-chain/issues/254
mod errors;
mod help;
mod job;
mod proxy_mode;
mod rustup_mode;
mod self_update;
mod setup_mode;
mod term2;

use crate::errors::*;
use rustup::env_var::RUST_RECURSION_COUNT_MAX;
use std::alloc::System;
use std::env;
use std::path::PathBuf;

// Always use the system allocator, to reduce binary size.
#[global_allocator]
static _ALLOCATOR: System = System;

fn main() {
    if let Err(ref e) = run_rustup() {
        common::report_error(e);
        std::process::exit(1);
    }
}

fn run_rustup() -> Result<()> {
    // Guard against infinite proxy recursion. This mostly happens due to
    // bugs in rustup.
    do_recursion_guard()?;

    // The name of arg0 determines how the program is going to behave
    let arg0 = env::args().next().map(PathBuf::from);
    let name = arg0
        .as_ref()
        .and_then(|a| a.file_stem())
        .and_then(|a| a.to_str());

    match name {
        Some("rustup") => rustup_mode::main(),
        Some(n) if n.starts_with("rustup-setup") || n.starts_with("rustup-init") => {
            // NB: The above check is only for the prefix of the file
            // name. Browsers rename duplicates to
            // e.g. rustup-setup(2), and this allows all variations
            // to work.
            setup_mode::main()
        }
        Some(n) if n.starts_with("rustup-gc-") => {
            // This is the final uninstallation stage on windows where
            // rustup deletes its own exe
            self_update::complete_windows_uninstall()
        }
        Some(_) => proxy_mode::main(),
        None => {
            // Weird case. No arg0, or it's unparsable.
            Err(ErrorKind::NoExeName.into())
        }
    }
}

fn do_recursion_guard() -> Result<()> {
    let recursion_count = env::var("RUST_RECURSION_COUNT")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(0);
    if recursion_count > RUST_RECURSION_COUNT_MAX {
        return Err(ErrorKind::InfiniteRecursion.into());
    }

    Ok(())
}
<commit_msg>Remove global allocator<commit_after>//! The main rustup commandline application
//!
//! The rustup binary is a chimera, changing its behavior based on the
//! name of the binary. This is used most prominently to enable
//! rustup's tool 'proxies' - that is, rustup itself and the rustup
//! proxies are the same binary; when the binary is called 'rustup' or
//! 'rustup.exe' rustup behaves like the rustup commandline
//! application; when it is called 'rustc' it behaves as a proxy to
//! 'rustc'.
//!
//! This scheme is further used to distingush the rustup installer,
//! called 'rustup-init' which is again just the rustup binary under a
//! different name.

#![recursion_limit = "1024"]

#[macro_use]
mod log;
mod common;
mod download_tracker;
#[allow(deprecated)] // WORKAROUND https://github.com/rust-lang-nursery/error-chain/issues/254
mod errors;
mod help;
mod job;
mod proxy_mode;
mod rustup_mode;
mod self_update;
mod setup_mode;
mod term2;

use crate::errors::*;
use rustup::env_var::RUST_RECURSION_COUNT_MAX;
use std::env;
use std::path::PathBuf;

fn main() {
    if let Err(ref e) = run_rustup() {
        common::report_error(e);
        std::process::exit(1);
    }
}

fn run_rustup() -> Result<()> {
    // Guard against infinite proxy recursion. This mostly happens due to
    // bugs in rustup.
    do_recursion_guard()?;

    // The name of arg0 determines how the program is going to behave
    let arg0 = env::args().next().map(PathBuf::from);
    let name = arg0
        .as_ref()
        .and_then(|a| a.file_stem())
        .and_then(|a| a.to_str());

    match name {
        Some("rustup") => rustup_mode::main(),
        Some(n) if n.starts_with("rustup-setup") || n.starts_with("rustup-init") => {
            // NB: The above check is only for the prefix of the file
            // name. Browsers rename duplicates to
            // e.g. rustup-setup(2), and this allows all variations
            // to work.
            setup_mode::main()
        }
        Some(n) if n.starts_with("rustup-gc-") => {
            // This is the final uninstallation stage on windows where
            // rustup deletes its own exe
            self_update::complete_windows_uninstall()
        }
        Some(_) => proxy_mode::main(),
        None => {
            // Weird case. No arg0, or it's unparsable.
            Err(ErrorKind::NoExeName.into())
        }
    }
}

fn do_recursion_guard() -> Result<()> {
    let recursion_count = env::var("RUST_RECURSION_COUNT")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(0);
    if recursion_count > RUST_RECURSION_COUNT_MAX {
        return Err(ErrorKind::InfiniteRecursion.into());
    }

    Ok(())
}
<|endoftext|>