<commit_before>// Defined note types for GNU systems.

// ABI information.  The descriptor consists of words:
//    word 0: OS descriptor
//    word 1: major version of the ABI
//    word 2: minor version of the ABI
//    word 3: subminor version of the ABI

pub const NT_GNU_ABI_TAG: u32 = 1;
// Old name
pub const ELF_NOTE_ABI: u32 = NT_GNU_ABI_TAG;
// Known OSes.  These values can appear in word 0 of an
//    NT_GNU_ABI_TAG note section entry.
pub const ELF_NOTE_OS_LINUX: u32 = 0;
pub const ELF_NOTE_OS_GNU: u32 = 1;
pub const ELF_NOTE_OS_SOLARIS2: u32 = 2;
pub const ELF_NOTE_OS_FREEBSD: u32 = 3;

// Synthetic hwcap information.  The descriptor begins with two words:
//    word 0: number of entries
//    word 1: bitmask of enabled entries
//    Then follow variable-length entries, one byte followed by a
//    '\0'-terminated hwcap name string.  The byte gives the bit
//    number to test if enabled, (1U << bit) & bitmask.
pub const NT_GNU_HWCAP: u32 = 2;

// Build ID bits as generated by ld --build-id.
//    The descriptor consists of any nonzero number of bytes.
pub const NT_GNU_BUILD_ID: u32 = 3;

// Version note generated by GNU gold containing a version string.
pub const NT_GNU_GOLD_VERSION: u32 = 4;

#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))]
#[repr(C)]
/// Note section contents. Each entry in the note section begins with a header of a fixed form.
pub struct Nhdr32 {
    /// Length of the note's name (includes the terminator)
    pub n_namesz: u32,
    /// Length of the note's descriptor
    pub n_descsz: u32,
    /// Type of the note
    pub n_type: u32,
}

#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))]
#[repr(C)]
/// Note section contents. Each entry in the note section begins with a header of a fixed form.
pub struct Nhdr64 {
    /// Length of the note's name (includes the terminator)
    pub n_namesz: u64,
    /// Length of the note's descriptor.
    pub n_descsz: u64,
    /// Type of the note.
    pub n_type: u64,
}

if_alloc! {
    use error;
    use container;
    use scroll::{ctx, Pread};
    use alloc::vec::Vec;

    /// An iterator over ELF binary notes in a note section or segment
    pub struct NoteDataIterator<'a> {
        pub data: &'a [u8],
        pub size: usize,
        pub offset: usize,
        pub ctx: (usize, container::Ctx), // (alignment, ctx)
    }

    impl<'a> Iterator for NoteDataIterator<'a> {
        type Item = error::Result<Note<'a>>;
        fn next(&mut self) -> Option<Self::Item> {
            if self.offset >= self.size {
                None
            } else {
                debug!("NoteIterator - {:#x}", self.offset);
                match self.data.gread_with(&mut self.offset, self.ctx) {
                    Ok(res) => Some(Ok(res)),
                    Err(e) => Some(Err(e.into()))
                }
            }
        }
    }

    /// An iterator over ELF binary notes
    pub struct NoteIterator<'a> {
        pub iters: Vec<NoteDataIterator<'a>>,
        pub index: usize,
    }

    impl<'a> Iterator for NoteIterator<'a> {
        type Item = error::Result<Note<'a>>;
        fn next(&mut self) -> Option<Self::Item> {
            while self.index < self.iters.len() {
                if let Some(note_result) = self.iters[self.index].next() {
                    return Some(note_result);
                }

                self.index += 1;
            }

            None
        }
    }

    #[derive(Debug)]
    struct NoteHeader {
        n_namesz: usize,
        n_descsz: usize,
        n_type: u32,
    }

    impl From<Nhdr32> for NoteHeader {
        fn from(header: Nhdr32) -> Self {
            NoteHeader {
                n_namesz: header.n_namesz as usize,
                n_descsz: header.n_descsz as usize,
                n_type: header.n_type,
            }
        }
    }

    impl From<Nhdr64> for NoteHeader {
        fn from(header: Nhdr64) -> Self {
            NoteHeader {
                n_namesz: header.n_namesz as usize,
                n_descsz: header.n_descsz as usize,
                n_type: header.n_type as u32,
            }
        }
    }

    fn align(alignment: usize, offset: &mut usize) {
        let diff = *offset % alignment;
        if diff != 0 {
            *offset += alignment - diff;
        }
    }

    /// A 32/64 bit Note struct, with the name and desc pre-parsed
    #[derive(Debug)]
    pub struct Note<'a> {
        /// The type of this note
        pub n_type: u32,
        /// NUL terminated string, where `namesz` includes the terminator
        pub name: &'a str, // needs padding such that namesz + padding % {wordsize} == 0
        /// arbitrary data of length `descsz`
        pub desc: &'a [u8], // needs padding such that descsz + padding % {wordsize} == 0
    }

    impl<'a> Note<'a> {
        pub fn type_to_str(&self) -> &'static str {
            match self.n_type {
                NT_GNU_ABI_TAG => "NT_GNU_ABI_TAG",
                NT_GNU_HWCAP => "NT_GNU_HWCAP",
                NT_GNU_BUILD_ID => "NT_GNU_BUILD_ID",
                NT_GNU_GOLD_VERSION => "NT_GNU_GOLD_VERSION",
                _ => "NT_UNKNOWN"
            }
        }
    }

    impl<'a> ctx::TryFromCtx<'a, (usize, container::Ctx)> for Note<'a> {
        type Error = error::Error;
        type Size = usize;
        fn try_from_ctx(bytes: &'a [u8], (alignment, ctx): (usize, container::Ctx)) -> Result<(Self, Self::Size), Self::Error> {
            let offset = &mut 0;
            let header: NoteHeader = {
                match alignment {
                    4 => bytes.gread_with::<Nhdr32>(offset, ctx.le)?.into(),
                    // this is a guess; i haven't seen gcc/clang compilers emit 64-bit notes, and i don't have any non gcc/clang compilers
                    8 => bytes.gread_with::<Nhdr64>(offset, ctx.le)?.into(),
                    _ => return Err(error::Error::Malformed(format!("Notes has unimplemented alignment requirement: {:#x}", alignment)))
                }
            };
            debug!("{:?} - {:#x}", header, *offset);
            // -1 because includes \0 terminator
            let name = bytes.gread_with::<&'a str>(offset, ctx::StrCtx::Length(header.n_namesz - 1))?;
            *offset += 1;
            align(alignment, offset);
            debug!("note name {} - {:#x}", name, *offset);
            let desc = bytes.gread_with::<&'a [u8]>(offset, header.n_descsz)?;
            align(alignment, offset);
            debug!("desc {:?} - {:#x}", desc, *offset);
            Ok((Note {
                name,
                desc,
                n_type: header.n_type,
            }, *offset))
        }
    }

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

        static NOTE_DATA: [u8; 68] = [0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
                                     0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
                                     0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
                                     0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
                                     0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
                                     0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
                                     0xbc, 0xfc, 0x66, 0xcd, 0xc7, 0xd5, 0x14, 0x7b,
                                     0x53, 0xb1, 0x10, 0x11, 0x94, 0x86, 0x8e, 0xf9,
                                     0x4f, 0xe8, 0xdd, 0xdb];

        static CONTEXT: (usize, container::Ctx) = (4, container::Ctx {
            container: container::Container::Big,
            le: ::scroll::Endian::Little,
        });

        fn make_note_iter(start: usize, end: usize) -> NoteDataIterator<'static> {
            NoteDataIterator {
                data: &NOTE_DATA,
                size: end,
                offset: start,
                ctx: CONTEXT,
            }
        }

        #[test]
        fn iter_single_section() {
            let mut notes = NoteIterator {
                iters: vec![make_note_iter(0, 68)],
                index: 0,
            };

            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
            assert!(notes.next().is_none());
        }

        #[test]
        fn iter_multiple_sections() {
            let mut notes = NoteIterator {
                iters: vec![make_note_iter(0, 32), make_note_iter(32, 68)],
                index: 0,
            };

            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
            assert!(notes.next().is_none());
        }

        #[test]
        fn skip_empty_sections() {
            let mut notes = NoteIterator {
                iters: vec![
                    make_note_iter(0, 32),
                    make_note_iter(0, 0),
                    make_note_iter(32, 68),
                ],
                index: 0,
            };

            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
            assert!(notes.next().is_none());
        }

        #[test]
        fn ignore_no_sections() {
            let mut notes = NoteIterator { iters: vec![], index: 0 };
            assert!(notes.next().is_none());
        }
    }
}
<commit_msg>treat note  alignments as in readelf<commit_after>// Defined note types for GNU systems.

// ABI information.  The descriptor consists of words:
//    word 0: OS descriptor
//    word 1: major version of the ABI
//    word 2: minor version of the ABI
//    word 3: subminor version of the ABI

pub const NT_GNU_ABI_TAG: u32 = 1;
// Old name
pub const ELF_NOTE_ABI: u32 = NT_GNU_ABI_TAG;
// Known OSes.  These values can appear in word 0 of an
//    NT_GNU_ABI_TAG note section entry.
pub const ELF_NOTE_OS_LINUX: u32 = 0;
pub const ELF_NOTE_OS_GNU: u32 = 1;
pub const ELF_NOTE_OS_SOLARIS2: u32 = 2;
pub const ELF_NOTE_OS_FREEBSD: u32 = 3;

// Synthetic hwcap information.  The descriptor begins with two words:
//    word 0: number of entries
//    word 1: bitmask of enabled entries
//    Then follow variable-length entries, one byte followed by a
//    '\0'-terminated hwcap name string.  The byte gives the bit
//    number to test if enabled, (1U << bit) & bitmask.
pub const NT_GNU_HWCAP: u32 = 2;

// Build ID bits as generated by ld --build-id.
//    The descriptor consists of any nonzero number of bytes.
pub const NT_GNU_BUILD_ID: u32 = 3;

// Version note generated by GNU gold containing a version string.
pub const NT_GNU_GOLD_VERSION: u32 = 4;

#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))]
#[repr(C)]
/// Note section contents. Each entry in the note section begins with a header of a fixed form.
pub struct Nhdr32 {
    /// Length of the note's name (includes the terminator)
    pub n_namesz: u32,
    /// Length of the note's descriptor
    pub n_descsz: u32,
    /// Type of the note
    pub n_type: u32,
}

#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))]
#[repr(C)]
/// Note section contents. Each entry in the note section begins with a header of a fixed form.
pub struct Nhdr64 {
    /// Length of the note's name (includes the terminator)
    pub n_namesz: u64,
    /// Length of the note's descriptor.
    pub n_descsz: u64,
    /// Type of the note.
    pub n_type: u64,
}

if_alloc! {
    use error;
    use container;
    use scroll::{ctx, Pread};
    use alloc::vec::Vec;

    /// An iterator over ELF binary notes in a note section or segment
    pub struct NoteDataIterator<'a> {
        pub data: &'a [u8],
        pub size: usize,
        pub offset: usize,
        pub ctx: (usize, container::Ctx), // (alignment, ctx)
    }

    impl<'a> Iterator for NoteDataIterator<'a> {
        type Item = error::Result<Note<'a>>;
        fn next(&mut self) -> Option<Self::Item> {
            if self.offset >= self.size {
                None
            } else {
                debug!("NoteIterator - {:#x}", self.offset);
                match self.data.gread_with(&mut self.offset, self.ctx) {
                    Ok(res) => Some(Ok(res)),
                    Err(e) => Some(Err(e.into()))
                }
            }
        }
    }

    /// An iterator over ELF binary notes
    pub struct NoteIterator<'a> {
        pub iters: Vec<NoteDataIterator<'a>>,
        pub index: usize,
    }

    impl<'a> Iterator for NoteIterator<'a> {
        type Item = error::Result<Note<'a>>;
        fn next(&mut self) -> Option<Self::Item> {
            while self.index < self.iters.len() {
                if let Some(note_result) = self.iters[self.index].next() {
                    return Some(note_result);
                }

                self.index += 1;
            }

            None
        }
    }

    #[derive(Debug)]
    struct NoteHeader {
        n_namesz: usize,
        n_descsz: usize,
        n_type: u32,
    }

    impl From<Nhdr32> for NoteHeader {
        fn from(header: Nhdr32) -> Self {
            NoteHeader {
                n_namesz: header.n_namesz as usize,
                n_descsz: header.n_descsz as usize,
                n_type: header.n_type,
            }
        }
    }

    impl From<Nhdr64> for NoteHeader {
        fn from(header: Nhdr64) -> Self {
            NoteHeader {
                n_namesz: header.n_namesz as usize,
                n_descsz: header.n_descsz as usize,
                n_type: header.n_type as u32,
            }
        }
    }

    fn align(alignment: usize, offset: &mut usize) {
        let diff = *offset % alignment;
        if diff != 0 {
            *offset += alignment - diff;
        }
    }

    /// A 32/64 bit Note struct, with the name and desc pre-parsed
    #[derive(Debug)]
    pub struct Note<'a> {
        /// The type of this note
        pub n_type: u32,
        /// NUL terminated string, where `namesz` includes the terminator
        pub name: &'a str, // needs padding such that namesz + padding % {wordsize} == 0
        /// arbitrary data of length `descsz`
        pub desc: &'a [u8], // needs padding such that descsz + padding % {wordsize} == 0
    }

    impl<'a> Note<'a> {
        pub fn type_to_str(&self) -> &'static str {
            match self.n_type {
                NT_GNU_ABI_TAG => "NT_GNU_ABI_TAG",
                NT_GNU_HWCAP => "NT_GNU_HWCAP",
                NT_GNU_BUILD_ID => "NT_GNU_BUILD_ID",
                NT_GNU_GOLD_VERSION => "NT_GNU_GOLD_VERSION",
                _ => "NT_UNKNOWN"
            }
        }
    }

    impl<'a> ctx::TryFromCtx<'a, (usize, container::Ctx)> for Note<'a> {
        type Error = error::Error;
        type Size = usize;
        fn try_from_ctx(bytes: &'a [u8], (alignment, ctx): (usize, container::Ctx)) -> Result<(Self, Self::Size), Self::Error> {
            let offset = &mut 0;
            let mut alignment = alignment;
            if alignment < 4 {
                alignment = 4;
            }
            let header: NoteHeader = {
                match alignment {
                    4|8 => bytes.gread_with::<Nhdr32>(offset, ctx.le)?.into(),
                    _ => return Err(error::Error::Malformed(format!("Notes has unimplemented alignment requirement: {:#x}", alignment)))
                }
            };
            debug!("{:?} - {:#x}", header, *offset);
            // -1 because includes \0 terminator
            let name = bytes.gread_with::<&'a str>(offset, ctx::StrCtx::Length(header.n_namesz - 1))?;
            *offset += 1;
            align(alignment, offset);
            debug!("note name {} - {:#x}", name, *offset);
            let desc = bytes.gread_with::<&'a [u8]>(offset, header.n_descsz)?;
            align(alignment, offset);
            debug!("desc {:?} - {:#x}", desc, *offset);
            Ok((Note {
                name,
                desc,
                n_type: header.n_type,
            }, *offset))
        }
    }

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

        static NOTE_DATA: [u8; 68] = [0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
                                     0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
                                     0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
                                     0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
                                     0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
                                     0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
                                     0xbc, 0xfc, 0x66, 0xcd, 0xc7, 0xd5, 0x14, 0x7b,
                                     0x53, 0xb1, 0x10, 0x11, 0x94, 0x86, 0x8e, 0xf9,
                                     0x4f, 0xe8, 0xdd, 0xdb];

        static CONTEXT: (usize, container::Ctx) = (4, container::Ctx {
            container: container::Container::Big,
            le: ::scroll::Endian::Little,
        });

        fn make_note_iter(start: usize, end: usize) -> NoteDataIterator<'static> {
            NoteDataIterator {
                data: &NOTE_DATA,
                size: end,
                offset: start,
                ctx: CONTEXT,
            }
        }

        #[test]
        fn iter_single_section() {
            let mut notes = NoteIterator {
                iters: vec![make_note_iter(0, 68)],
                index: 0,
            };

            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
            assert!(notes.next().is_none());
        }

        #[test]
        fn iter_multiple_sections() {
            let mut notes = NoteIterator {
                iters: vec![make_note_iter(0, 32), make_note_iter(32, 68)],
                index: 0,
            };

            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
            assert!(notes.next().is_none());
        }

        #[test]
        fn skip_empty_sections() {
            let mut notes = NoteIterator {
                iters: vec![
                    make_note_iter(0, 32),
                    make_note_iter(0, 0),
                    make_note_iter(32, 68),
                ],
                index: 0,
            };

            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
            assert!(notes.next().is_none());
        }

        #[test]
        fn ignore_no_sections() {
            let mut notes = NoteIterator { iters: vec![], index: 0 };
            assert!(notes.next().is_none());
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (2015-2017) Michael 'ExpHP' Lamparski
//
// Licensed under the terms of the MIT License, available at:
//  http://opensource.org/licenses/MIT
// and also included in the file COPYING at the root of this distribution.
// This file may not be copied, modified, or distributed except according
// to those terms.

use std::default::Default;
use std::ops::Mul;
use std::cmp::{min,max};
use std::collections::BTreeMap;

use num;
use num::{One, Num, Integer, FromPrimitive};

use crate::FactorExt;

/// An integer stored as its prime factorization.
///
/// A `Factored` represents a non-negative integer stored in the form
///
/// ```text
/// pow(p1, n1) * pow(p2, n2) * ... * pow(pN, nN)
/// ```
///
/// where `p1, p2, ...` are prime numbers (or zero) and `n1, n2, ...` are positive
/// integers. Some properties are easier to compute in this form, such as the euler
/// totient, or a sum of divisors. Also of note is that because the prime factorization
/// of any given number is unique, any two `Factorization`s constructed from the same
/// `x` are equal.
///
/// Negative numbers, zero, as well as negative powers (i.e. rationals) are not
/// permitted.
///
/// One caveat:  It is not feasible for `Factorization` to enforce that the prime
///  factors it is built from are actually prime.  This is the job of the
///  `TryFactor` used to produce it!  The behavior of any mathematical method
///  on a `Factorization` with a composite factor is **undefined**. (in the sense
///  of nonsensical results; not in the sense that the safety guidelines of Rust
///  may be violated)
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct Factored<X> {
    // powers are currently usize for consistency with the pow() function
    //  in the num crate.
    powers: BTreeMap<X, usize>,
}

// TODO: some of Factorization's methods need to be special cased for Zero

impl<X> Factored<X>
where
    X: Ord,
{
    /// Set the power of a prime factor in the `Factorization`. `prime` is
    ///  assumed to be prime. Note that while composite numbers
    ///  may be accepted, the behavior of the resulting `Factorization` is
    ///  ill-defined.
    // NOTE: No diagnostic is provided because it would require some sort
    //  of additional bound like Zero, One, or NumCast.
    pub fn set(&mut self, prime: X, exp: usize) {
        match exp {
            0 => self.powers.remove(&prime),
            _ => self.powers.insert(prime, exp),
        };
    }

    /// Get the power of a prime factor in the `Factorization`. `prime` is
    ///  assumed to be prime. The value returned for non-prime
    ///  keys is undefined.
    pub fn get(&self, prime: &X) -> usize {
        match self.powers.get(prime) {
            None => 0,
            Some(&exp) => exp,
        }
    }

    /// Create a `Factored` from a single prime.
    ///
    /// Do not use on a composite number (use `factor::factorize`)
    /// or one (use `One::one()`).
    pub fn from_prime(prime: X) -> Self {
        Factored {
            powers: ::std::iter::once((prime, 1)).collect(),
        }
    }

    /// Recover the integer represented by the `Factored`.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let f = factorize(20u64);
    /// assert_eq!(f.product(), 20);
    /// ```
    pub fn product(&self) -> X
    where
        X: Clone + One,
    {
        self.iter().map(clone2).factor_product()
    }

    /// Compute the total number of positive integers which evenly divide
    ///  a number from its prime decomposition.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let f = factorize(20u64);
    /// assert_eq!(f.divisor_count(), 6);
    /// ```
    pub fn divisor_count(&self) -> X
    where
        X: Clone + FromPrimitive + One,
    {
        self.iter().map(clone2).divisor_count()
    }

    /// Compute the sum of all positive integers which evenly divide the number
    ///  represented by the `Factored` (including 1 and the number itself).
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let f = factorize(20u64);
    /// assert_eq!(f.divisor_sum(), 42);
    /// ```
    pub fn divisor_sum(&self) -> X
    where
        X: Clone + One + Num,
    {
        self.iter().map(clone2).divisor_sum()
    }

    /// Compute the Euler totient `phi(x)`, the total number of
    ///  positive integers less than `x` that are coprime to `x`.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let f = factorize(20u64);
    /// assert_eq!(f.totient(), 8);
    /// ```
    pub fn totient(&self) -> X
    where
        X: Clone + One + Num,
    {
        self.iter().map(clone2).totient()
    }

    /// Compute the value modulo `m`.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::prelude::*;
    ///
    /// let f: factor::Factored<_> = (1..=20u64).map(factor::factorize).product();
    /// assert_eq!(f.product_mod(&1297), 278);
    /// ```
    pub fn product_mod(self, modulus: &X) -> X
    where
        X: Clone + One + Integer,
        Self: Sized,
    {
        self.iter().map(clone2).factor_product_mod(modulus)
    }
}

impl<X> Factored<X>
where
    X: Ord,
{
    /// Get the square root of a perfect square.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let mut f = factorize(20u64);
    /// assert!(f.clone().sqrt().is_none());    // 20 is not square!
    ///
    /// f = f * factorize(5);
    /// assert_eq!(f.sqrt().unwrap().product(), 10);  // sqrt(100)
    /// ```
    pub fn sqrt(self) -> Option<Self> {
        self.root(2)
    }

    /// Get the nth root of a perfect nth power.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let mut f = factorize(20u64);
    /// assert!(f.clone().root(3).is_none());    // 20 is not a cube!
    ///
    /// f = f * factorize(50);
    /// assert_eq!(f.root(3).unwrap().product(), 10);  // cbrt(1000)
    /// ```
    pub fn root(mut self, n: usize) -> Option<Self> {
        for (_, v) in self.powers.iter_mut() {
            if *v % n == 1 {
                return None; // Not a root!  Abort!  Abort!
            };

            *v = *v / n;
        }
        Some(self)
    }

    // TODO: Docs & test
    pub fn pow(mut self, pwr: usize) -> Self
    where
        X: Clone,
    {
        for (_, v) in self.powers.iter_mut() {
            *v = *v * pwr;
        }
        self
    }

    /*
        // TODO: Docs & test
        pub fn gcd(mut self, other: mut Self) -> Self
         where X: Clone,
        {
            // Iterate through shorter list
            if other.powers.len() < self.powers.len() {
                return self.gcd(other);
            };

            let mut result: Self = One::one();
            for (k,v) in other {
                self.set(k.clone(), min(self.get(&k), v));
            };
            self
        }

        // TODO: Docs & test
        pub fn lcm(&self, other: &Self) -> Self
         where X: Clone,
        {
            let mut result = self.clone();
            for (k,v) in self.powers.clone().into_iter() {
                self.set(k.clone(), max(self.get(&k), v));
            };
            self
        }
    */

    /// Iterate over unique prime factors.
    ///
    /// Item type is `&X`.
    pub fn primes(&self) -> Primes<'_, X> {
        self.powers.keys()
    }

    /// Iterate over `(prime, power)` pairs.
    ///
    /// Item type is `(&X, usize)`.
    pub fn iter(&self) -> Iter<'_, X> {
        self.powers.iter()
    }

    /// Consume to iterate over the `(prime, power)` pairs.
    ///
    /// Item type is `(X, usize)`.
    pub fn into_iter(self) -> IntoIter<X> {
        self.powers.into_iter()
    }

    /// Immutably borrow the underlying map.
    pub fn as_btree_map(&self) -> &BTreeMap<X, usize> {
        &self.powers
    }
    /// Mutably borrow the underlying map.
    ///
    /// This is provided on the grounds of "eh, why the hell not."  After all,
    /// it's not exactly like `Factored` was doing a very good job at protecting
    /// any of its invariants to begin with.
    pub fn as_mut_btree_map(&mut self) -> &mut BTreeMap<X, usize> {
        &mut self.powers
    }
    /// Consume to obtain the underlying map.
    pub fn into_btree_map(self) -> BTreeMap<X, usize> {
        self.powers
    }

    /// Construct from a map of unique `(prime, power)` pairs.
    pub fn from_btree_map(map: BTreeMap<X, usize>) -> Self {
        Factored { powers: map }
    }

    /// Construct from an iterator of unique `(prime, power)` pairs.
    pub fn from_iter<I: IntoIterator<Item = (X, usize)>>(iter: I) -> Self {
        Factored::from_btree_map(iter.into_iter().collect())
    }

    // TODO: Reasonable (?) methods to implement
    // pub fn is_multiple_of(&self, other: &Self) -> bool
}

impl<X> One for Factored<X>
where
    X: Ord + Clone,
{
    /// Create a `Factored` representing `1`.
    fn one() -> Self {
        Self::from_btree_map(BTreeMap::new())
    }
}

impl<X> Default for Factored<X>
where
    X: Ord + Clone,
{
    /// Create a `Factored` representing `1`.
    fn default() -> Self {
        One::one()
    }
}

impl<X> Mul<Factored<X>> for Factored<X>
where
    X: Ord + Clone,
{
    type Output = Self;
    fn mul(self, other: Self) -> Self {
        let mut result = self;
        for (k, v) in other.powers.into_iter() {
            let current = result.get(&k); // owned proxy
            result.set(k.clone(), current + v);
        }
        result
    }
}

impl<X> std::iter::Product for Factored<X>
where
    X: Ord + Clone,
{
    fn product<I: Iterator<Item=Factored<X>>>(iter: I) -> Self {
        iter.fold(Default::default(), |acc, x| acc * x)
    }
}

//------------------------------------

pub type Iter<'a, T> = ::std::collections::btree_map::Iter<'a, T, usize>;
pub type IntoIter<T> = ::std::collections::btree_map::IntoIter<T, usize>;
pub type Primes<'a, T> = ::std::collections::btree_map::Keys<'a, T, usize>;

#[inline]
fn clone2<A, B>((a, b): (&A, &B)) -> (A, B)
where
    A: Clone,
    B: Clone,
{
    (a.clone(), b.clone())
}
<commit_msg>impl IntoIterator for Factored, add checked_div<commit_after>// Copyright (2015-2017) Michael 'ExpHP' Lamparski
//
// Licensed under the terms of the MIT License, available at:
//  http://opensource.org/licenses/MIT
// and also included in the file COPYING at the root of this distribution.
// This file may not be copied, modified, or distributed except according
// to those terms.

use std::default::Default;
use std::ops::{Mul, Div, MulAssign, DivAssign};
use std::cmp::{min,max};
use std::collections::BTreeMap;

use num;
use num::{One, Num, Integer, FromPrimitive};

use crate::FactorExt;

/// An integer stored as its prime factorization.
///
/// A `Factored` represents a non-negative integer stored in the form
///
/// ```text
/// pow(p1, n1) * pow(p2, n2) * ... * pow(pN, nN)
/// ```
///
/// where `p1, p2, ...` are prime numbers (or zero) and `n1, n2, ...` are positive
/// integers. Some properties are easier to compute in this form, such as the euler
/// totient, or a sum of divisors. Also of note is that because the prime factorization
/// of any given number is unique, any two `Factorization`s constructed from the same
/// `x` are equal.
///
/// Negative numbers, zero, as well as negative powers (i.e. rationals) are not
/// permitted.
///
/// One caveat:  It is not feasible for `Factorization` to enforce that the prime
///  factors it is built from are actually prime.  This is the job of the
///  `TryFactor` used to produce it!  The behavior of any mathematical method
///  on a `Factorization` with a composite factor is **undefined**. (in the sense
///  of nonsensical results; not in the sense that the safety guidelines of Rust
///  may be violated)
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct Factored<X> {
    // powers are currently usize for consistency with the pow() function
    //  in the num crate.
    powers: BTreeMap<X, usize>,
}

// TODO: some of Factorization's methods need to be special cased for Zero

impl<X> Factored<X>
where
    X: Ord,
{
    /// Set the power of a prime factor in the `Factorization`. `prime` is
    ///  assumed to be prime. Note that while composite numbers
    ///  may be accepted, the behavior of the resulting `Factorization` is
    ///  ill-defined.
    // NOTE: No diagnostic is provided because it would require some sort
    //  of additional bound like Zero, One, or NumCast.
    pub fn set(&mut self, prime: X, exp: usize) {
        match exp {
            0 => self.powers.remove(&prime),
            _ => self.powers.insert(prime, exp),
        };
    }

    /// Get the power of a prime factor in the `Factorization`. `prime` is
    ///  assumed to be prime. The value returned for non-prime
    ///  keys is undefined.
    pub fn get(&self, prime: &X) -> usize {
        match self.powers.get(prime) {
            None => 0,
            Some(&exp) => exp,
        }
    }

    /// Create a `Factored` from a single prime.
    ///
    /// Do not use on a composite number (use `factor::factorize`)
    /// or one (use `One::one()`).
    pub fn from_prime(prime: X) -> Self {
        Factored {
            powers: ::std::iter::once((prime, 1)).collect(),
        }
    }

    /// Recover the integer represented by the `Factored`.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let f = factorize(20u64);
    /// assert_eq!(f.product(), 20);
    /// ```
    pub fn product(&self) -> X
    where
        X: Clone + One,
    {
        self.iter().map(clone2).factor_product()
    }

    /// Compute the total number of positive integers which evenly divide
    ///  a number from its prime decomposition.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let f = factorize(20u64);
    /// assert_eq!(f.divisor_count(), 6);
    /// ```
    pub fn divisor_count(&self) -> X
    where
        X: Clone + FromPrimitive + One,
    {
        self.iter().map(clone2).divisor_count()
    }

    /// Compute the sum of all positive integers which evenly divide the number
    ///  represented by the `Factored` (including 1 and the number itself).
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let f = factorize(20u64);
    /// assert_eq!(f.divisor_sum(), 42);
    /// ```
    pub fn divisor_sum(&self) -> X
    where
        X: Clone + One + Num,
    {
        self.iter().map(clone2).divisor_sum()
    }

    /// Compute the Euler totient `phi(x)`, the total number of
    ///  positive integers less than `x` that are coprime to `x`.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let f = factorize(20u64);
    /// assert_eq!(f.totient(), 8);
    /// ```
    pub fn totient(&self) -> X
    where
        X: Clone + One + Num,
    {
        self.iter().map(clone2).totient()
    }

    /// Compute the value modulo `m`.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::prelude::*;
    ///
    /// let f: factor::Factored<_> = (1..=20u64).map(factor::factorize).product();
    /// assert_eq!(f.product_mod(&1297), 278);
    /// ```
    pub fn product_mod(self, modulus: &X) -> X
    where
        X: Clone + One + Integer,
        Self: Sized,
    {
        self.iter().map(clone2).factor_product_mod(modulus)
    }
}

impl<X> Factored<X>
where
    X: Ord,
{
    /// Get the square root of a perfect square.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let mut f = factorize(20u64);
    /// assert!(f.clone().sqrt().is_none());    // 20 is not square!
    ///
    /// f = f * factorize(5);
    /// assert_eq!(f.sqrt().unwrap().product(), 10);  // sqrt(100)
    /// ```
    pub fn sqrt(self) -> Option<Self> {
        self.root(2)
    }

    /// Get the nth root of a perfect nth power.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// let mut f = factorize(20u64);
    /// assert!(f.clone().root(3).is_none());    // 20 is not a cube!
    ///
    /// f = f * factorize(50);
    /// assert_eq!(f.root(3).unwrap().product(), 10);  // cbrt(1000)
    /// ```
    pub fn root(mut self, n: usize) -> Option<Self> {
        for (_, v) in self.powers.iter_mut() {
            if *v % n == 1 {
                return None; // Not a root!  Abort!  Abort!
            };

            *v = *v / n;
        }
        Some(self)
    }

    // TODO: Docs & test
    pub fn pow(mut self, pwr: usize) -> Self
    where
        X: Clone,
    {
        for (_, v) in self.powers.iter_mut() {
            *v = *v * pwr;
        }
        self
    }

    /*
        // TODO: Docs & test
        pub fn gcd(mut self, other: mut Self) -> Self
         where X: Clone,
        {
            // Iterate through shorter list
            if other.powers.len() < self.powers.len() {
                return self.gcd(other);
            };

            let mut result: Self = One::one();
            for (k,v) in other {
                self.set(k.clone(), min(self.get(&k), v));
            };
            self
        }

        // TODO: Docs & test
        pub fn lcm(&self, other: &Self) -> Self
         where X: Clone,
        {
            let mut result = self.clone();
            for (k,v) in self.powers.clone().into_iter() {
                self.set(k.clone(), max(self.get(&k), v));
            };
            self
        }
    */

    /// Divide by another factorized value.
    ///
    /// Cannot produce fractional values.
    ///
    /// # Example:
    ///
    /// ```
    /// use factor::factorize;
    ///
    /// assert_eq!(factorize(15u64).checked_div(factorize(5u64)), Some(factorize(3u64)));
    /// assert_eq!(factorize(15u64).checked_div(factorize(2u64)), None);
    /// ```
    pub fn checked_div(&self, other: impl FactorExt<X>) -> Option<Factored<X>>
    where
        X: Clone,
    {
        let mut result = self.clone();
        for (k, v) in other {
            let current = result.get(&k);
            match current.checked_sub(v) {
                Some(v) => result.set(k.clone(), v),
                None => return None,
            }
        }
        Some(result)
    }

    /// Iterate over unique prime factors.
    ///
    /// Item type is `&X`.
    pub fn primes(&self) -> Primes<'_, X> {
        self.powers.keys()
    }

    /// Iterate over `(prime, power)` pairs.
    ///
    /// Item type is `(&X, usize)`.
    pub fn iter(&self) -> Iter<'_, X> {
        self.powers.iter()
    }

    /// Immutably borrow the underlying map.
    pub fn as_btree_map(&self) -> &BTreeMap<X, usize> {
        &self.powers
    }
    /// Mutably borrow the underlying map.
    ///
    /// This is provided on the grounds of "eh, why the hell not."  After all,
    /// it's not exactly like `Factored` was doing a very good job at protecting
    /// any of its invariants to begin with.
    pub fn as_mut_btree_map(&mut self) -> &mut BTreeMap<X, usize> {
        &mut self.powers
    }
    /// Consume to obtain the underlying map.
    pub fn into_btree_map(self) -> BTreeMap<X, usize> {
        self.powers
    }

    /// Construct from a map of unique `(prime, power)` pairs.
    pub fn from_btree_map(map: BTreeMap<X, usize>) -> Self {
        Factored { powers: map }
    }

    /// Construct from an iterator of unique `(prime, power)` pairs.
    pub fn from_iter<I: IntoIterator<Item = (X, usize)>>(iter: I) -> Self {
        Factored::from_btree_map(iter.into_iter().collect())
    }

    // TODO: Reasonable (?) methods to implement
    // pub fn is_multiple_of(&self, other: &Self) -> bool
}

impl<X> One for Factored<X>
where
    X: Ord + Clone,
{
    /// Create a `Factored` representing `1`.
    fn one() -> Self {
        Self::from_btree_map(BTreeMap::new())
    }
}

impl<X> Default for Factored<X>
where
    X: Ord + Clone,
{
    /// Create a `Factored` representing `1`.
    fn default() -> Self {
        One::one()
    }
}

impl<X> Mul<Factored<X>> for Factored<X>
where
    X: Ord + Clone,
{
    type Output = Self;
    fn mul(self, other: Self) -> Self {
        let mut result = self;
        for (k, v) in other.powers.into_iter() {
            let current = result.get(&k);
            result.set(k.clone(), current + v);
        }
        result
    }
}

impl<X> std::iter::Product for Factored<X>
where
    X: Ord + Clone,
{
    fn product<I: Iterator<Item=Factored<X>>>(iter: I) -> Self {
        iter.fold(Default::default(), |acc, x| acc * x)
    }
}

impl<X: Ord> IntoIterator for Factored<X>
{
    type Item = (X, usize);
    type IntoIter = IntoIter<X>;

    fn into_iter(self) -> Self::IntoIter { self.powers.into_iter() }
}

//------------------------------------

pub type Iter<'a, T> = ::std::collections::btree_map::Iter<'a, T, usize>;
pub type IntoIter<T> = ::std::collections::btree_map::IntoIter<T, usize>;
pub type Primes<'a, T> = ::std::collections::btree_map::Keys<'a, T, usize>;

#[inline]
fn clone2<A, B>((a, b): (&A, &B)) -> (A, B)
where
    A: Clone,
    B: Clone,
{
    (a.clone(), b.clone())
}
<|endoftext|>


<commit_before>//! Aggregates the weights of equal records into at most one record.
//!
//! As differential dataflow streams are unordered and taken to be the accumulation of all records,
//! no semantic change happens via `consolidate`. However, there is a practical difference between
//! a collection that aggregates down to zero records, and one that actually has no records. The
//! underlying system can more clearly see that no work must be done in the later case, and we can
//! drop out of, e.g. iterative computations.
//!
//! #Examples
//!
//! This example performs a standard "word count", where each line of text is split into multiple
//! words, each word is converted to a word with count 1, and `consolidate` then accumulates the
//! counts of equivalent words.
//!
//! ```ignore
//! stream.flat_map(|line| line.split_whitespace())
//!       .map(|word| (word, 1))
//!       .consolidate();
//! ```

use std::rc::Rc;
use std::fmt::Debug;

use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::dataflow::channels::pact::Exchange;
// use timely::dataflow::channels::Content;

use collection::Lookup;
use iterators::coalesce::Coalesce;
use radix_sort::{RadixSorter, Unsigned};

use ::Data;

use timely::drain::DrainExt;

/// An extension method for consolidating weighted streams.
pub trait ConsolidateExt<D: Data> {
    /// Aggregates the weights of equal records into at most one record.
    fn consolidate(&self) -> Self;

    /// Aggregates the weights of equal records into at most one record, partitions data using the
    /// supplied partition function.
    fn consolidate_by<U: Unsigned, F: Fn(&D)->U+'static>(&self, part: F) -> Self;
}

impl<G: Scope, D: Ord+Data+Debug> ConsolidateExt<D> for Stream<G, (D, i32)> {
    fn consolidate(&self) -> Self {
       self.consolidate_by(|x| x.hashed())
    }
    fn consolidate_by<U: Unsigned, F: Fn(&D)->U+'static>(&self, part: F) -> Self {

        let mut inputs = Vec::new();    // Vec<(G::Timestamp, Vec<(D, i32))>
        let part1 = Rc::new(part);
        let part2 = part1.clone();

        let exch = Exchange::new(move |&(ref x,_)| (*part1)(x).as_u64());
        self.unary_notify(exch, "Consolidate", vec![], move |input, output, notificator| {

            // input.for_each(|index: &G::Timestamp, data: &mut Content<(D, i32)>| {
            while let Some((index, data)) = input.next() {
                notificator.notify_at(&index);
                inputs.entry_or_insert(index.clone(), || RadixSorter::new())
                      .extend(data.drain_temp(), &|x| (*part2)(&x.0));
            }
            // });

            // 2. go through each time of interest that has reached completion
            while let Some((index, _count)) = notificator.next() {
            // notificator.for_each(|index, _count| {
                if let Some(mut stash) = inputs.remove_key(&index) {

                    let start = ::time::precise_time_s();

                    let mut session = output.session(&index);
                    let mut buffer = vec![];
                    let mut current = 0;

                    let source = stash.finish(&|x| (*part2)(&x.0));
                    for (datum, wgt) in source.into_iter().flat_map(|x| x.into_iter()) {
                        let hash = (*part2)(&datum).as_u64();
                        if buffer.len() > 0 && hash != current {
                            buffer.sort_by(|x: &(D,i32),y: &(D,i32)| x.0.cmp(&y.0));
                            session.give_iterator(buffer.drain_temp().coalesce());
                            current = hash;
                        }
                        buffer.push((datum,wgt));
                    }

                    if buffer.len() > 0 {
                        buffer.sort_by(|x: &(D,i32),y: &(D,i32)| x.0.cmp(&y.0));
                        session.give_iterator(buffer.drain_temp().coalesce());
                    }

                    println!("consolidated {:?} in {:?}s", index, ::time::precise_time_s() - start);
                }
            }
            // });
        })
    }
}
<commit_msg>removing printing<commit_after>//! Aggregates the weights of equal records into at most one record.
//!
//! As differential dataflow streams are unordered and taken to be the accumulation of all records,
//! no semantic change happens via `consolidate`. However, there is a practical difference between
//! a collection that aggregates down to zero records, and one that actually has no records. The
//! underlying system can more clearly see that no work must be done in the later case, and we can
//! drop out of, e.g. iterative computations.
//!
//! #Examples
//!
//! This example performs a standard "word count", where each line of text is split into multiple
//! words, each word is converted to a word with count 1, and `consolidate` then accumulates the
//! counts of equivalent words.
//!
//! ```ignore
//! stream.flat_map(|line| line.split_whitespace())
//!       .map(|word| (word, 1))
//!       .consolidate();
//! ```

use std::rc::Rc;
use std::fmt::Debug;

use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::dataflow::channels::pact::Exchange;
// use timely::dataflow::channels::Content;

use collection::Lookup;
use iterators::coalesce::Coalesce;
use radix_sort::{RadixSorter, Unsigned};

use ::Data;

use timely::drain::DrainExt;

/// An extension method for consolidating weighted streams.
pub trait ConsolidateExt<D: Data> {
    /// Aggregates the weights of equal records into at most one record.
    fn consolidate(&self) -> Self;

    /// Aggregates the weights of equal records into at most one record, partitions data using the
    /// supplied partition function.
    fn consolidate_by<U: Unsigned, F: Fn(&D)->U+'static>(&self, part: F) -> Self;
}

impl<G: Scope, D: Ord+Data+Debug> ConsolidateExt<D> for Stream<G, (D, i32)> {
    fn consolidate(&self) -> Self {
       self.consolidate_by(|x| x.hashed())
    }
    fn consolidate_by<U: Unsigned, F: Fn(&D)->U+'static>(&self, part: F) -> Self {

        let mut inputs = Vec::new();    // Vec<(G::Timestamp, Vec<(D, i32))>
        let part1 = Rc::new(part);
        let part2 = part1.clone();

        let exch = Exchange::new(move |&(ref x,_)| (*part1)(x).as_u64());
        self.unary_notify(exch, "Consolidate", vec![], move |input, output, notificator| {

            // input.for_each(|index: &G::Timestamp, data: &mut Content<(D, i32)>| {
            while let Some((index, data)) = input.next() {
                notificator.notify_at(&index);
                inputs.entry_or_insert(index.clone(), || RadixSorter::new())
                      .extend(data.drain_temp(), &|x| (*part2)(&x.0));
            }
            // });

            // 2. go through each time of interest that has reached completion
            while let Some((index, _count)) = notificator.next() {
            // notificator.for_each(|index, _count| {
                if let Some(mut stash) = inputs.remove_key(&index) {

                    // let start = ::time::precise_time_s();

                    let mut session = output.session(&index);
                    let mut buffer = vec![];
                    let mut current = 0;

                    let source = stash.finish(&|x| (*part2)(&x.0));
                    for (datum, wgt) in source.into_iter().flat_map(|x| x.into_iter()) {
                        let hash = (*part2)(&datum).as_u64();
                        if buffer.len() > 0 && hash != current {
                            buffer.sort_by(|x: &(D,i32),y: &(D,i32)| x.0.cmp(&y.0));
                            session.give_iterator(buffer.drain_temp().coalesce());
                            current = hash;
                        }
                        buffer.push((datum,wgt));
                    }

                    if buffer.len() > 0 {
                        buffer.sort_by(|x: &(D,i32),y: &(D,i32)| x.0.cmp(&y.0));
                        session.give_iterator(buffer.drain_temp().coalesce());
                    }

                    // println!("consolidated {:?} in {:?}s", index, ::time::precise_time_s() - start);
                }
            }
            // });
        })
    }
}
<|endoftext|>


<commit_before>//extern crate gcc;

use std::process::Command;
use std::env;
use std::path::Path;

fn llvm_config_get(llvm_config: &str, config: &str) -> String {
    let cmd = Command::new(llvm_config).arg(config).output().unwrap();
    String::from_utf8(cmd.stdout).unwrap().trim().to_string()
}

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let llvm_config = {
        if let Ok(v) = env::var("LLVM_CONFIG") {
            v
        } else {
            "llvm-config-3.8".to_string()
        }
    };

    macro_rules! get_flags {
        ($arg:expr) => (
            {
                let llvm_flags = llvm_config_get(&llvm_config, $arg);
                let llvm_flags_args: Vec<String> =
                    llvm_flags
                    .split_whitespace()
                    .map(|s| s.to_owned())
                    .collect();
                llvm_flags_args
            }
            )
    }

    let llvm_cxxflags = get_flags!("--cxxflags");
    let llvm_ldflags = get_flags!("--ldflags");
    let llvm_libs = get_flags!("--libs");
    let llvm_syslibs = get_flags!("--system-libs");

    let out_obj = format!("{}/assemble.o", out_dir);
    let out_lib = format!("{}/libassemble.a", out_dir);

    if !Command::new("g++").args(&["src/c/assemble.cc", "-g", "-c", "-fPIC", "-o"])
        .arg(&out_obj)
        .args(&llvm_cxxflags)
        .status().unwrap().success()
    {
        panic!("g++ failed to build");
    }

    if !Command::new("ar").args(&["crus", &out_lib, &out_obj])
        .current_dir(&Path::new(&out_dir))
        .status().unwrap().success()
    {
        panic!("ar failed to build");
    }

    print!("cargo:rustc-flags=");
    for path in llvm_ldflags {
        print!("-L {} ", &path[2..]);
    }
    for lib in llvm_libs {
        print!("-l {} ", &lib[2..]);
    }
    for lib in llvm_syslibs {
        print!("-l {} ", &lib[2..]);
    }

    println!("");
    println!("cargo:rustc-link-lib=dylib=stdc++");
    println!("cargo:rustc-link-search=native={}", out_dir);
    println!("cargo:rustc-link-lib=static={}", "assemble");
}
<commit_msg>bypassing rust issue #16402<commit_after>//extern crate gcc;

use std::process::Command;
use std::env;
use std::path::Path;

fn llvm_config_get(llvm_config: &str, config: &str) -> String {
    let cmd = Command::new(llvm_config).arg(config).output().unwrap();
    String::from_utf8(cmd.stdout).unwrap().trim().to_string()
}

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let llvm_config = {
        if let Ok(v) = env::var("LLVM_CONFIG") {
            v
        } else {
            "llvm-config-3.8".to_string()
        }
    };

    macro_rules! get_flags {
        ($arg:expr) => (
            {
                let llvm_flags = llvm_config_get(&llvm_config, $arg);
                let llvm_flags_args: Vec<String> =
                    llvm_flags
                    .split_whitespace()
                    .map(|s| s.to_owned())
                    .collect();
                llvm_flags_args
            }
            )
    }

    let llvm_cxxflags = get_flags!("--cxxflags");
    let llvm_ldflags = get_flags!("--ldflags");
    let llvm_libs = get_flags!("--libs");
    let llvm_syslibs = get_flags!("--system-libs");

    let out_obj = format!("{}/assemble.o", out_dir);
    let out_lib = format!("{}/libassemble.a", out_dir);

    if !Command::new("g++").args(&["src/c/assemble.cc", "-g", "-c", "-fPIC", "-o"])
        .arg(&out_obj)
        .args(&llvm_cxxflags)
        .status().unwrap().success()
    {
        panic!("g++ failed to build");
    }

    if !Command::new("ar").args(&["crus", &out_lib, &out_obj])
        .current_dir(&Path::new(&out_dir))
        .status().unwrap().success()
    {
        panic!("ar failed to build");
    }

    print!("cargo:rustc-flags=");
    for path in llvm_ldflags {
        print!("-L native={} ", &path[2..]);
    }
    for lib in llvm_libs {
        print!("-l {} ", &lib[2..]);
    }
    for lib in llvm_syslibs {
        print!("-l {} ", &lib[2..]);
    }

    println!("");
    println!("cargo:rustc-link-lib=dylib=stdc++");
    println!("cargo:rustc-link-search=native={}", out_dir);
    println!("cargo:rustc-link-lib=static={}", "assemble");
}
<|endoftext|>


<commit_before>extern crate vergen;

use clap::IntoApp;
use clap_generate::generate_to;
use clap_generate::generators::{Bash, Elvish, Fish, PowerShell, Zsh};
use std::{env, fs};
use vergen::{generate_cargo_keys, ConstantsFlags};

include!("src/cli.rs");

fn main() {
    // Setup the flags, toggling off the 'SEMVER_FROM_CARGO_PKG' flag
    let mut flags = ConstantsFlags::all();
    flags.toggle(ConstantsFlags::SEMVER_FROM_CARGO_PKG);

    // Generate the 'cargo:' key output
    generate_cargo_keys(flags).expect("Unable to generate the cargo keys!");

    // let outdir = env::var_os("OUT_DIR").unwrap();
    let profile = env::var("PROFILE").unwrap();
    let completionsdir = format!("target/{}/completions", profile);
    fs::create_dir(&completionsdir).unwrap();
    println!("CoDi {}", &completionsdir);
    let mut app = Cli::into_app();
    let bin_name = "casile";
    generate_to::<Bash, _, _>(&mut app, bin_name, &completionsdir);
    generate_to::<Elvish, _, _>(&mut app, bin_name, &completionsdir);
    generate_to::<Fish, _, _>(&mut app, bin_name, &completionsdir);
    generate_to::<PowerShell, _, _>(&mut app, bin_name, &completionsdir);
    generate_to::<Zsh, _, _>(&mut app, bin_name, &completionsdir);
}
<commit_msg>fix(build): Don't fail on subsequent rebuilds<commit_after>extern crate vergen;

use clap::IntoApp;
use clap_generate::generate_to;
use clap_generate::generators::{Bash, Elvish, Fish, PowerShell, Zsh};
use std::{env, fs};
use vergen::{generate_cargo_keys, ConstantsFlags};

include!("src/cli.rs");

fn main() {
    // Setup the flags, toggling off the 'SEMVER_FROM_CARGO_PKG' flag
    let mut flags = ConstantsFlags::all();
    flags.toggle(ConstantsFlags::SEMVER_FROM_CARGO_PKG);

    // Generate the 'cargo:' key output
    generate_cargo_keys(flags).expect("Unable to generate the cargo keys!");

    // let outdir = env::var_os("OUT_DIR").unwrap();
    let profile = env::var("PROFILE").unwrap();
    let completionsdir = format!("target/{}/completions", profile);
    fs::create_dir_all(&completionsdir).unwrap();
    let mut app = Cli::into_app();
    let bin_name = "casile";
    generate_to::<Bash, _, _>(&mut app, bin_name, &completionsdir);
    generate_to::<Elvish, _, _>(&mut app, bin_name, &completionsdir);
    generate_to::<Fish, _, _>(&mut app, bin_name, &completionsdir);
    generate_to::<PowerShell, _, _>(&mut app, bin_name, &completionsdir);
    generate_to::<Zsh, _, _>(&mut app, bin_name, &completionsdir);
}
<|endoftext|>


<commit_before>use std::io::{self, Write};
use std::fmt::Display;
use std::fs::{File, create_dir_all, set_permissions, metadata};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{symlink, MetadataExt, PermissionsExt};
use std::path::Path;
use std::collections::{BTreeMap, BTreeSet};

use cronparse::Limited;
use cronparse::crontab::{CrontabEntry, SystemCrontabEntry, UserCrontabEntry};
use cronparse::schedule::{Schedule, Period, Calendar};
use cronparse::interval::Interval;

use users::{get_user_by_name, get_user_by_uid};

use super::{REBOOT_FILE, PACKAGE, LIB_DIR};

pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String>, path: &Path, dstdir: &Path) -> io::Result<()> {
    use cronparse::crontab::CrontabEntry::*;

    info!("generating units for {}: \"{}\", {:?}", path.display(), entry, env);

    let owner = try!(metadata(path)).uid();

    let mut persistent = env.get("PERSISTENT").and_then(|v| match &**v {
        "yes" | "true" | "1" => Some(true),
        "auto" | "" => None,
        _ => Some(false)
    }).unwrap_or_else(|| match entry {
        Anacron(_) | User(UserCrontabEntry { sched: Schedule::Period(_), .. }) | System(SystemCrontabEntry { sched: Schedule::Period(_), .. }) => true,
        _ => false
    });

    let batch = env.get("BATCH").map(|v| match &**v {
        "yes" | "true" | "1" => true,
        _ => false
    }).unwrap_or(false);

    let random_delay = env.get("RANDOM_DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(1);
    let mut delay = env.get("DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
    let hour = env.get("START_HOURS_RANGE").and_then(|v| v.splitn(1, '-').next().and_then(|v| v.parse::<u64>().ok())).unwrap_or(0);
    let shell = env.get("SHELL").map(|v| &**v).unwrap_or("/bin/sh");
    let daemon_reload = metadata(REBOOT_FILE).map(|m| m.is_file()).unwrap_or(false);

    let schedule = entry.period().and_then(|period| match *period {
        Period::Reboot => {
            persistent = false;
            if delay == 0 {
                delay = 1;
            }
            None
        },
        Period::Minutely => {
            persistent = false;
            Some("@minutely".to_owned())
        },
        Period::Hourly => {
            if delay == 0 {
                Some("@hourly".to_owned())
            } else {
                Some(format!("*-*-* *:{}:0", delay))
            }
        },
        Period::Midnight => {
            if delay == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* 0:{}:0", delay))
            }
        },
        Period::Daily => {
            if delay == 0 && hour == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Weekly => {
            if delay == 0 && hour == 0 {
                Some("@weekly".to_owned())
            } else {
                Some(format!("Mon *-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Monthly => {
            if delay == 0 && hour == 0 {
                Some("@monthly".to_owned())
            } else {
                Some(format!("*-*-1 {}:{}:0", hour, delay))
            }
        },
        Period::Quaterly => {
            if delay == 0 && hour == 0 {
                Some("@quaterly".to_owned())
            } else {
                Some(format!("*-1,4,7,10-1 {}:{}:0", hour, delay))
            }
        },
        Period::Biannually => {
            if delay == 0 && hour == 0 {
                Some("@semi-annually".to_owned())
            } else {
                Some(format!("*-1,7-1 {}:{}:0", hour, delay))
            }
        },
        Period::Yearly => {
            if delay == 0 && hour == 0 {
                Some("@yearly".to_owned())
            } else {
                Some(format!("*-1-1 {}:{}:0", hour, delay))
            }
        },
        Period::Days(days) => {
            // workaround for anacrontab
            if days > 31 {
                Some(format!("*-1/{}-1 {}:{}:0", days / 30, hour, delay))
            } else {
                Some(format!("*-*-1/{} {}:{}:0", days, hour, delay))
            }
        },
    }).or_else(|| entry.calendar().and_then(|cal| {
        let Calendar {
            ref dows,
            ref days,
            ref mons,
            ref hrs,
            ref mins
        } = *cal;

        Some(format!("{} *-{}-{} {}:{}:00",
                     linearize(&**dows, ""),
                     linearize(&**mons, "*"),
                     linearize(&**days, "*"),
                     linearize(&**hrs, "*"),
                     linearize(&**mins, "*")))
    }));

    if daemon_reload && schedule.is_none() {
        warn!("skipping job from {}: \"{}\"", path.display(), entry);
        return Ok(());
    }

    if let Some(cmd) = entry.command() {

        // make sure we know the user
        let user = try!(entry.user().and_then(get_user_by_name).or_else(|| get_user_by_uid(owner))
            .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown user")));

        // generate unique cron job id
        let mut md5ctx = ::md5::Context::new();
        md5ctx.consume(path.as_os_str().as_bytes());
        if let Some(ref schedule) = schedule {
            md5ctx.consume(schedule.as_bytes());
        }
        md5ctx.consume(cmd.as_bytes());
        let md5hex = tohex(&md5ctx.compute());

        // create service and timer unit names
        let service_unit_name = format!("cron-{}.service", md5hex);
        let timer_unit_name = format!("cron-{}.timer", md5hex);

        // unit paths
        let service_unit_path = dstdir.join(&service_unit_name);
        let timer_unit_path = dstdir.join(&timer_unit_name);

        // make sure cron.target.wants dir exists
        let cron_target_wants_path = dstdir.join("cron.target.wants");
        try!(create_dir_all(&cron_target_wants_path));

        // process command in case it should be put into script
        let command = if metadata(cmd).map(|m| m.is_file()).unwrap_or(false) {
            cmd.to_owned()
        } else {
            let script_command_path = dstdir.join(format!("cron-{}.sh", md5hex));

            debug!("generating script {:?} from {:?}", script_command_path, path);
            {
                let mut script_command_file = try!(File::create(&script_command_path));
                try!(writeln!(script_command_file, "#!{}", shell));
                try!(writeln!(script_command_file, "{}", cmd));
            }

            let mut perms = try!(metadata(&script_command_path)).permissions();
            perms.set_mode(0o755);
            try!(set_permissions(&script_command_path, perms));
            script_command_path.to_str().unwrap().to_owned()
        };

        debug!("generating service {:?} from {:?}", service_unit_path, path);
        {
            let mut service_unit_file = try!(File::create(service_unit_path));

            try!(writeln!(service_unit_file, r###"[Unit]
Description=[Cron] "{entry}"
Documentation=man:systemd-crontab-generator(8)
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}"###,
                entry = entry,
                source_crontab_path = path.display(),
                ));

            if env.contains_key("MAILTO") {
                try!(writeln!(service_unit_file, "OnFailure=cron-failure@%i.service"));
            }

            if user.uid != 0 {
                try!(writeln!(service_unit_file, "Requires=systemd-user-sessions.service"));
                if !user.home_dir.is_empty() {
                    try!(writeln!(service_unit_file, "RequiresMountsFor={}", user.home_dir));
                }
            }

            try!(writeln!(service_unit_file, r###"
[Service]
Type=oneshot
IgnoreSIGPIPE=false
ExecStart={command}"###,
                command = command,
                ));

            if schedule.is_some() && delay > 0 {
                try!(writeln!(service_unit_file, "ExecStartPre=-{}/{}/boot-delay {}", LIB_DIR, PACKAGE, delay));
            }

            if user.uid != 0 {
                try!(writeln!(service_unit_file, "User={}", user.name));
            }

            if let Some(group) = entry.group() {
                try!(writeln!(service_unit_file, "Group={}", group));
            }
            if batch {
                try!(writeln!(service_unit_file, "CPUSchedulingPolicy=idle"));
                try!(writeln!(service_unit_file, "IOSchedulingClass=idle"));
            }

            if !env.is_empty() {
                try!(write!(service_unit_file, "Environment="));
                for (name, value) in env.iter() {
                    try!(write!(service_unit_file, r#""{}={}" "#, name, value));
                }
                try!(write!(service_unit_file, "\n"));
            }
        }

        debug!("generating timer {:?} from {:?}", timer_unit_path, path);
        {
            let mut timer_unit_file = try!(File::create(&timer_unit_path));

            try!(writeln!(timer_unit_file, r###"[Unit]
Description=[Timer] "{entry}"
Documentation=man:systemd-crontab-generator(8)
PartOf=cron.target
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}

[Timer]
Unit={service_unit_name}"###,
                entry = entry,
                source_crontab_path = path.display(),
                service_unit_name = service_unit_name,
                ));

            if cfg![feature="persistent"] {
                try!(writeln!(timer_unit_file, "Persistent={}", persistent));
            }

            if let Some(schedule) = schedule {
                try!(writeln!(timer_unit_file, "OnCalendar={}", schedule));
            } else {
                try!(writeln!(timer_unit_file, "OnBootSec={}m", delay));
            }

            if random_delay != 1 {
                try!(writeln!(timer_unit_file, "AccuracySec={}m", random_delay));
            }
        }
        try!(symlink(timer_unit_path, cron_target_wants_path.join(timer_unit_name)));
    }

    Ok(())
}

fn linearize<T: Limited + Display>(input: &[Interval<T>], star: &str) -> String {
    if input.len() == 1 && input[0] == Interval::Full(1) {
        star.to_owned()
    } else {
        let mut output = String::new();
        for part in input.iter().flat_map(|v| v.iter()).collect::<BTreeSet<_>>().iter() {
            output.push_str(&*part.to_string());
            output.push(',');
        }
        output.pop();
        output
    }
}

fn tohex(input: &[u8]) -> String {
    #[inline]
    fn hex(d: u8) -> char {
        match d {
            0...9 => (d + 0x30) as char,
            10...15 => (d + 0x57) as char,
            _ => unreachable!("unexpected value: {}", d)
        }
    }

    let mut buf = String::with_capacity(32);
    for b in input.into_iter() {
        buf.push(hex(b >> 4));
        buf.push(hex(b & 0xf));
    }
    buf
}

<commit_msg>simplify environment generation<commit_after>use std::io::{self, Write};
use std::fmt::Display;
use std::fs::{File, create_dir_all, set_permissions, metadata};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{symlink, MetadataExt, PermissionsExt};
use std::path::Path;
use std::collections::{BTreeMap, BTreeSet};

use cronparse::Limited;
use cronparse::crontab::{CrontabEntry, SystemCrontabEntry, UserCrontabEntry};
use cronparse::schedule::{Schedule, Period, Calendar};
use cronparse::interval::Interval;

use users::{get_user_by_name, get_user_by_uid};

use super::{REBOOT_FILE, PACKAGE, LIB_DIR};

pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String>, path: &Path, dstdir: &Path) -> io::Result<()> {
    use cronparse::crontab::CrontabEntry::*;

    info!("generating units for {}: \"{}\", {:?}", path.display(), entry, env);

    let owner = try!(metadata(path)).uid();

    let mut persistent = env.get("PERSISTENT").and_then(|v| match &**v {
        "yes" | "true" | "1" => Some(true),
        "auto" | "" => None,
        _ => Some(false)
    }).unwrap_or_else(|| match entry {
        Anacron(_) | User(UserCrontabEntry { sched: Schedule::Period(_), .. }) | System(SystemCrontabEntry { sched: Schedule::Period(_), .. }) => true,
        _ => false
    });

    let batch = env.get("BATCH").map(|v| match &**v {
        "yes" | "true" | "1" => true,
        _ => false
    }).unwrap_or(false);

    let random_delay = env.get("RANDOM_DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(1);
    let mut delay = env.get("DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
    let hour = env.get("START_HOURS_RANGE").and_then(|v| v.splitn(1, '-').next().and_then(|v| v.parse::<u64>().ok())).unwrap_or(0);
    let shell = env.get("SHELL").map(|v| &**v).unwrap_or("/bin/sh");
    let daemon_reload = metadata(REBOOT_FILE).map(|m| m.is_file()).unwrap_or(false);

    let schedule = entry.period().and_then(|period| match *period {
        Period::Reboot => {
            persistent = false;
            if delay == 0 {
                delay = 1;
            }
            None
        },
        Period::Minutely => {
            persistent = false;
            Some("@minutely".to_owned())
        },
        Period::Hourly => {
            if delay == 0 {
                Some("@hourly".to_owned())
            } else {
                Some(format!("*-*-* *:{}:0", delay))
            }
        },
        Period::Midnight => {
            if delay == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* 0:{}:0", delay))
            }
        },
        Period::Daily => {
            if delay == 0 && hour == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Weekly => {
            if delay == 0 && hour == 0 {
                Some("@weekly".to_owned())
            } else {
                Some(format!("Mon *-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Monthly => {
            if delay == 0 && hour == 0 {
                Some("@monthly".to_owned())
            } else {
                Some(format!("*-*-1 {}:{}:0", hour, delay))
            }
        },
        Period::Quaterly => {
            if delay == 0 && hour == 0 {
                Some("@quaterly".to_owned())
            } else {
                Some(format!("*-1,4,7,10-1 {}:{}:0", hour, delay))
            }
        },
        Period::Biannually => {
            if delay == 0 && hour == 0 {
                Some("@semi-annually".to_owned())
            } else {
                Some(format!("*-1,7-1 {}:{}:0", hour, delay))
            }
        },
        Period::Yearly => {
            if delay == 0 && hour == 0 {
                Some("@yearly".to_owned())
            } else {
                Some(format!("*-1-1 {}:{}:0", hour, delay))
            }
        },
        Period::Days(days) => {
            // workaround for anacrontab
            if days > 31 {
                Some(format!("*-1/{}-1 {}:{}:0", days / 30, hour, delay))
            } else {
                Some(format!("*-*-1/{} {}:{}:0", days, hour, delay))
            }
        },
    }).or_else(|| entry.calendar().and_then(|cal| {
        let Calendar {
            ref dows,
            ref days,
            ref mons,
            ref hrs,
            ref mins
        } = *cal;

        Some(format!("{} *-{}-{} {}:{}:00",
                     linearize(&**dows, ""),
                     linearize(&**mons, "*"),
                     linearize(&**days, "*"),
                     linearize(&**hrs, "*"),
                     linearize(&**mins, "*")))
    }));

    if daemon_reload && schedule.is_none() {
        warn!("skipping job from {}: \"{}\"", path.display(), entry);
        return Ok(());
    }

    if let Some(cmd) = entry.command() {

        // make sure we know the user
        let user = try!(entry.user().and_then(get_user_by_name).or_else(|| get_user_by_uid(owner))
            .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown user")));

        // generate unique cron job id
        let mut md5ctx = ::md5::Context::new();
        md5ctx.consume(path.as_os_str().as_bytes());
        if let Some(ref schedule) = schedule {
            md5ctx.consume(schedule.as_bytes());
        }
        md5ctx.consume(cmd.as_bytes());
        let md5hex = tohex(&md5ctx.compute());

        // create service and timer unit names
        let service_unit_name = format!("cron-{}.service", md5hex);
        let timer_unit_name = format!("cron-{}.timer", md5hex);

        // unit paths
        let service_unit_path = dstdir.join(&service_unit_name);
        let timer_unit_path = dstdir.join(&timer_unit_name);

        // make sure cron.target.wants dir exists
        let cron_target_wants_path = dstdir.join("cron.target.wants");
        try!(create_dir_all(&cron_target_wants_path));

        // process command in case it should be put into script
        let command = if metadata(cmd).map(|m| m.is_file()).unwrap_or(false) {
            cmd.to_owned()
        } else {
            let script_command_path = dstdir.join(format!("cron-{}.sh", md5hex));

            debug!("generating script {:?} from {:?}", script_command_path, path);
            {
                let mut script_command_file = try!(File::create(&script_command_path));
                try!(writeln!(script_command_file, "#!{}", shell));
                try!(writeln!(script_command_file, "{}", cmd));
            }

            let mut perms = try!(metadata(&script_command_path)).permissions();
            perms.set_mode(0o755);
            try!(set_permissions(&script_command_path, perms));
            script_command_path.to_str().unwrap().to_owned()
        };

        debug!("generating service {:?} from {:?}", service_unit_path, path);
        {
            let mut service_unit_file = try!(File::create(service_unit_path));

            try!(writeln!(service_unit_file, r###"[Unit]
Description=[Cron] "{entry}"
Documentation=man:systemd-crontab-generator(8)
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}"###,
                entry = entry,
                source_crontab_path = path.display(),
                ));

            if env.contains_key("MAILTO") {
                try!(writeln!(service_unit_file, "OnFailure=cron-failure@%i.service"));
            }

            if user.uid != 0 {
                try!(writeln!(service_unit_file, "Requires=systemd-user-sessions.service"));
                if !user.home_dir.is_empty() {
                    try!(writeln!(service_unit_file, "RequiresMountsFor={}", user.home_dir));
                }
            }

            try!(writeln!(service_unit_file, r###"
[Service]
Type=oneshot
IgnoreSIGPIPE=false
ExecStart={command}"###,
                command = command,
                ));

            if schedule.is_some() && delay > 0 {
                try!(writeln!(service_unit_file, "ExecStartPre=-{}/{}/boot-delay {}", LIB_DIR, PACKAGE, delay));
            }

            if user.uid != 0 {
                try!(writeln!(service_unit_file, "User={}", user.name));
            }

            if let Some(group) = entry.group() {
                try!(writeln!(service_unit_file, "Group={}", group));
            }
            if batch {
                try!(writeln!(service_unit_file, "CPUSchedulingPolicy=idle"));
                try!(writeln!(service_unit_file, "IOSchedulingClass=idle"));
            }

            if !env.is_empty() {
                for (name, value) in env.iter() {
                    try!(writeln!(service_unit_file, r#"Environment="{}={}""#, name, value));
                }
            }
        }

        debug!("generating timer {:?} from {:?}", timer_unit_path, path);
        {
            let mut timer_unit_file = try!(File::create(&timer_unit_path));

            try!(writeln!(timer_unit_file, r###"[Unit]
Description=[Timer] "{entry}"
Documentation=man:systemd-crontab-generator(8)
PartOf=cron.target
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}

[Timer]
Unit={service_unit_name}"###,
                entry = entry,
                source_crontab_path = path.display(),
                service_unit_name = service_unit_name,
                ));

            if cfg![feature="persistent"] {
                try!(writeln!(timer_unit_file, "Persistent={}", persistent));
            }

            if let Some(schedule) = schedule {
                try!(writeln!(timer_unit_file, "OnCalendar={}", schedule));
            } else {
                try!(writeln!(timer_unit_file, "OnBootSec={}m", delay));
            }

            if random_delay != 1 {
                try!(writeln!(timer_unit_file, "AccuracySec={}m", random_delay));
            }
        }
        try!(symlink(timer_unit_path, cron_target_wants_path.join(timer_unit_name)));
    }

    Ok(())
}

fn linearize<T: Limited + Display>(input: &[Interval<T>], star: &str) -> String {
    if input.len() == 1 && input[0] == Interval::Full(1) {
        star.to_owned()
    } else {
        let mut output = String::new();
        for part in input.iter().flat_map(|v| v.iter()).collect::<BTreeSet<_>>().iter() {
            output.push_str(&*part.to_string());
            output.push(',');
        }
        output.pop();
        output
    }
}

fn tohex(input: &[u8]) -> String {
    #[inline]
    fn hex(d: u8) -> char {
        match d {
            0...9 => (d + 0x30) as char,
            10...15 => (d + 0x57) as char,
            _ => unreachable!("unexpected value: {}", d)
        }
    }

    let mut buf = String::with_capacity(32);
    for b in input.into_iter() {
        buf.push(hex(b >> 4));
        buf.push(hex(b & 0xf));
    }
    buf
}

<|endoftext|>


<commit_before>use glob::glob;
use hyper::client::HttpConnector;
use hyper::rt::{self, Future, Stream};
use hyper::{header, Body, Chunk, Client, Request, Response};
use hyper_tls::HttpsConnector;
use serde_json;
use statics::TEMP_DIRNAME;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::sync::mpsc;
use structs::{CliError, Config, Member, PublicKey, RateLimit, Repo, Sector, SectorGroup, Team};

pub struct GithubClient {
    pub conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { conf: config.clone() }
    }

    fn get_cache_path(url: &str) -> std::path::PathBuf {
        let mut path = std::env::temp_dir();
        path.push(TEMP_DIRNAME);
        path.push(url);
        path
    }

    fn load_contents_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), CliError> {
        let path = Self::get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut contents = String::new();
        f.read_to_string(&mut contents)?;
        Ok((metadata, contents))
    }

    fn store_contents_to_cache(&self, url: &str, contents: &str) -> Result<(), CliError> {
        let path = Self::get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap_or(std::path::Path::new("/")))?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(contents.as_bytes())?;
        Ok(())
    }

    fn get_contents_from_url(&self, url: &str) -> Result<String, CliError> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_contents_from_url_page(url, page)?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let contents = serde_json::ser::to_string(&all_contents)?;
        self.store_contents_to_cache(url, &contents)?;
        Ok(contents)
    }

    fn build_request(&self, url: &str) -> Request<Body> {
        let token = String::from("token ") + &self.conf.token;
        Request::get(url).header(header::AUTHORIZATION, token.as_str())
                         .header(header::USER_AGENT, "sectora")
                         .body(Body::empty())
                         .expect("Request")
    }

    fn build_page_request(&self, url: &str, page: u64) -> Request<Body> {
        let sep = if url.contains("?") { "&" } else { "?" };
        let url_p = format!("{}{}page={}", url, sep, page);
        self.build_request(&url_p)
    }

    fn build_https_client(&self) -> Client<HttpsConnector<HttpConnector>> {
        let https = HttpsConnector::new(4).expect("HttpsConnector");
        Client::builder().build(https)
    }

    fn run_request(&self, req: Request<Body>) -> Result<Chunk, CliError> {
        let client = self.build_https_client();
        let concat_response = |res: Response<Body>| res.into_body().concat2();
        let (tx, rx) = mpsc::sync_channel(1);
        let etx = tx.clone();
        let send_response = move |r| tx.send(Ok(r)).expect("send response");
        let send_err = move |e| etx.send(Err(CliError::from(e))).expect("send err");
        rt::run(rt::lazy(move || {
                             client.request(req)
                                   .and_then(concat_response)
                                   .map(send_response)
                                   .map_err(send_err)
                         }));
        rx.recv().expect("recv response")
    }

    fn get_contents_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::Value>, CliError> {
        let req = self.build_page_request(url, page);
        self.run_request(req).and_then(|body| serde_json::from_slice(&body).map_err(CliError::from))
    }

    fn get_contents(&self, url: &str) -> Result<String, CliError> {
        match self.load_contents_from_cache(url) {
            Ok((metadata, cache_contents)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_contents_from_url(url) {
                            Ok(contents_from_url) => Ok(contents_from_url),
                            Err(_) => Ok(cache_contents),
                        }
                    } else {
                        Ok(cache_contents)
                    }
                }
                Err(_) => Ok(cache_contents),
            },
            Err(_) => self.get_contents_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn print_user_public_key(&self, user: &str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user: &str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let contents = self.get_contents(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&contents)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<String>>().join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, CliError> {
        let sectors = self.get_sectors()?;
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub fn get_sectors(&self) -> Result<Vec<SectorGroup>, CliError> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result()?;
        sectors.append(&mut self.get_repos_result()?);
        Ok(sectors)
    }

    fn get_teams_result(&self) -> Result<Vec<SectorGroup>, CliError> {
        let gh_teams = self.get_team_map()?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let &Some(gh_team) = &gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid.clone(),
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id)?, });
            }
        }
        Ok(teams)
    }

    fn get_team_map(&self) -> Result<HashMap<String, Team>, CliError> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, self.conf.org);
        let contents = self.get_contents(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&contents)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_repos_result(&self) -> Result<Vec<SectorGroup>, CliError> {
        let gh_repos = self.get_repo_map()?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let &Some(gh_repo) = &gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid.clone(),
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&gh_repo.name)?, });
            }
        }
        Ok(repos)
    }

    fn get_repo_map(&self) -> Result<HashMap<String, Repo>, CliError> {
        let url = format!("{}/orgs/{}/repos", self.conf.endpoint, self.conf.org);
        let contents = self.get_contents(&url)?;
        let repos = serde_json::from_str::<Vec<Repo>>(&contents)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_repo_collaborators(&self, repo_name: &str) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, self.conf.org, repo_name);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_rate_limit(&self) -> Result<RateLimit, CliError> {
        let url = format!("{}/rate_limit", self.conf.endpoint);
        let req = self.build_request(&url);
        self.run_request(req).and_then(|body| serde_json::from_slice(&body).map_err(CliError::from))
    }

    #[allow(dead_code)]
    pub fn print_rate_limit(&self) -> Result<(), CliError> {
        let rate_limit = self.get_rate_limit()?;
        println!("{:?}", rate_limit);
        Ok(())
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push(TEMP_DIRNAME);
        path.push("**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<commit_msg>Change build_https_client to an associated function<commit_after>use glob::glob;
use hyper::client::HttpConnector;
use hyper::rt::{self, Future, Stream};
use hyper::{header, Body, Chunk, Client, Request, Response};
use hyper_tls::HttpsConnector;
use serde_json;
use statics::TEMP_DIRNAME;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::sync::mpsc;
use structs::{CliError, Config, Member, PublicKey, RateLimit, Repo, Sector, SectorGroup, Team};

pub struct GithubClient {
    pub conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { conf: config.clone() }
    }

    fn get_cache_path(url: &str) -> std::path::PathBuf {
        let mut path = std::env::temp_dir();
        path.push(TEMP_DIRNAME);
        path.push(url);
        path
    }

    fn load_contents_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), CliError> {
        let path = Self::get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut contents = String::new();
        f.read_to_string(&mut contents)?;
        Ok((metadata, contents))
    }

    fn store_contents_to_cache(&self, url: &str, contents: &str) -> Result<(), CliError> {
        let path = Self::get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap_or(std::path::Path::new("/")))?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(contents.as_bytes())?;
        Ok(())
    }

    fn get_contents_from_url(&self, url: &str) -> Result<String, CliError> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_contents_from_url_page(url, page)?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let contents = serde_json::ser::to_string(&all_contents)?;
        self.store_contents_to_cache(url, &contents)?;
        Ok(contents)
    }

    fn build_request(&self, url: &str) -> Request<Body> {
        let token = String::from("token ") + &self.conf.token;
        Request::get(url).header(header::AUTHORIZATION, token.as_str())
                         .header(header::USER_AGENT, "sectora")
                         .body(Body::empty())
                         .expect("Request")
    }

    fn build_page_request(&self, url: &str, page: u64) -> Request<Body> {
        let sep = if url.contains("?") { "&" } else { "?" };
        let url_p = format!("{}{}page={}", url, sep, page);
        self.build_request(&url_p)
    }

    fn build_https_client() -> Client<HttpsConnector<HttpConnector>> {
        let https = HttpsConnector::new(4).expect("HttpsConnector");
        Client::builder().build(https)
    }

    fn run_request(&self, req: Request<Body>) -> Result<Chunk, CliError> {
        let client = Self::build_https_client();
        let concat_response = |res: Response<Body>| res.into_body().concat2();
        let (tx, rx) = mpsc::sync_channel(1);
        let etx = tx.clone();
        let send_response = move |r| tx.send(Ok(r)).expect("send response");
        let send_err = move |e| etx.send(Err(CliError::from(e))).expect("send err");
        rt::run(rt::lazy(move || {
                             client.request(req)
                                   .and_then(concat_response)
                                   .map(send_response)
                                   .map_err(send_err)
                         }));
        rx.recv().expect("recv response")
    }

    fn get_contents_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::Value>, CliError> {
        let req = self.build_page_request(url, page);
        self.run_request(req).and_then(|body| serde_json::from_slice(&body).map_err(CliError::from))
    }

    fn get_contents(&self, url: &str) -> Result<String, CliError> {
        match self.load_contents_from_cache(url) {
            Ok((metadata, cache_contents)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_contents_from_url(url) {
                            Ok(contents_from_url) => Ok(contents_from_url),
                            Err(_) => Ok(cache_contents),
                        }
                    } else {
                        Ok(cache_contents)
                    }
                }
                Err(_) => Ok(cache_contents),
            },
            Err(_) => self.get_contents_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn print_user_public_key(&self, user: &str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user: &str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let contents = self.get_contents(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&contents)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<String>>().join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, CliError> {
        let sectors = self.get_sectors()?;
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub fn get_sectors(&self) -> Result<Vec<SectorGroup>, CliError> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result()?;
        sectors.append(&mut self.get_repos_result()?);
        Ok(sectors)
    }

    fn get_teams_result(&self) -> Result<Vec<SectorGroup>, CliError> {
        let gh_teams = self.get_team_map()?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let &Some(gh_team) = &gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid.clone(),
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id)?, });
            }
        }
        Ok(teams)
    }

    fn get_team_map(&self) -> Result<HashMap<String, Team>, CliError> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, self.conf.org);
        let contents = self.get_contents(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&contents)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_repos_result(&self) -> Result<Vec<SectorGroup>, CliError> {
        let gh_repos = self.get_repo_map()?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let &Some(gh_repo) = &gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid.clone(),
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&gh_repo.name)?, });
            }
        }
        Ok(repos)
    }

    fn get_repo_map(&self) -> Result<HashMap<String, Repo>, CliError> {
        let url = format!("{}/orgs/{}/repos", self.conf.endpoint, self.conf.org);
        let contents = self.get_contents(&url)?;
        let repos = serde_json::from_str::<Vec<Repo>>(&contents)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_repo_collaborators(&self, repo_name: &str) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, self.conf.org, repo_name);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_rate_limit(&self) -> Result<RateLimit, CliError> {
        let url = format!("{}/rate_limit", self.conf.endpoint);
        let req = self.build_request(&url);
        self.run_request(req).and_then(|body| serde_json::from_slice(&body).map_err(CliError::from))
    }

    #[allow(dead_code)]
    pub fn print_rate_limit(&self) -> Result<(), CliError> {
        let rate_limit = self.get_rate_limit()?;
        println!("{:?}", rate_limit);
        Ok(())
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push(TEMP_DIRNAME);
        path.push("**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<|endoftext|>