<commit_before>use std::iter;
use byteorder::{BigEndian, ByteOrder};

pub const K: [u64; 80] = [
    0x428a2f98d728ae22,
    0x7137449123ef65cd,
    0xb5c0fbcfec4d3b2f,
    0xe9b5dba58189dbbc,
    0x3956c25bf348b538,
    0x59f111f1b605d019,
    0x923f82a4af194f9b,
    0xab1c5ed5da6d8118,
    0xd807aa98a3030242,
    0x12835b0145706fbe,
    0x243185be4ee4b28c,
    0x550c7dc3d5ffb4e2,
    0x72be5d74f27b896f,
    0x80deb1fe3b1696b1,
    0x9bdc06a725c71235,
    0xc19bf174cf692694,
    0xe49b69c19ef14ad2,
    0xefbe4786384f25e3,
    0x0fc19dc68b8cd5b5,
    0x240ca1cc77ac9c65,
    0x2de92c6f592b0275,
    0x4a7484aa6ea6e483,
    0x5cb0a9dcbd41fbd4,
    0x76f988da831153b5,
    0x983e5152ee66dfab,
    0xa831c66d2db43210,
    0xb00327c898fb213f,
    0xbf597fc7beef0ee4,
    0xc6e00bf33da88fc2,
    0xd5a79147930aa725,
    0x06ca6351e003826f,
    0x142929670a0e6e70,
    0x27b70a8546d22ffc,
    0x2e1b21385c26c926,
    0x4d2c6dfc5ac42aed,
    0x53380d139d95b3df,
    0x650a73548baf63de,
    0x766a0abb3c77b2a8,
    0x81c2c92e47edaee6,
    0x92722c851482353b,
    0xa2bfe8a14cf10364,
    0xa81a664bbc423001,
    0xc24b8b70d0f89791,
    0xc76c51a30654be30,
    0xd192e819d6ef5218,
    0xd69906245565a910,
    0xf40e35855771202a,
    0x106aa07032bbd1b8,
    0x19a4c116b8d2d0c8,
    0x1e376c085141ab53,
    0x2748774cdf8eeb99,
    0x34b0bcb5e19b48a8,
    0x391c0cb3c5c95a63,
    0x4ed8aa4ae3418acb,
    0x5b9cca4f7763e373,
    0x682e6ff3d6b2b8a3,
    0x748f82ee5defb2fc,
    0x78a5636f43172f60,
    0x84c87814a1f0ab72,
    0x8cc702081a6439ec,
    0x90befffa23631e28,
    0xa4506cebde82bde9,
    0xbef9a3f7b2c67915,
    0xc67178f2e372532b,
    0xca273eceea26619c,
    0xd186b8c721c0c207,
    0xeada7dd6cde0eb1e,
    0xf57d4f7fee6ed178,
    0x06f067aa72176fba,
    0x0a637dc5a2c898a6,
    0x113f9804bef90dae,
    0x1b710b35131c471b,
    0x28db77f523047d84,
    0x32caab7b40c72493,
    0x3c9ebe0a15c9bebc,
    0x431d67c49c100d4c,
    0x4cc5d4becb3e42b6,
    0x597f299cfc657e2a,
    0x5fcb6fab3ad6faec,
    0x6c44198c4a475817,
];

pub struct SHA512 {}

impl SHA512 {
    pub fn digest(message: &[u8]) -> [u8; 64] {
        let mut sha = SHA(
            [
                0x6a09e667f3bcc908,
                0xbb67ae8584caa73b,
                0x3c6ef372fe94f82b,
                0xa54ff53a5f1d36f1,
                0x510e527fade682d1,
                0x9b05688c2b3e6c1f,
                0x1f83d9abfb41bd6b,
                0x5be0cd19137e2179,
            ],
        );
        sha.process(message);
        sha.digest()
    }
}

struct SHA([u64; 8]);

impl SHA {
    fn process(&mut self, message: &[u8]) {
        let mut message = message.to_vec();
        Self::pad(&mut message);
        let mut w = [0; 80];
        for chunk in message.chunks(128) {
            for (wt, long) in w.iter_mut().zip(chunk.chunks(8)) {
                *wt = BigEndian::read_u64(long);
            }
            for t in 16..80 {
                w[t] = Self::ssig1(w[t - 2])
                    .wrapping_add(w[t - 7])
                    .wrapping_add(Self::ssig0(w[t - 15]))
                    .wrapping_add(w[t - 16]);
            }
            let mut a = self.0[0];
            let mut b = self.0[1];
            let mut c = self.0[2];
            let mut d = self.0[3];
            let mut e = self.0[4];
            let mut f = self.0[5];
            let mut g = self.0[6];
            let mut h = self.0[7];
            for (&kt, &wt) in K.iter().zip(w.iter()) {
                let t1 = h.wrapping_add(Self::bsig1(e))
                    .wrapping_add(Self::ch(e, f, g))
                    .wrapping_add(kt)
                    .wrapping_add(wt);
                let t2 = Self::bsig0(a).wrapping_add(Self::maj(a, b, c));
                h = g;
                g = f;
                f = e;
                e = d.wrapping_add(t1);
                d = c;
                c = b;
                b = a;
                a = t1.wrapping_add(t2);
            }
            self.0[0] = self.0[0].wrapping_add(a);
            self.0[1] = self.0[1].wrapping_add(b);
            self.0[2] = self.0[2].wrapping_add(c);
            self.0[3] = self.0[3].wrapping_add(d);
            self.0[4] = self.0[4].wrapping_add(e);
            self.0[5] = self.0[5].wrapping_add(f);
            self.0[6] = self.0[6].wrapping_add(g);
            self.0[7] = self.0[7].wrapping_add(h);
        }
    }

    fn digest(self) -> [u8; 64] {
        let mut digest = [0; 64];
        for (chunk, &long) in digest.chunks_mut(8).zip(&self.0) {
            BigEndian::write_u64(chunk, long);
        }
        digest
    }
    fn ch(x: u64, y: u64, z: u64) -> u64 {
        (x & y) ^ (!x & z)
    }

    fn maj(x: u64, y: u64, z: u64) -> u64 {
        (x & y) ^ (x & z) ^ (y & z)
    }

    fn bsig0(x: u64) -> u64 {
        x.rotate_right(28) ^ x.rotate_right(34) ^ x.rotate_right(39)
    }

    fn bsig1(x: u64) -> u64 {
        x.rotate_right(14) ^ x.rotate_right(18) ^ x.rotate_right(41)
    }

    fn ssig0(x: u64) -> u64 {
        x.rotate_right(1) ^ x.rotate_right(8) ^ (x >> 7)
    }

    fn ssig1(x: u64) -> u64 {
        x.rotate_right(19) ^ x.rotate_right(61) ^ (x >> 6)
    }

    /// Only supports messages with at most 2^64 - 1 bits for now
    fn pad(bytes: &mut Vec<u8>) {
        let len = len(bytes);
        bytes.push(0x80);
        let padding = (128 + 112 - bytes.len() % 128) % 128;
        bytes.extend(iter::repeat(0).take(padding));
        bytes.extend_from_slice(&[0; 8]);
        bytes.extend_from_slice(&len);
    }
}

fn len(bytes: &[u8]) -> [u8; 8] {
    let mut len = [0; 8];
    BigEndian::write_u64(&mut len, 8 * bytes.len() as u64);
    len
}

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

    #[test]
    fn test_pad() {
        let mut message = vec![0b01100001, 0b01100010, 0b01100011, 0b01100100, 0b01100101];
        let expected = h2b(
            "6162636465800000000000000000000000000000000000000000000000000000\
             0000000000000000000000000000000000000000000000000000000000000000\
             0000000000000000000000000000000000000000000000000000000000000000\
             0000000000000000000000000000000000000000000000000000000000000028",
        );
        SHA::pad(&mut message);
        assert_eq!(expected, message);
    }

    #[test]
    fn test_digest() {
        let expected = h2b(
            "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce\
             47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
        );
        let actual = SHA512::digest(&[]);
        assert_eq!(64, actual.len());
        for (lhs, rhs) in expected.iter().zip(actual.iter()) {
            assert_eq!(lhs, rhs);
        }
    }
}
<commit_msg>sha: add SHA384<commit_after>use std::iter;
use byteorder::{BigEndian, ByteOrder};

pub const K: [u64; 80] = [
    0x428a2f98d728ae22,
    0x7137449123ef65cd,
    0xb5c0fbcfec4d3b2f,
    0xe9b5dba58189dbbc,
    0x3956c25bf348b538,
    0x59f111f1b605d019,
    0x923f82a4af194f9b,
    0xab1c5ed5da6d8118,
    0xd807aa98a3030242,
    0x12835b0145706fbe,
    0x243185be4ee4b28c,
    0x550c7dc3d5ffb4e2,
    0x72be5d74f27b896f,
    0x80deb1fe3b1696b1,
    0x9bdc06a725c71235,
    0xc19bf174cf692694,
    0xe49b69c19ef14ad2,
    0xefbe4786384f25e3,
    0x0fc19dc68b8cd5b5,
    0x240ca1cc77ac9c65,
    0x2de92c6f592b0275,
    0x4a7484aa6ea6e483,
    0x5cb0a9dcbd41fbd4,
    0x76f988da831153b5,
    0x983e5152ee66dfab,
    0xa831c66d2db43210,
    0xb00327c898fb213f,
    0xbf597fc7beef0ee4,
    0xc6e00bf33da88fc2,
    0xd5a79147930aa725,
    0x06ca6351e003826f,
    0x142929670a0e6e70,
    0x27b70a8546d22ffc,
    0x2e1b21385c26c926,
    0x4d2c6dfc5ac42aed,
    0x53380d139d95b3df,
    0x650a73548baf63de,
    0x766a0abb3c77b2a8,
    0x81c2c92e47edaee6,
    0x92722c851482353b,
    0xa2bfe8a14cf10364,
    0xa81a664bbc423001,
    0xc24b8b70d0f89791,
    0xc76c51a30654be30,
    0xd192e819d6ef5218,
    0xd69906245565a910,
    0xf40e35855771202a,
    0x106aa07032bbd1b8,
    0x19a4c116b8d2d0c8,
    0x1e376c085141ab53,
    0x2748774cdf8eeb99,
    0x34b0bcb5e19b48a8,
    0x391c0cb3c5c95a63,
    0x4ed8aa4ae3418acb,
    0x5b9cca4f7763e373,
    0x682e6ff3d6b2b8a3,
    0x748f82ee5defb2fc,
    0x78a5636f43172f60,
    0x84c87814a1f0ab72,
    0x8cc702081a6439ec,
    0x90befffa23631e28,
    0xa4506cebde82bde9,
    0xbef9a3f7b2c67915,
    0xc67178f2e372532b,
    0xca273eceea26619c,
    0xd186b8c721c0c207,
    0xeada7dd6cde0eb1e,
    0xf57d4f7fee6ed178,
    0x06f067aa72176fba,
    0x0a637dc5a2c898a6,
    0x113f9804bef90dae,
    0x1b710b35131c471b,
    0x28db77f523047d84,
    0x32caab7b40c72493,
    0x3c9ebe0a15c9bebc,
    0x431d67c49c100d4c,
    0x4cc5d4becb3e42b6,
    0x597f299cfc657e2a,
    0x5fcb6fab3ad6faec,
    0x6c44198c4a475817,
];

pub struct SHA512 {}

impl SHA512 {
    pub fn digest(message: &[u8]) -> [u8; 64] {
        Self::get_digest(message)
    }
}

impl Digest for SHA512 {
    const OUTPUT_LEN: usize = 64;
    const INITIAL_STATE: [u64; 8] = [
        0x6a09e667f3bcc908,
        0xbb67ae8584caa73b,
        0x3c6ef372fe94f82b,
        0xa54ff53a5f1d36f1,
        0x510e527fade682d1,
        0x9b05688c2b3e6c1f,
        0x1f83d9abfb41bd6b,
        0x5be0cd19137e2179,
    ];
}

pub struct SHA384 {}

impl SHA384 {
    // TODO: change the return type and/or do something else to reduce duplication
    pub fn digest(message: &[u8]) -> [u8; Self::OUTPUT_LEN] {
        let mut digest = [0; Self::OUTPUT_LEN];
        digest.copy_from_slice(&Self::get_digest(message)[..Self::OUTPUT_LEN]);
        digest
    }
}

impl Digest for SHA384 {
    const OUTPUT_LEN: usize = 48;
    const INITIAL_STATE: [u64; 8] = [
        0xcbbb9d5dc1059ed8,
        0x629a292a367cd507,
        0x9159015a3070dd17,
        0x152fecd8f70e5939,
        0x67332667ffc00b31,
        0x8eb44a8768581511,
        0xdb0c2e0d64f98fa7,
        0x47b5481dbefa4fa4,
    ];
}

trait Digest {
    const OUTPUT_LEN: usize;
    const INITIAL_STATE: [u64; 8];

    fn get_digest(message: &[u8]) -> [u8; 64] {
        let mut sha = SHA(Self::INITIAL_STATE);
        sha.process(message);
        sha.digest(Self::OUTPUT_LEN)
    }
}

struct SHA([u64; 8]);

impl SHA {
    fn process(&mut self, message: &[u8]) {
        let mut message = message.to_vec();
        Self::pad(&mut message);
        let mut w = [0; 80];
        for chunk in message.chunks(128) {
            for (wt, long) in w.iter_mut().zip(chunk.chunks(8)) {
                *wt = BigEndian::read_u64(long);
            }
            for t in 16..80 {
                w[t] = Self::ssig1(w[t - 2])
                    .wrapping_add(w[t - 7])
                    .wrapping_add(Self::ssig0(w[t - 15]))
                    .wrapping_add(w[t - 16]);
            }
            let mut a = self.0[0];
            let mut b = self.0[1];
            let mut c = self.0[2];
            let mut d = self.0[3];
            let mut e = self.0[4];
            let mut f = self.0[5];
            let mut g = self.0[6];
            let mut h = self.0[7];
            for (&kt, &wt) in K.iter().zip(w.iter()) {
                let t1 = h.wrapping_add(Self::bsig1(e))
                    .wrapping_add(Self::ch(e, f, g))
                    .wrapping_add(kt)
                    .wrapping_add(wt);
                let t2 = Self::bsig0(a).wrapping_add(Self::maj(a, b, c));
                h = g;
                g = f;
                f = e;
                e = d.wrapping_add(t1);
                d = c;
                c = b;
                b = a;
                a = t1.wrapping_add(t2);
            }
            self.0[0] = self.0[0].wrapping_add(a);
            self.0[1] = self.0[1].wrapping_add(b);
            self.0[2] = self.0[2].wrapping_add(c);
            self.0[3] = self.0[3].wrapping_add(d);
            self.0[4] = self.0[4].wrapping_add(e);
            self.0[5] = self.0[5].wrapping_add(f);
            self.0[6] = self.0[6].wrapping_add(g);
            self.0[7] = self.0[7].wrapping_add(h);
        }
    }

    fn digest(self, len: usize) -> [u8; 64] {
        let mut digest = [0; 64];
        for (chunk, &long) in digest.chunks_mut(8).zip(&self.0).take(len / 8) {
            BigEndian::write_u64(chunk, long);
        }
        digest
    }

    fn ch(x: u64, y: u64, z: u64) -> u64 {
        (x & y) ^ (!x & z)
    }

    fn maj(x: u64, y: u64, z: u64) -> u64 {
        (x & y) ^ (x & z) ^ (y & z)
    }

    fn bsig0(x: u64) -> u64 {
        x.rotate_right(28) ^ x.rotate_right(34) ^ x.rotate_right(39)
    }

    fn bsig1(x: u64) -> u64 {
        x.rotate_right(14) ^ x.rotate_right(18) ^ x.rotate_right(41)
    }

    fn ssig0(x: u64) -> u64 {
        x.rotate_right(1) ^ x.rotate_right(8) ^ (x >> 7)
    }

    fn ssig1(x: u64) -> u64 {
        x.rotate_right(19) ^ x.rotate_right(61) ^ (x >> 6)
    }

    /// Only supports messages with at most 2^64 - 1 bits for now
    fn pad(bytes: &mut Vec<u8>) {
        let len = len(bytes);
        bytes.push(0x80);
        let padding = (128 + 112 - bytes.len() % 128) % 128;
        bytes.extend(iter::repeat(0).take(padding));
        bytes.extend_from_slice(&[0; 8]);
        bytes.extend_from_slice(&len);
    }
}

fn len(bytes: &[u8]) -> [u8; 8] {
    let mut len = [0; 8];
    BigEndian::write_u64(&mut len, 8 * bytes.len() as u64);
    len
}

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

    #[test]
    fn test_pad() {
        let mut message = vec![0b01100001, 0b01100010, 0b01100011, 0b01100100, 0b01100101];
        let expected = h2b(
            "6162636465800000000000000000000000000000000000000000000000000000\
             0000000000000000000000000000000000000000000000000000000000000000\
             0000000000000000000000000000000000000000000000000000000000000000\
             0000000000000000000000000000000000000000000000000000000000000028",
        );
        SHA::pad(&mut message);
        assert_eq!(expected, message);
    }

    #[test]
    fn test_digest() {
        let mut expected = h2b(
            "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce\
             47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
        );
        let actual = SHA512::digest(&[]);
        assert_eq!(expected, actual.to_vec());

        expected = h2b(
            "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da\
             274edebfe76f65fbd51ad2f14898b95b",
        );
        let actual = SHA384::digest(&[]);
        assert_eq!(expected, actual.to_vec());
    }
}
<|endoftext|>


<commit_before>use crate::base128;
use crate::parser::{ParseError, ParseErrorKind, ParseResult};
use crate::writer::{WriteBuf, WriteResult};

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum TagClass {
    Universal = 0b00,
    Application = 0b01,
    ContextSpecific = 0b10,
    Private = 0b11,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Tag {
    value: u32,
    constructed: bool,
    class: TagClass,
}

pub(crate) const CONSTRUCTED: u32 = 0x20;

impl Tag {
    /// Parses a `Tag` from bytes and returns either the `Tag` and the
    /// remaining bytes from the input or an error.
    pub fn from_bytes(mut data: &[u8]) -> ParseResult<(Tag, &[u8])> {
        let tag = match data.first() {
            Some(&b) => u32::from(b),
            None => return Err(ParseError::new(ParseErrorKind::ShortData)),
        };
        data = &data[1..];
        let mut value = tag & 0x1f;
        let constructed = tag & CONSTRUCTED == CONSTRUCTED;

        let tag_class_bits = tag >> 6;
        let class = if tag_class_bits == TagClass::Universal as u32 {
            TagClass::Universal
        } else if tag_class_bits == TagClass::Application as u32 {
            TagClass::Application
        } else if tag_class_bits == TagClass::ContextSpecific as u32 {
            TagClass::ContextSpecific
        } else {
            assert!(tag_class_bits == TagClass::Private as u32);
            TagClass::Private
        };

        // Long form tag
        if value == 0x1f {
            let result = base128::read_base128_int(data)
                .map_err(|_| ParseError::new(ParseErrorKind::InvalidTag))?;
            // Rust 1.47 doesn't support writing `(value, data) = ...;`
            value = result.0;
            data = result.1;
            // Tags must be encoded in minimal form.
            if value < 0x1f {
                return Err(ParseError::new(ParseErrorKind::InvalidTag));
            }
        }

        Ok((
            Tag {
                value,
                constructed,
                class,
            },
            data,
        ))
    }

    pub(crate) const fn new(tag: u32, class: TagClass, constructed: bool) -> Tag {
        Tag {
            value: tag,
            constructed,
            class,
        }
    }

    /// This is `pub` for use in tests but is not considered part of the
    /// supported API.
    #[doc(hidden)]
    pub const fn primitive(tag: u32) -> Tag {
        Tag::new(tag, TagClass::Universal, false)
    }

    pub(crate) const fn constructed(tag: u32) -> Tag {
        Tag::new(tag, TagClass::Universal, true)
    }

    /// Returns the tag's representation (including tag class and constructed
    /// bits) as a `u8` if the `value` component fits in a short form
    /// (value < 31) or `None` if this is a long-form tag.
    pub fn as_u8(self) -> Option<u8> {
        if self.value >= 0x1f {
            return None;
        }
        Some(
            ((self.class as u8) << 6)
                | if self.constructed {
                    CONSTRUCTED as u8
                } else {
                    0
                }
                | (self.value as u8),
        )
    }

    pub(crate) fn write_bytes(self, dest: &mut WriteBuf) -> WriteResult {
        let mut b = ((self.class as u8) << 6)
            | if self.constructed {
                CONSTRUCTED as u8
            } else {
                0
            };
        if self.value >= 0x1f {
            b |= 0x1f;
            dest.push_byte(b)?;
            let len = base128::base128_length(self.value);
            let orig_len = dest.len();
            for _ in 0..len {
                dest.push_byte(0)?;
            }
            base128::write_base128_int(&mut dest.as_mut_slice()[orig_len..], self.value);
        } else {
            b |= self.value as u8;
            dest.push_byte(b)?;
        }

        Ok(())
    }

    pub(crate) const fn is_constructed(self) -> bool {
        self.constructed
    }
}

#[cfg(test)]
mod tests {
    use super::{Tag, TagClass, CONSTRUCTED};

    #[test]
    fn test_constructed() {
        for i in 0..31 {
            let tag = Tag::constructed(i);
            assert_eq!(tag.as_u8(), Some((CONSTRUCTED | i) as u8));
            assert!(tag.is_constructed());
        }
    }

    #[test]
    fn test_as_u8() {
        for (t, expected) in &[
            (Tag::new(5, TagClass::Application, true), Some(0x65)),
            (Tag::new(5, TagClass::Universal, false), Some(0x05)),
            (Tag::new(0x1f, TagClass::Universal, false), None),
        ] {
            assert_eq!(&t.as_u8(), expected);
        }
    }
}
<commit_msg>Cleanup integer casting in tag.rs (#321)<commit_after>use crate::base128;
use crate::parser::{ParseError, ParseErrorKind, ParseResult};
use crate::writer::{WriteBuf, WriteResult};

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum TagClass {
    Universal = 0b00,
    Application = 0b01,
    ContextSpecific = 0b10,
    Private = 0b11,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Tag {
    value: u32,
    constructed: bool,
    class: TagClass,
}

pub(crate) const CONSTRUCTED: u8 = 0x20;

impl Tag {
    /// Parses a `Tag` from bytes and returns either the `Tag` and the
    /// remaining bytes from the input or an error.
    pub fn from_bytes(mut data: &[u8]) -> ParseResult<(Tag, &[u8])> {
        let tag = match data.first() {
            Some(&b) => b,
            None => return Err(ParseError::new(ParseErrorKind::ShortData)),
        };
        data = &data[1..];
        let mut value = u32::from(tag & 0x1f);
        let constructed = tag & CONSTRUCTED == CONSTRUCTED;

        let tag_class_bits = tag >> 6;
        let class = if tag_class_bits == TagClass::Universal as u8 {
            TagClass::Universal
        } else if tag_class_bits == TagClass::Application as u8 {
            TagClass::Application
        } else if tag_class_bits == TagClass::ContextSpecific as u8 {
            TagClass::ContextSpecific
        } else {
            assert!(tag_class_bits == TagClass::Private as u8);
            TagClass::Private
        };

        // Long form tag
        if value == 0x1f {
            let result = base128::read_base128_int(data)
                .map_err(|_| ParseError::new(ParseErrorKind::InvalidTag))?;
            // Rust 1.47 doesn't support writing `(value, data) = ...;`
            value = result.0;
            data = result.1;
            // Tags must be encoded in minimal form.
            if value < 0x1f {
                return Err(ParseError::new(ParseErrorKind::InvalidTag));
            }
        }

        Ok((
            Tag {
                value,
                constructed,
                class,
            },
            data,
        ))
    }

    pub(crate) const fn new(tag: u32, class: TagClass, constructed: bool) -> Tag {
        Tag {
            value: tag,
            constructed,
            class,
        }
    }

    /// This is `pub` for use in tests but is not considered part of the
    /// supported API.
    #[doc(hidden)]
    pub const fn primitive(tag: u32) -> Tag {
        Tag::new(tag, TagClass::Universal, false)
    }

    pub(crate) const fn constructed(tag: u32) -> Tag {
        Tag::new(tag, TagClass::Universal, true)
    }

    /// Returns the tag's representation (including tag class and constructed
    /// bits) as a `u8` if the `value` component fits in a short form
    /// (value < 31) or `None` if this is a long-form tag.
    pub fn as_u8(self) -> Option<u8> {
        if self.value >= 0x1f {
            return None;
        }
        Some(
            ((self.class as u8) << 6)
                | if self.constructed { CONSTRUCTED } else { 0 }
                | (self.value as u8),
        )
    }

    pub(crate) fn write_bytes(self, dest: &mut WriteBuf) -> WriteResult {
        let mut b = ((self.class as u8) << 6) | if self.constructed { CONSTRUCTED } else { 0 };
        if self.value >= 0x1f {
            b |= 0x1f;
            dest.push_byte(b)?;
            let len = base128::base128_length(self.value);
            let orig_len = dest.len();
            for _ in 0..len {
                dest.push_byte(0)?;
            }
            base128::write_base128_int(&mut dest.as_mut_slice()[orig_len..], self.value);
        } else {
            b |= self.value as u8;
            dest.push_byte(b)?;
        }

        Ok(())
    }

    pub(crate) const fn is_constructed(self) -> bool {
        self.constructed
    }
}

#[cfg(test)]
mod tests {
    use super::{Tag, TagClass, CONSTRUCTED};

    #[test]
    fn test_constructed() {
        for i in 0..31 {
            let tag = Tag::constructed(u32::from(i));
            assert_eq!(tag.as_u8(), Some(CONSTRUCTED | i));
            assert!(tag.is_constructed());
        }
    }

    #[test]
    fn test_as_u8() {
        for (t, expected) in &[
            (Tag::new(5, TagClass::Application, true), Some(0x65)),
            (Tag::new(5, TagClass::Universal, false), Some(0x05)),
            (Tag::new(0x1f, TagClass::Universal, false), None),
        ] {
            assert_eq!(&t.as_u8(), expected);
        }
    }
}
<|endoftext|>


<commit_before>extern crate glib;

extern crate gtk;
use gtk::{Inhibit, WidgetExt};

extern crate cairo;

use std::rc::{Rc, Weak};
use std::cell::RefCell;

use std::collections::vec_deque::{VecDeque};

use std::ops::{Deref, DerefMut};

use ::media::{Context, AudioBuffer};

use super::{MediaController, MediaHandler};

pub struct AudioController {
    media_ctl: MediaController,
    drawingarea: gtk::DrawingArea,

    circ_buffer: VecDeque<AudioBuffer>,
    channels: usize,
    sample_offset: f64,
    samples_nb: f64,
}

impl AudioController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(AudioController {
            media_ctl: MediaController::new(
                builder.get_object("audio-container").unwrap(),
            ),
            drawingarea: builder.get_object("audio-drawingarea").unwrap(),

            circ_buffer: VecDeque::new(),
            channels: 0,
            sample_offset: 0f64,
            samples_nb: 0f64,
        }));

        {
            let this_ref = this.borrow();
            let this_weak = Rc::downgrade(&this);
            this_ref.drawingarea.connect_draw(move |ref drawing_area, ref cairo_ctx| {
                let this = this_weak.upgrade()
                    .expect("Main controller is no longer available for select_media");
                return this.borrow().draw(drawing_area, cairo_ctx);
            });
        }

        this
    }

    pub fn clear(&mut self) {
        self.circ_buffer.clear();
        self.channels = 0;
        self.sample_offset = 0f64;
        self.samples_nb = 0f64;
    }

    pub fn have_buffer(&mut self, buffer: AudioBuffer) {
        // Firt approximation: suppose the buffers come in ordered
        if self.sample_offset == 0f64 {
            self.sample_offset = buffer.sample_offset as f64;
            self.channels = buffer.caps.channels;
        }
        self.samples_nb += buffer.samples_nb as f64;

        self.circ_buffer.push_back(buffer);
    }

    fn draw(&self, drawing_area: &gtk::DrawingArea, cr: &cairo::Context) -> Inhibit {
        if self.circ_buffer.len() == 0 {
            return Inhibit(false);
        }

        let sample_nb = self.samples_nb.min(5_000f64);
        let sample_dyn = 1024f64;

        let allocation = drawing_area.get_allocation();
        cr.scale(
            allocation.width as f64 / sample_nb,
            allocation.height as f64 / 2f64 / sample_dyn,
        );
        cr.set_line_width(1f64);

        let mut colors = vec![(0.9f64, 0.9f64, 0.9f64), (0.9f64, 0f64, 0f64)];
        for channel in 2..self.channels {
            colors.push((0f64, 0f64, 0.2f64 * channel as f64));
        }

        for ref buffer in self.circ_buffer.iter() {
            for ref channel in buffer.channels.iter() {
                let color = colors[channel.id];
                cr.set_source_rgb(color.0, color.1, color.2);

                let mut x = buffer.sample_offset as f64 - self.sample_offset;
                let mut is_first = true;
                for &sample in channel.iter() {
                    let y = sample_dyn * (1f64 - sample);
                    if !is_first {
                        cr.line_to(x, y);
                    } else {
                        cr.move_to(x, y);
                        is_first = false;
                    }
                    x += 1f64;
                    if x > sample_nb { break; }
                }

                cr.stroke();
            }
        }

        Inhibit(false)
    }
}

impl Deref for AudioController {
	type Target = MediaController;

	fn deref(&self) -> &Self::Target {
		&self.media_ctl
	}
}

impl DerefMut for AudioController {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.media_ctl
	}
}

impl MediaHandler for AudioController {
    fn new_media(&mut self, ctx: &Context) {
        if let Some(audio_buffer) = self.circ_buffer.get(0) {
            println!("\n{:?}", audio_buffer.caps);
            self.drawingarea.queue_draw();
            self.media_ctl.show();
        }
        else {
            self.media_ctl.hide();
        }
    }
}
<commit_msg>Queue draw upon audio buffer reception (#7)<commit_after>extern crate glib;

extern crate gtk;
use gtk::{Inhibit, WidgetExt};

extern crate cairo;

use std::rc::{Rc, Weak};
use std::cell::RefCell;

use std::collections::vec_deque::{VecDeque};

use std::ops::{Deref, DerefMut};

use ::media::{Context, AudioBuffer};

use super::{MediaController, MediaHandler};

pub struct AudioController {
    media_ctl: MediaController,
    drawingarea: gtk::DrawingArea,

    circ_buffer: VecDeque<AudioBuffer>,
    channels: usize,
    sample_offset: f64,
    samples_nb: f64,
}

impl AudioController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(AudioController {
            media_ctl: MediaController::new(
                builder.get_object("audio-container").unwrap(),
            ),
            drawingarea: builder.get_object("audio-drawingarea").unwrap(),

            circ_buffer: VecDeque::new(),
            channels: 0,
            sample_offset: 0f64,
            samples_nb: 0f64,
        }));

        {
            let this_ref = this.borrow();
            let this_weak = Rc::downgrade(&this);
            this_ref.drawingarea.connect_draw(move |ref drawing_area, ref cairo_ctx| {
                let this = this_weak.upgrade()
                    .expect("Main controller is no longer available for select_media");
                return this.borrow().draw(drawing_area, cairo_ctx);
            });
        }

        this
    }

    pub fn clear(&mut self) {
        self.circ_buffer.clear();
        self.channels = 0;
        self.sample_offset = 0f64;
        self.samples_nb = 0f64;
    }

    pub fn have_buffer(&mut self, buffer: AudioBuffer) {
        // Firt approximation: suppose the buffers come in ordered
        if self.sample_offset == 0f64 {
            self.sample_offset = buffer.sample_offset as f64;
            self.channels = buffer.caps.channels;
        }
        self.samples_nb += buffer.samples_nb as f64;

        self.circ_buffer.push_back(buffer);
        self.drawingarea.queue_draw();
    }

    fn draw(&self, drawing_area: &gtk::DrawingArea, cr: &cairo::Context) -> Inhibit {
        if self.circ_buffer.len() == 0 {
            return Inhibit(false);
        }

        let sample_nb = self.samples_nb.min(32_767f64);
        let sample_dyn = 1024f64;

        let allocation = drawing_area.get_allocation();
        cr.scale(
            allocation.width as f64 / sample_nb,
            allocation.height as f64 / 2f64 / sample_dyn,
        );
        cr.set_line_width(1.5f64);

        let mut colors = vec![(0.9f64, 0.9f64, 0.9f64), (0.9f64, 0f64, 0f64)];
        for channel in 2..self.channels {
            colors.push((0f64, 0f64, 0.2f64 * channel as f64));
        }

        for ref buffer in self.circ_buffer.iter() {
            for ref channel in buffer.channels.iter() {
                let color = colors[channel.id];
                cr.set_source_rgb(color.0, color.1, color.2);

                let mut x = buffer.sample_offset as f64 - self.sample_offset;
                let mut is_first = true;
                for &sample in channel.iter() {
                    let y = sample_dyn * (1f64 - sample);
                    if !is_first {
                        cr.line_to(x, y);
                    } else {
                        cr.move_to(x, y);
                        is_first = false;
                    }
                    x += 1f64;
                    if x > sample_nb { break; }
                }

                cr.stroke();
            }
        }

        Inhibit(false)
    }
}

impl Deref for AudioController {
	type Target = MediaController;

	fn deref(&self) -> &Self::Target {
		&self.media_ctl
	}
}

impl DerefMut for AudioController {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.media_ctl
	}
}

impl MediaHandler for AudioController {
    fn new_media(&mut self, ctx: &Context) {
        if let Some(audio_buffer) = self.circ_buffer.get(0) {
            println!("\n{:?}", audio_buffer.caps);
            self.drawingarea.queue_draw();
            self.media_ctl.show();
        }
        else {
            self.media_ctl.hide();
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::HTMLDocumentBinding;
use dom::bindings::utils::{Reflectable, Reflector, Traceable};
use dom::document::{AbstractDocument, Document, HTML};
use dom::element::HTMLHeadElementTypeId;
use dom::htmlcollection::HTMLCollection;
use dom::namespace::Null;
use dom::node::{AbstractNode, ElementNodeTypeId};
use dom::window::Window;

use js::jsapi::JSTracer;
use std::str::eq_slice;
use style::TElement;

pub struct HTMLDocument {
    parent: Document
}

impl HTMLDocument {
    pub fn new_inherited(window: @mut Window) -> HTMLDocument {
        HTMLDocument {
            parent: Document::new_inherited(window, HTML)
        }
    }

    pub fn new(window: @mut Window) -> AbstractDocument {
        let document = HTMLDocument::new_inherited(window);
        Document::reflect_document(@mut document, window, HTMLDocumentBinding::Wrap)
    }
}

impl HTMLDocument {
    pub fn Images(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "img"))
    }

    pub fn Embeds(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "embed"))
    }

    pub fn Plugins(&self) -> @mut HTMLCollection {
        self.Embeds()
    }

    pub fn Links(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem|
            (eq_slice(elem.tag_name, "a") || eq_slice(elem.tag_name, "area"))
            && elem.get_attr(Null, "href").is_some())
    }

    pub fn Forms(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "form"))
    }

    pub fn Scripts(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "script"))
    }

    pub fn Anchors(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem|
            eq_slice(elem.tag_name, "a") && elem.get_attr(Null, "name").is_some())
    }

    pub fn Applets(&self) -> @mut HTMLCollection {
        // FIXME: This should be return OBJECT elements containing applets.
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "applet"))
    }
}

impl Reflectable for HTMLDocument {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.parent.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.parent.mut_reflector()
    }
}

impl Traceable for HTMLDocument {
    fn trace(&self, tracer: *mut JSTracer) {
        self.parent.trace(tracer);
    }
}
<commit_msg>auto merge of #1481 : Ms2ger/servo/unused-imports-2, r=metajack<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::HTMLDocumentBinding;
use dom::bindings::utils::{Reflectable, Reflector, Traceable};
use dom::document::{AbstractDocument, Document, HTML};
use dom::htmlcollection::HTMLCollection;
use dom::namespace::Null;
use dom::window::Window;

use js::jsapi::JSTracer;
use std::str::eq_slice;
use style::TElement;

pub struct HTMLDocument {
    parent: Document
}

impl HTMLDocument {
    pub fn new_inherited(window: @mut Window) -> HTMLDocument {
        HTMLDocument {
            parent: Document::new_inherited(window, HTML)
        }
    }

    pub fn new(window: @mut Window) -> AbstractDocument {
        let document = HTMLDocument::new_inherited(window);
        Document::reflect_document(@mut document, window, HTMLDocumentBinding::Wrap)
    }
}

impl HTMLDocument {
    pub fn Images(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "img"))
    }

    pub fn Embeds(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "embed"))
    }

    pub fn Plugins(&self) -> @mut HTMLCollection {
        self.Embeds()
    }

    pub fn Links(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem|
            (eq_slice(elem.tag_name, "a") || eq_slice(elem.tag_name, "area"))
            && elem.get_attr(Null, "href").is_some())
    }

    pub fn Forms(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "form"))
    }

    pub fn Scripts(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "script"))
    }

    pub fn Anchors(&self) -> @mut HTMLCollection {
        self.parent.createHTMLCollection(|elem|
            eq_slice(elem.tag_name, "a") && elem.get_attr(Null, "name").is_some())
    }

    pub fn Applets(&self) -> @mut HTMLCollection {
        // FIXME: This should be return OBJECT elements containing applets.
        self.parent.createHTMLCollection(|elem| eq_slice(elem.tag_name, "applet"))
    }
}

impl Reflectable for HTMLDocument {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.parent.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.parent.mut_reflector()
    }
}

impl Traceable for HTMLDocument {
    fn trace(&self, tracer: *mut JSTracer) {
        self.parent.trace(tracer);
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

/// An element of the Table of contents
#[derive(Debug, Clone)]
pub struct TocElement {
    /// The level. 0: part, 1: chapter, 2: section, ...
    pub level: i32,
    /// The link
    pub url: String,
    /// Title of this entry
    pub title: String,
    /// Inner elements
    pub children: Vec<TocElement>,
}

impl TocElement {
    /// Creates a new element of the toc
    pub fn new<S1: Into<String>, S2: Into<String>>(url: S1,
                                                   title: S2) -> TocElement {
        TocElement {
            level: 1,
            url: url.into(),
            title: title.into(),
            children: vec!(),
        }
    }

    /// Sets the level of a TocElement
    pub fn level(mut self, level: i32) -> Self {
        self.level = level;
        self
    }

    /// Change level, recursively, so the structure keeps having some sense
    pub fn level_up(&mut self, level: i32) {
        self.level = level;
        for mut child in self.children.iter_mut() {
            if child.level <= self.level {
                child.level_up(level + 1);
            }
        }
    }

    /// Add a child to this element. Adjust the level of the child to be the level
    /// of its parents, plus 1.
    pub fn child(mut self, mut child: TocElement) -> Self {
        if child.level <= self.level {
            child.level_up(self.level + 1);
        }
        self.children.push(child);
        self
    }

    /// Add element to self or to children, according to level
    pub fn add(&mut self, element: TocElement) {
        let mut inserted = false;
        if let Some(ref mut last_elem) = self.children.last_mut() {
            if element.level > last_elem.level {
                last_elem.add(element.clone());
                inserted = true;
            }
        }
        if !inserted {
            self.children.push(element);
        }
    }

    /// Render element for Epub's toc.ncx format
    pub fn render_epub(&self, mut offset: u32) -> (u32, String) {
        offset += 1;
        let id = offset;
        let children = if self.children.is_empty() {
            String::new()
        } else {
            let mut output = String::new();
            for child in &self.children {
                let (n, s) = child.render_epub(offset);
                offset = n;
                output.push_str(&s);
            }
            output
        };
        (offset,
         format!("
<navPoint id = \"navPoint-{id}\">
  <navLabel>
   <text>{title}</text>
  </navLabel>
  <content src = \"{url}\" />
{children}
</navPoint>",
                id = id,
                title = self.title,
                url = self.url,
                children = children))

    }

    /// Render element as a list element
    pub fn render(&self, numbered: bool) -> String {
        let children = if self.children.is_empty() {
            String::new()
        } else {
            let mut output = String::new();
            for child in &self.children {
                output.push_str(&child.render(numbered));
            }
            format!("\n<{oul}>{children}\n</{oul}>\n",
                    oul = if numbered { "ol" } else { "ul" },
                    children = output)
        };
        format!("
{title}{children}
\n",
                link = self.url,
                title = self.title,
                children = children)

    }
}

/// A Table Of Contents
///
/// It basically contains a list of [`TocElement`](struct.TocElement.html)s.
///
/// # Example
///
/// Creates a Toc, fills it, and render it to HTML:
///
/// ```
/// use epub_builder::{Toc, TocElement};
/// Toc::new()
///    // add a level-1 element
///    .add(TocElement::new("intro.xhtml", "Introduction"))
///    // add a level-1 element with children
///    .add(TocElement::new("chapter_1.xhtml", "Chapter 1")
///            .child(TocElement::new("chapter_1.xhtml#section1", "1.1: Some section"))
///            .child(TocElement::new("chapter_1.xhtml#section2", "1.2: another section")))
///    // add a level-2 element, which will thus get "attached" to previous level-1 element
///    .add(TocElement::new("chapter_1.xhtml#section3", "1.3: yet another section")
///            .level(2))
///    // render the toc (non-numbered list) and returns a string
///    .render(false);
/// ```
#[derive(Debug)]
pub struct Toc {
    /// The elements composing the TOC
    pub elements: Vec<TocElement>,
}

impl Toc {
    /// Creates a new, empty, Toc
    pub fn new() -> Toc {
        Toc {
            elements: vec![],
        }
    }

    /// Returns `true` if the toc is empty, `false` else.
    ///
    /// Note that `empty` here means that the the toc has zero *or one*
    /// element, since it's still not worth displaying it in this case.
    pub fn is_empty(&self) -> bool {
        self.elements.len() <= 1
    }

    /// Adds a [`TocElement`](struct.TocElement.html) to the Toc.
    ///
    /// This will look at the element's level and will insert it as a child of the last
    /// element of the Toc that has an inferior level.
    ///
    /// # Example
    ///
    /// ```
    /// # use epub_builder::{Toc, TocElement};
    /// let mut toc = Toc::new();
    /// // Insert an element at default level (1)
    /// toc.add(TocElement::new("chapter_1.xhtml", "Chapter 1"));
    ///
    /// // Insert an element at level 2
    /// toc.add(TocElement::new("section_1.xhtml", "Section 1")
    ///           .level(2));
    /// // "Section 1" is now a child of "Chapter 1"
    /// ```
    ///
    /// There are some cases where this behaviour might not be what you want; however,
    /// it makes sure that the TOC can still be renderer correctly for HTML and EPUB.
    pub fn add(&mut self, element: TocElement) -> &mut Self {
        let mut inserted = false;
        if let Some(ref mut last_elem) = self.elements.last_mut() {
            if element.level > last_elem.level {
                last_elem.add(element.clone());
                inserted = true;
            }
        }
        if !inserted {
            self.elements.push(element);
        }

        self
    }

    /// Render the Toc in a toc.ncx compatible way, for EPUB.
    pub fn render_epub(&mut self) -> String {
        let mut output = String::new();
        let mut offset = 0;
        for elem in &self.elements {
            let (n, s) = elem.render_epub(offset);
            offset = n;
            output.push_str(&s);
        }
        output
    }

    /// Render the Toc in either
 or
 form (according to numbered)
    pub fn render(&mut self, numbered: bool) -> String {
        let mut output = String::new();
        for elem in &self.elements {
            output.push_str(&elem.render(numbered));
        }
        format!("<{oul}>\n{output}\n</{oul}>\n",
                output = output,
                oul = if numbered { "ol" } else { "ul" })
    }
}

/////////////////////////////////////////////////////////////////////////////////
//                                   TESTS                                     //
/////////////////////////////////////////////////////////////////////////////////

#[test]
fn toc_simple() {
    let mut toc = Toc::new();
    toc.add(TocElement::new("#1", "0.0.1").level(3));
    toc.add(TocElement::new("#2", "1").level(1));
    toc.add(TocElement::new("#3", "1.0.1").level(3));
    toc.add(TocElement::new("#4", "1.1").level(2));
    toc.add(TocElement::new("#5", "2"));
    let actual = toc.render(false);
    let expected = "

0.0.1

1

1.0.1

1.1

2

";
    assert_eq!(&actual, expected);
}

#[test]
fn toc_epub_simple() {
    let mut toc = Toc::new();
    toc.add(TocElement::new("#1", "1"));
    toc.add(TocElement::new("#2", "2"));
    let actual = toc.render_epub();
    let expected = "
<navPoint id = \"navPoint-1\">
  <navLabel>
   <text>1</text>
  </navLabel>
  <content src = \"#1\" />

</navPoint>
<navPoint id = \"navPoint-2\">
  <navLabel>
   <text>2</text>
  </navLabel>
  <content src = \"#2\" />

</navPoint>";
    assert_eq!(&actual, expected);
}

#[test]
fn toc_epub_simple_sublevels() {
    let mut toc = Toc::new();
    toc.add(TocElement::new("#1", "1"));
    toc.add(TocElement::new("#1.1","1.1").level(2));
    toc.add(TocElement::new("#2", "2"));
    toc.add(TocElement::new("#2.1","2.1").level(2));
    let actual = toc.render_epub();
    let expected = "
<navPoint id = \"navPoint-1\">
  <navLabel>
   <text>1</text>
  </navLabel>
  <content src = \"#1\" />

<navPoint id = \"navPoint-2\">
  <navLabel>
   <text>1.1</text>
  </navLabel>
  <content src = \"#1.1\" />

</navPoint>
</navPoint>
<navPoint id = \"navPoint-3\">
  <navLabel>
   <text>2</text>
  </navLabel>
  <content src = \"#2\" />

<navPoint id = \"navPoint-4\">
  <navLabel>
   <text>2.1</text>
  </navLabel>
  <content src = \"#2.1\" />

</navPoint>
</navPoint>";
    assert_eq!(&actual, expected);
}

#[test]
fn toc_epub_broken_sublevels() {
    let mut toc = Toc::new();
    toc.add(TocElement::new("#1.1", "1.1").level(2));
    toc.add(TocElement::new("#2", "2"));
    toc.add(TocElement::new("#2.1", "2.1").level(2));
    let actual = toc.render_epub();
    let expected = "
<navPoint id = \"navPoint-1\">
  <navLabel>
   <text>1.1</text>
  </navLabel>
  <content src = \"#1.1\" />

</navPoint>\n<navPoint id = \"navPoint-2\">
  <navLabel>
   <text>2</text>
  </navLabel>
  <content src = \"#2\" />

<navPoint id = \"navPoint-3\">
  <navLabel>
   <text>2.1</text>
  </navLabel>
  <content src = \"#2.1\" />

</navPoint>\n</navPoint>";
    assert_eq!(&actual, expected);
}
<commit_msg>Add documentation to TocElement<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

/// An element of the [Table of contents](struct.Toc.html)
///
/// # Example
///
/// ```
/// use epub_builder::TocElement;
/// TocElement::new("chapter_1.xhtml", "Chapter 1")
///     .child(TocElement::new("chapter_1.xhtml#1", "Chapter 1, section 1")
///               .child(TocElement::new("chapter_1.xhtml#1-1", "Chapter 1, section 1, subsection 1")));
/// ```
#[derive(Debug, Clone)]
pub struct TocElement {
    /// The level. 0: part, 1: chapter, 2: section, ...
    pub level: i32,
    /// The link
    pub url: String,
    /// Title of this entry
    pub title: String,
    /// Inner elements
    pub children: Vec<TocElement>,
}

impl TocElement {
    /// Creates a new element of the toc
    ///
    /// By default, the element's level is `1` and it has no children.
    pub fn new<S1: Into<String>, S2: Into<String>>(url: S1,
                                                   title: S2) -> TocElement {
        TocElement {
            level: 1,
            url: url.into(),
            title: title.into(),
            children: vec!(),
        }
    }

    /// Sets the level of a TocElement
    pub fn level(mut self, level: i32) -> Self {
        self.level = level;
        self
    }

    /// Change level, recursively, so the structure keeps having some sense
    fn level_up(&mut self, level: i32) {
        self.level = level;
        for mut child in self.children.iter_mut() {
            if child.level <= self.level {
                child.level_up(level + 1);
            }
        }
    }

    /// Add a child to this element.
    ///
    /// This adjust the level of the child to be the level of its parents, plus 1;
    /// this means that there is no point in manually setting the level to elements
    /// added with this method.
    ///
    /// # Example
    ///
    /// ```
    /// use epub_builder::TocElement;
    /// let elem = TocElement::new("foo.xhtml", "Foo")
    ///     .child(TocElement::new("bar.xhtml", "Bar")
    ///          .level(42));
    ///
    /// // `Bar`'s level wiss still be `2`.
    /// ```
    pub fn child(mut self, mut child: TocElement) -> Self {
        if child.level <= self.level {
            child.level_up(self.level + 1);
        }
        self.children.push(child);
        self
    }

    /// Add element to self or to children, according to its level
    ///
    /// This will adds `element` directly to `self` if its level is equal or less
    /// to the last children element; else it will insert it to the last child.
    ///
    /// See the `add` method of [`Toc](struct.toc.html).
    pub fn add(&mut self, element: TocElement) {
        let mut inserted = false;
        if let Some(ref mut last_elem) = self.children.last_mut() {
            if element.level > last_elem.level {
                last_elem.add(element.clone());
                inserted = true;
            }
        }
        if !inserted {
            self.children.push(element);
        }
    }

    /// Render element for Epub's toc.ncx format
    #[doc(hidden)]
    pub fn render_epub(&self, mut offset: u32) -> (u32, String) {
        offset += 1;
        let id = offset;
        let children = if self.children.is_empty() {
            String::new()
        } else {
            let mut output = String::new();
            for child in &self.children {
                let (n, s) = child.render_epub(offset);
                offset = n;
                output.push_str(&s);
            }
            output
        };
        (offset,
         format!("
<navPoint id = \"navPoint-{id}\">
  <navLabel>
   <text>{title}</text>
  </navLabel>
  <content src = \"{url}\" />
{children}
</navPoint>",
                id = id,
                title = self.title,
                url = self.url,
                children = children))

    }

    /// Render element as a list element
    #[doc(hidden)]
    pub fn render(&self, numbered: bool) -> String {
        let children = if self.children.is_empty() {
            String::new()
        } else {
            let mut output = String::new();
            for child in &self.children {
                output.push_str(&child.render(numbered));
            }
            format!("\n<{oul}>{children}\n</{oul}>\n",
                    oul = if numbered { "ol" } else { "ul" },
                    children = output)
        };
        format!("
{title}{children}
\n",
                link = self.url,
                title = self.title,
                children = children)

    }
}

/// A Table Of Contents
///
/// It basically contains a list of [`TocElement`](struct.TocElement.html)s.
///
/// # Example
///
/// Creates a Toc, fills it, and render it to HTML:
///
/// ```
/// use epub_builder::{Toc, TocElement};
/// Toc::new()
///    // add a level-1 element
///    .add(TocElement::new("intro.xhtml", "Introduction"))
///    // add a level-1 element with children
///    .add(TocElement::new("chapter_1.xhtml", "Chapter 1")
///            .child(TocElement::new("chapter_1.xhtml#section1", "1.1: Some section"))
///            .child(TocElement::new("chapter_1.xhtml#section2", "1.2: another section")))
///    // add a level-2 element, which will thus get "attached" to previous level-1 element
///    .add(TocElement::new("chapter_1.xhtml#section3", "1.3: yet another section")
///            .level(2))
///    // render the toc (non-numbered list) and returns a string
///    .render(false);
/// ```
#[derive(Debug)]
pub struct Toc {
    /// The elements composing the TOC
    pub elements: Vec<TocElement>,
}

impl Toc {
    /// Creates a new, empty, Toc
    pub fn new() -> Toc {
        Toc {
            elements: vec![],
        }
    }

    /// Returns `true` if the toc is empty, `false` else.
    ///
    /// Note that `empty` here means that the the toc has zero *or one*
    /// element, since it's still not worth displaying it in this case.
    pub fn is_empty(&self) -> bool {
        self.elements.len() <= 1
    }

    /// Adds a [`TocElement`](struct.TocElement.html) to the Toc.
    ///
    /// This will look at the element's level and will insert it as a child of the last
    /// element of the Toc that has an inferior level.
    ///
    /// # Example
    ///
    /// ```
    /// # use epub_builder::{Toc, TocElement};
    /// let mut toc = Toc::new();
    /// // Insert an element at default level (1)
    /// toc.add(TocElement::new("chapter_1.xhtml", "Chapter 1"));
    ///
    /// // Insert an element at level 2
    /// toc.add(TocElement::new("section_1.xhtml", "Section 1")
    ///           .level(2));
    /// // "Section 1" is now a child of "Chapter 1"
    /// ```
    ///
    /// There are some cases where this behaviour might not be what you want; however,
    /// it makes sure that the TOC can still be renderer correctly for HTML and EPUB.
    pub fn add(&mut self, element: TocElement) -> &mut Self {
        let mut inserted = false;
        if let Some(ref mut last_elem) = self.elements.last_mut() {
            if element.level > last_elem.level {
                last_elem.add(element.clone());
                inserted = true;
            }
        }
        if !inserted {
            self.elements.push(element);
        }

        self
    }

    /// Render the Toc in a toc.ncx compatible way, for EPUB.
    pub fn render_epub(&mut self) -> String {
        let mut output = String::new();
        let mut offset = 0;
        for elem in &self.elements {
            let (n, s) = elem.render_epub(offset);
            offset = n;
            output.push_str(&s);
        }
        output
    }

    /// Render the Toc in either
 or
 form (according to numbered)
    pub fn render(&mut self, numbered: bool) -> String {
        let mut output = String::new();
        for elem in &self.elements {
            output.push_str(&elem.render(numbered));
        }
        format!("<{oul}>\n{output}\n</{oul}>\n",
                output = output,
                oul = if numbered { "ol" } else { "ul" })
    }
}

/////////////////////////////////////////////////////////////////////////////////
//                                   TESTS                                     //
/////////////////////////////////////////////////////////////////////////////////

#[test]
fn toc_simple() {
    let mut toc = Toc::new();
    toc.add(TocElement::new("#1", "0.0.1").level(3));
    toc.add(TocElement::new("#2", "1").level(1));
    toc.add(TocElement::new("#3", "1.0.1").level(3));
    toc.add(TocElement::new("#4", "1.1").level(2));
    toc.add(TocElement::new("#5", "2"));
    let actual = toc.render(false);
    let expected = "

0.0.1

1

1.0.1

1.1

2

";
    assert_eq!(&actual, expected);
}

#[test]
fn toc_epub_simple() {
    let mut toc = Toc::new();
    toc.add(TocElement::new("#1", "1"));
    toc.add(TocElement::new("#2", "2"));
    let actual = toc.render_epub();
    let expected = "
<navPoint id = \"navPoint-1\">
  <navLabel>
   <text>1</text>
  </navLabel>
  <content src = \"#1\" />

</navPoint>
<navPoint id = \"navPoint-2\">
  <navLabel>
   <text>2</text>
  </navLabel>
  <content src = \"#2\" />

</navPoint>";
    assert_eq!(&actual, expected);
}

#[test]
fn toc_epub_simple_sublevels() {
    let mut toc = Toc::new();
    toc.add(TocElement::new("#1", "1"));
    toc.add(TocElement::new("#1.1","1.1").level(2));
    toc.add(TocElement::new("#2", "2"));
    toc.add(TocElement::new("#2.1","2.1").level(2));
    let actual = toc.render_epub();
    let expected = "
<navPoint id = \"navPoint-1\">
  <navLabel>
   <text>1</text>
  </navLabel>
  <content src = \"#1\" />

<navPoint id = \"navPoint-2\">
  <navLabel>
   <text>1.1</text>
  </navLabel>
  <content src = \"#1.1\" />

</navPoint>
</navPoint>
<navPoint id = \"navPoint-3\">
  <navLabel>
   <text>2</text>
  </navLabel>
  <content src = \"#2\" />

<navPoint id = \"navPoint-4\">
  <navLabel>
   <text>2.1</text>
  </navLabel>
  <content src = \"#2.1\" />

</navPoint>
</navPoint>";
    assert_eq!(&actual, expected);
}

#[test]
fn toc_epub_broken_sublevels() {
    let mut toc = Toc::new();
    toc.add(TocElement::new("#1.1", "1.1").level(2));
    toc.add(TocElement::new("#2", "2"));
    toc.add(TocElement::new("#2.1", "2.1").level(2));
    let actual = toc.render_epub();
    let expected = "
<navPoint id = \"navPoint-1\">
  <navLabel>
   <text>1.1</text>
  </navLabel>
  <content src = \"#1.1\" />

</navPoint>\n<navPoint id = \"navPoint-2\">
  <navLabel>
   <text>2</text>
  </navLabel>
  <content src = \"#2\" />

<navPoint id = \"navPoint-3\">
  <navLabel>
   <text>2.1</text>
  </navLabel>
  <content src = \"#2.1\" />

</navPoint>\n</navPoint>";
    assert_eq!(&actual, expected);
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::env;
use std::ffi::OsStr;
use std::io;
use std::path::{Path, PathBuf};

use lazy_static::lazy_static;
use log::warn;
use strum_macros::EnumString;
use walkdir;

use crate::config::{self, VutConfig};
use crate::template::{self, RenderTemplateError, TemplateInput};
use crate::util;
use crate::version::{self, Version};
use crate::version_source::{self, VersionSource};

const VUT_CONFIG_FILENAME: &str = ".vutconfig.toml";

lazy_static! {
    static ref VUTEMPLATE_EXTENSION: &'static OsStr = OsStr::new("vutemplate");
}

#[derive(Debug, EnumString)]
#[strum(serialize_all = "lowercase")]
pub enum BumpVersion {
    Major,
    Minor,
    Patch,
    Prerelease,
    Build,
}

pub enum VutError {
    OpenConfig(util::FileError),
    ReadConfig(io::Error),
    ParseConfig(Cow<'static, str>),
    NoVersionSource,
    VersionFileOpen(util::FileError),
    VersionFileRead(io::Error),
    VersionFileWrite(io::Error),
    TemplateGenerate(RenderTemplateError),
    Other(Cow<'static, str>),
}

pub struct Vut {
    root_path: PathBuf,
    config: VutConfig,
    authoritative_version_source: Box<dyn VersionSource>,
}

impl Vut {
    pub fn new(path: impl AsRef<Path>) -> Self {
        let path = path.as_ref();
        let source = version_source::VersionFileSource::new(path);

        Self {
            root_path: path.to_path_buf(),
            config: VutConfig::default(),
            authoritative_version_source: Box::new(source),
        }
    }

    pub fn from_path(path: impl AsRef<Path>) -> Result<Self, VutError> {
        let path = path.as_ref();

        let config_file_path = util::locate_config_file(path, VUT_CONFIG_FILENAME);

        let config = if let Some(path) = config_file_path.as_ref() {
            config::VutConfig::from_file(path)?
        } else {
            config::VutConfig::default()
        };

        let (root_path, authoritative_version_source) = if let Some(config_file_path) = config_file_path.as_ref() {
            let root_path = config_file_path.parent().unwrap().to_path_buf();

            // TODO: Implement the correct method to use here. This is wrong.
            let source = version_source::locate_version_source_from(path).ok_or_else(|| VutError::NoVersionSource)?;

            (root_path, source)
        } else {
            // No config file found.
            // Fall back to trying to locate a version source instead.

            let source = version_source::locate_version_source_from(path).ok_or_else(|| VutError::NoVersionSource)?;

            // TODO: Find a better way to display deprecation warning.
            warn!("DEPRECATED: Authoritative version source present with no config file. Create a .vutconfig in the root of the project.");

            let root_path = source.get_path().to_path_buf();

            (root_path, source)
        };

        Ok(Self {
            root_path: root_path.to_path_buf(),
            config,
            authoritative_version_source,
        })
    }

    pub fn from_current_dir() -> Result<Self, VutError> {
        let current_dir = env::current_dir().unwrap();

        Self::from_path(current_dir)
    }

    pub fn exists(&self) -> bool {
        self.authoritative_version_source.exists()
    }

    pub fn get_root_path(&self) -> &Path {
        &self.root_path
    }

    pub fn get_version(&self) -> Result<Version, VutError> {
        self.authoritative_version_source.get_version()
    }

    pub fn set_version(&mut self, version: &Version) -> Result<(), VutError> {
        self.authoritative_version_source.set_version(version)
    }

    pub fn bump_version(&mut self, bump_version: BumpVersion) -> Result<Version, VutError> {
        let version = self.get_version()?;

        let version = match bump_version {
            BumpVersion::Major => version.bump_major(),
            BumpVersion::Minor => version.bump_minor(),
            BumpVersion::Patch => version.bump_patch(),
            BumpVersion::Prerelease => version.bump_prerelease(),
            BumpVersion::Build => version.bump_build(),
        };

        self.authoritative_version_source.set_version(&version)?;

        Ok(version)
    }

    pub fn generate_template_input(&self) -> Result<TemplateInput, VutError> {
        let version = self.get_version()?;

        let mut template_input = TemplateInput::new();

        let split_prerelease = version
            .prerelease
            .as_ref()
            .map_or(None, |p| version::split_numbered_prerelease(p));
        let split_build = version
            .build
            .as_ref()
            .map_or(None, |b| version::split_numbered_prerelease(b));

        template_input
            .values
            .insert("FullVersion".to_owned(), version.to_string());
        template_input.values.insert(
            "Version".to_owned(),
            Version {
                build: None,
                ..version.clone()
            }
            .to_string(),
        );
        template_input.values.insert(
            "MajorMinorPatch".to_owned(),
            format!("{}.{}.{}", version.major, version.minor, version.patch),
        );
        template_input
            .values
            .insert("MajorMinor".to_owned(), format!("{}.{}", version.major, version.minor));
        template_input
            .values
            .insert("Major".to_owned(), format!("{}", version.major));
        template_input
            .values
            .insert("Minor".to_owned(), format!("{}", version.minor));
        template_input
            .values
            .insert("Patch".to_owned(), format!("{}", version.patch));
        template_input.values.insert(
            "Prerelease".to_owned(),
            version.prerelease.as_ref().map_or("", |p| p).to_owned(),
        );
        template_input.values.insert(
            "PrereleasePrefix".to_owned(),
            split_prerelease
                .and_then(|sp| Some(sp.0.to_owned()))
                .unwrap_or_else(|| "".to_owned()),
        );
        template_input.values.insert(
            "PrereleaseNumber".to_owned(),
            split_prerelease
                .and_then(|sp| Some(format!("{}", sp.1)))
                .unwrap_or_else(|| "".to_owned()),
        );
        template_input
            .values
            .insert("Build".to_owned(), version.build.as_ref().map_or("", |b| b).to_owned());
        template_input.values.insert(
            "BuildPrefix".to_owned(),
            split_build
                .and_then(|sp| Some(sp.0.to_owned()))
                .unwrap_or_else(|| "".to_owned()),
        );
        template_input.values.insert(
            "BuildNumber".to_owned(),
            split_build
                .and_then(|sp| Some(format!("{}", sp.1)))
                .unwrap_or_else(|| "".to_owned()),
        );

        Ok(template_input)
    }

    pub fn locate_templates(&self) -> Vec<PathBuf> {
        let root_path = &self.root_path;

        let template_files: Vec<PathBuf> = walkdir::WalkDir::new(root_path)
            .into_iter()
            // Filter known VCS metadata directories
            .filter_entry(|entry| {
                !entry
                    .file_name()
                    .to_str()
                    .map(|s| s == ".git" || s == ".hg")
                    .unwrap_or(false)
            })
            .filter_map(|entry| entry.ok())
            .map(|entry| entry.into_path())
            // Only include template files
            .filter(|path| match path.extension() {
                Some(ext) => ext == *VUTEMPLATE_EXTENSION,
                None => false,
            })
            // Make paths absolute
            .map(|path| util::normalize_path(&path).into_owned())
            // Exclude paths outside the root path
            .filter(|path| path.starts_with(&root_path))
            .collect();

        template_files
    }

    pub fn locate_nested_sources(&self) -> Vec<Box<dyn VersionSource>> {
        let root_path = &self.root_path;

        let directories: Vec<PathBuf> = walkdir::WalkDir::new(root_path)
            .into_iter()
            // Filter known VCS metadata directories
            .filter_entry(|entry| {
                !entry
                    .file_name()
                    .to_str()
                    .map(|s| s == ".git" || s == ".hg")
                    .unwrap_or(false)
            })
            .filter_map(|entry| entry.ok())
            .map(|entry| entry.into_path())
            // Only include directories
            .filter(|path| path.is_dir())
            // Make paths absolute
            .map(|path| util::normalize_path(&path).into_owned())
            // Exclude paths outside the root path
            .filter(|path| path.starts_with(&root_path))
            .collect();

        let mut sources: Vec<Box<dyn VersionSource>> = Vec::new();

        for path in directories.iter() {
            if let Some(source) = version_source::version_source_from_path(path) {
                sources.push(source);
            }
        }

        sources
    }

    pub fn generate_output(&self) -> Result<(), VutError> {
        let template_input = self.generate_template_input()?;
        let template_files = self.locate_templates();

        let mut processed_files: Vec<PathBuf> = Vec::new();
        let mut generated_files: Vec<PathBuf> = Vec::new();

        for file in template_files {
            let generated_file =
                template::generate_template::<template::processor::VutProcessor>(&file, &template_input, None)
                    .map_err(|err| VutError::TemplateGenerate(err))?;

            processed_files.push(file);
            generated_files.push(generated_file);
        }

        if self.config.update_nested_sources {
            let version = self.get_version()?;
            let nested_sources = self.locate_nested_sources();

            for mut source in nested_sources {
                source.set_version(&version)?;
            }
        }

        Ok(())
    }
}
<commit_msg>feat: Improve performance of generation<commit_after>use std::borrow::Cow;
use std::env;
use std::ffi::OsStr;
use std::io;
use std::path::{Path, PathBuf};

use lazy_static::lazy_static;
use log::warn;
use strum_macros::EnumString;
use walkdir;

use crate::config::{self, VutConfig};
use crate::template::{self, RenderTemplateError, TemplateInput};
use crate::util;
use crate::version::{self, Version};
use crate::version_source::{self, VersionSource};

const VUT_CONFIG_FILENAME: &str = ".vutconfig.toml";

lazy_static! {
    static ref VUTEMPLATE_EXTENSION: &'static OsStr = OsStr::new("vutemplate");
}

#[derive(Debug, EnumString)]
#[strum(serialize_all = "lowercase")]
pub enum BumpVersion {
    Major,
    Minor,
    Patch,
    Prerelease,
    Build,
}

pub enum VutError {
    OpenConfig(util::FileError),
    ReadConfig(io::Error),
    ParseConfig(Cow<'static, str>),
    NoVersionSource,
    VersionFileOpen(util::FileError),
    VersionFileRead(io::Error),
    VersionFileWrite(io::Error),
    TemplateGenerate(RenderTemplateError),
    Other(Cow<'static, str>),
}

pub struct Vut {
    root_path: PathBuf,
    config: VutConfig,
    authoritative_version_source: Box<dyn VersionSource>,
}

impl Vut {
    pub fn new(path: impl AsRef<Path>) -> Self {
        let path = path.as_ref();
        let source = version_source::VersionFileSource::new(path);

        Self {
            root_path: path.to_path_buf(),
            config: VutConfig::default(),
            authoritative_version_source: Box::new(source),
        }
    }

    pub fn from_path(path: impl AsRef<Path>) -> Result<Self, VutError> {
        let path = path.as_ref();

        let config_file_path = util::locate_config_file(path, VUT_CONFIG_FILENAME);

        let config = if let Some(path) = config_file_path.as_ref() {
            config::VutConfig::from_file(path)?
        } else {
            config::VutConfig::default()
        };

        let (root_path, authoritative_version_source) = if let Some(config_file_path) = config_file_path.as_ref() {
            let root_path = config_file_path.parent().unwrap().to_path_buf();

            // TODO: Implement the correct method to use here. This is wrong.
            let source = version_source::locate_version_source_from(path).ok_or_else(|| VutError::NoVersionSource)?;

            (root_path, source)
        } else {
            // No config file found.
            // Fall back to trying to locate a version source instead.

            let source = version_source::locate_version_source_from(path).ok_or_else(|| VutError::NoVersionSource)?;

            // TODO: Find a better way to display deprecation warning.
            warn!("DEPRECATED: Authoritative version source present with no config file. Create a .vutconfig in the root of the project.");

            let root_path = source.get_path().to_path_buf();

            (root_path, source)
        };

        Ok(Self {
            root_path: root_path.to_path_buf(),
            config,
            authoritative_version_source,
        })
    }

    pub fn from_current_dir() -> Result<Self, VutError> {
        let current_dir = env::current_dir().unwrap();

        Self::from_path(current_dir)
    }

    pub fn exists(&self) -> bool {
        self.authoritative_version_source.exists()
    }

    pub fn get_root_path(&self) -> &Path {
        &self.root_path
    }

    pub fn get_version(&self) -> Result<Version, VutError> {
        self.authoritative_version_source.get_version()
    }

    pub fn set_version(&mut self, version: &Version) -> Result<(), VutError> {
        self.authoritative_version_source.set_version(version)
    }

    pub fn bump_version(&mut self, bump_version: BumpVersion) -> Result<Version, VutError> {
        let version = self.get_version()?;

        let version = match bump_version {
            BumpVersion::Major => version.bump_major(),
            BumpVersion::Minor => version.bump_minor(),
            BumpVersion::Patch => version.bump_patch(),
            BumpVersion::Prerelease => version.bump_prerelease(),
            BumpVersion::Build => version.bump_build(),
        };

        self.authoritative_version_source.set_version(&version)?;

        Ok(version)
    }

    pub fn generate_template_input(&self) -> Result<TemplateInput, VutError> {
        let version = self.get_version()?;

        let mut template_input = TemplateInput::new();

        let split_prerelease = version
            .prerelease
            .as_ref()
            .map_or(None, |p| version::split_numbered_prerelease(p));
        let split_build = version
            .build
            .as_ref()
            .map_or(None, |b| version::split_numbered_prerelease(b));

        template_input
            .values
            .insert("FullVersion".to_owned(), version.to_string());
        template_input.values.insert(
            "Version".to_owned(),
            Version {
                build: None,
                ..version.clone()
            }
            .to_string(),
        );
        template_input.values.insert(
            "MajorMinorPatch".to_owned(),
            format!("{}.{}.{}", version.major, version.minor, version.patch),
        );
        template_input
            .values
            .insert("MajorMinor".to_owned(), format!("{}.{}", version.major, version.minor));
        template_input
            .values
            .insert("Major".to_owned(), format!("{}", version.major));
        template_input
            .values
            .insert("Minor".to_owned(), format!("{}", version.minor));
        template_input
            .values
            .insert("Patch".to_owned(), format!("{}", version.patch));
        template_input.values.insert(
            "Prerelease".to_owned(),
            version.prerelease.as_ref().map_or("", |p| p).to_owned(),
        );
        template_input.values.insert(
            "PrereleasePrefix".to_owned(),
            split_prerelease
                .and_then(|sp| Some(sp.0.to_owned()))
                .unwrap_or_else(|| "".to_owned()),
        );
        template_input.values.insert(
            "PrereleaseNumber".to_owned(),
            split_prerelease
                .and_then(|sp| Some(format!("{}", sp.1)))
                .unwrap_or_else(|| "".to_owned()),
        );
        template_input
            .values
            .insert("Build".to_owned(), version.build.as_ref().map_or("", |b| b).to_owned());
        template_input.values.insert(
            "BuildPrefix".to_owned(),
            split_build
                .and_then(|sp| Some(sp.0.to_owned()))
                .unwrap_or_else(|| "".to_owned()),
        );
        template_input.values.insert(
            "BuildNumber".to_owned(),
            split_build
                .and_then(|sp| Some(format!("{}", sp.1)))
                .unwrap_or_else(|| "".to_owned()),
        );

        Ok(template_input)
    }

    pub fn locate_templates_and_nested_sources(&self) -> (Vec<PathBuf>, Vec<Box<dyn VersionSource>>) {
        let root_path = &self.root_path;

        let dir_entries: Vec<walkdir::DirEntry> = walkdir::WalkDir::new(root_path)
            .into_iter()
            // Filter known VCS metadata directories
            .filter_entry(|entry| {
                !entry
                    .file_name()
                    .to_str()
                    .map(|s| s == ".git" || s == ".hg")
                    .unwrap_or(false)
            })
            .filter_map(|entry| entry.ok())
            .collect();

        let template_files: Vec<PathBuf> = dir_entries
            .iter()
            .map(|entry| entry.path())
            // Only include template files
            .filter(|path| match path.extension() {
                Some(ext) => ext == *VUTEMPLATE_EXTENSION,
                None => false,
            })
            // Transform Path references into owned PathBufs
            .map(|path| path.to_path_buf())
            .collect();

        let mut sources: Vec<Box<dyn VersionSource>> = Vec::new();

        if self.config.update_nested_sources {
            let dirs_iter = dir_entries
                .iter()
                .map(|entry| entry.path())
                // Only include directories
                .filter(|path| path.is_dir());

            for path in dirs_iter {
                if let Some(source) = version_source::version_source_from_path(&path) {
                    sources.push(source);
                }
            }
        }

        (template_files, sources)
    }

    pub fn generate_output(&self) -> Result<(), VutError> {
        let template_input = self.generate_template_input()?;
        let (template_files, nested_sources) = self.locate_templates_and_nested_sources();

        let mut processed_files: Vec<PathBuf> = Vec::new();
        let mut generated_files: Vec<PathBuf> = Vec::new();

        for file in template_files {
            let generated_file =
                template::generate_template::<template::processor::VutProcessor>(&file, &template_input, None)
                    .map_err(|err| VutError::TemplateGenerate(err))?;

            processed_files.push(file);
            generated_files.push(generated_file);
        }

        if self.config.update_nested_sources {
            let version = self.get_version()?;

            for mut source in nested_sources {
                source.set_version(&version)?;
            }
        }

        Ok(())
    }
}
<|endoftext|>


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

//! Windows console handling

// FIXME (#13400): this is only a tiny fraction of the Windows console api

extern crate kernel32;
extern crate winapi;

use std::io::prelude::*;
use std::io;
use std::ptr;

use Attr;
use Error;
use Result;
use Terminal;
use color;

/// A Terminal implementation which uses the Win32 Console API.
pub struct WinConsole<T> {
    buf: T,
    def_foreground: color::Color,
    def_background: color::Color,
    foreground: color::Color,
    background: color::Color,
}

fn color_to_bits(color: color::Color) -> u16 {
    // magic numbers from mingw-w64's wincon.h

    let bits = match color % 8 {
        color::BLACK   => 0,
        color::BLUE    => 0x1,
        color::GREEN   => 0x2,
        color::RED     => 0x4,
        color::YELLOW  => 0x2 | 0x4,
        color::MAGENTA => 0x1 | 0x4,
        color::CYAN    => 0x1 | 0x2,
        color::WHITE   => 0x1 | 0x2 | 0x4,
        _ => unreachable!()
    };

    if color >= 8 {
        bits | 0x8
    } else {
        bits
    }
}

fn bits_to_color(bits: u16) -> color::Color {
    let color = match bits & 0x7 {
        0 => color::BLACK,
        0x1 => color::BLUE,
        0x2 => color::GREEN,
        0x4 => color::RED,
        0x6 => color::YELLOW,
        0x5 => color::MAGENTA,
        0x3 => color::CYAN,
        0x7 => color::WHITE,
        _ => unreachable!()
    };

    color | (bits & 0x8) // copy the hi-intensity bit
}

// Just get a handle to the current console buffer whatever it is
fn conout() -> io::Result<winapi::HANDLE> {
    let name = b"CONOUT$\0";
    let handle = unsafe {
        kernel32::CreateFileA(
            name.as_ptr() as *const i8,
            winapi::GENERIC_READ | winapi::GENERIC_WRITE,
            winapi::FILE_SHARE_WRITE,
            ptr::null_mut(),
            winapi::OPEN_EXISTING,
            0,
            ptr::null_mut(),
        )
    };
    if handle == winapi::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else {
        Ok(handle)
    }
}

// This test will only pass if it is running in an actual console, probably
#[test]
fn test_conout() {
    assert!(conout().is_ok())
}

impl<T: Write+Send> WinConsole<T> {
    fn apply(&mut self) -> io::Result<()> {
        let out = try!(conout());
        let _unused = self.buf.flush();
        let mut accum: winapi::WORD = 0;
        accum |= color_to_bits(self.foreground);
        accum |= color_to_bits(self.background) << 4;
        unsafe {
            kernel32::SetConsoleTextAttribute(out, accum);
        }
        Ok(())
    }

    /// Returns `Err` whenever the terminal cannot be created for some
    /// reason.
    pub fn new(out: T) -> io::Result<WinConsole<T>> {
        let fg;
        let bg;
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                fg = bits_to_color(buffer_info.wAttributes);
                bg = bits_to_color(buffer_info.wAttributes >> 4);
            } else {
                return Err(io::Error::last_os_error())
            }
        }
        Ok(WinConsole {
            buf: out,
            def_foreground: fg,
            def_background: bg,
            foreground: fg,
            background: bg,
        })
    }
}

impl<T: Write> Write for WinConsole<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.buf.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

impl<T: Write+Send> Terminal for WinConsole<T> {
    type Output = T;

    fn fg(&mut self, color: color::Color) -> Result<()> {
        self.foreground = color;
        try!(self.apply());

        Ok(())
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        self.background = color;
        try!(self.apply());

        Ok(())
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(f) => {
                self.foreground = f;
                try!(self.apply());
                Ok(())
            },
            Attr::BackgroundColor(b) => {
                self.background = b;
                try!(self.apply());
                Ok(())
            },
            _ => Err(Error::NotSupported)
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        // it claims support for underscore and reverse video, but I can't get
        // it to do anything -cmr
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
            _ => false
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.foreground = self.def_foreground;
        self.background = self.def_background;
        try!(self.apply());

        Ok(())
    }

    fn cursor_up(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let (x, y) = (buffer_info.dwCursorPosition.X, buffer_info.dwCursorPosition.Y);
                if y == 0 {
                    Ok(false)
                } else {
                    let pos = winapi::COORD { X: x, Y: y - 1 };
                    if kernel32::SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(true)
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn delete_line(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 {
                return Err(io::Error::last_os_error().into())
            }
            let pos = buffer_info.dwCursorPosition;
            let size = buffer_info.dwSize;
            let num = (size.X - pos.X) as winapi::DWORD;
            let mut written = 0;
            if kernel32::FillConsoleOutputCharacterW(handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into())
            }
            if kernel32::FillConsoleOutputAttribute(handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into())
            }
            Ok(written != 0)
        }
    }

    fn carriage_return(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let (x, y) = (buffer_info.dwCursorPosition.X, buffer_info.dwCursorPosition.Y);
                if x == 0 {
                    Err(Error::CursorDestinationInvalid)
                } else {
                    let pos = winapi::COORD { X: 0, Y: y };
                    if kernel32::SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(true)
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn get_ref<'a>(&'a self) -> &'a T { &self.buf }

    fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.buf }

    fn into_inner(self) -> T where Self: Sized { self.buf }
}
<commit_msg>Patch up the Windows terminal to return Ok(()) in all cases<commit_after>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Windows console handling

// FIXME (#13400): this is only a tiny fraction of the Windows console api

extern crate kernel32;
extern crate winapi;

use std::io::prelude::*;
use std::io;
use std::ptr;

use Attr;
use Error;
use Result;
use Terminal;
use color;

/// A Terminal implementation which uses the Win32 Console API.
pub struct WinConsole<T> {
    buf: T,
    def_foreground: color::Color,
    def_background: color::Color,
    foreground: color::Color,
    background: color::Color,
}

fn color_to_bits(color: color::Color) -> u16 {
    // magic numbers from mingw-w64's wincon.h

    let bits = match color % 8 {
        color::BLACK   => 0,
        color::BLUE    => 0x1,
        color::GREEN   => 0x2,
        color::RED     => 0x4,
        color::YELLOW  => 0x2 | 0x4,
        color::MAGENTA => 0x1 | 0x4,
        color::CYAN    => 0x1 | 0x2,
        color::WHITE   => 0x1 | 0x2 | 0x4,
        _ => unreachable!()
    };

    if color >= 8 {
        bits | 0x8
    } else {
        bits
    }
}

fn bits_to_color(bits: u16) -> color::Color {
    let color = match bits & 0x7 {
        0 => color::BLACK,
        0x1 => color::BLUE,
        0x2 => color::GREEN,
        0x4 => color::RED,
        0x6 => color::YELLOW,
        0x5 => color::MAGENTA,
        0x3 => color::CYAN,
        0x7 => color::WHITE,
        _ => unreachable!()
    };

    color | (bits & 0x8) // copy the hi-intensity bit
}

// Just get a handle to the current console buffer whatever it is
fn conout() -> io::Result<winapi::HANDLE> {
    let name = b"CONOUT$\0";
    let handle = unsafe {
        kernel32::CreateFileA(
            name.as_ptr() as *const i8,
            winapi::GENERIC_READ | winapi::GENERIC_WRITE,
            winapi::FILE_SHARE_WRITE,
            ptr::null_mut(),
            winapi::OPEN_EXISTING,
            0,
            ptr::null_mut(),
        )
    };
    if handle == winapi::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else {
        Ok(handle)
    }
}

// This test will only pass if it is running in an actual console, probably
#[test]
fn test_conout() {
    assert!(conout().is_ok())
}

impl<T: Write+Send> WinConsole<T> {
    fn apply(&mut self) -> io::Result<()> {
        let out = try!(conout());
        let _unused = self.buf.flush();
        let mut accum: winapi::WORD = 0;
        accum |= color_to_bits(self.foreground);
        accum |= color_to_bits(self.background) << 4;
        unsafe {
            kernel32::SetConsoleTextAttribute(out, accum);
        }
        Ok(())
    }

    /// Returns `Err` whenever the terminal cannot be created for some
    /// reason.
    pub fn new(out: T) -> io::Result<WinConsole<T>> {
        let fg;
        let bg;
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                fg = bits_to_color(buffer_info.wAttributes);
                bg = bits_to_color(buffer_info.wAttributes >> 4);
            } else {
                return Err(io::Error::last_os_error())
            }
        }
        Ok(WinConsole {
            buf: out,
            def_foreground: fg,
            def_background: bg,
            foreground: fg,
            background: bg,
        })
    }
}

impl<T: Write> Write for WinConsole<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.buf.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

impl<T: Write+Send> Terminal for WinConsole<T> {
    type Output = T;

    fn fg(&mut self, color: color::Color) -> Result<()> {
        self.foreground = color;
        try!(self.apply());

        Ok(())
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        self.background = color;
        try!(self.apply());

        Ok(())
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(f) => {
                self.foreground = f;
                try!(self.apply());
                Ok(())
            },
            Attr::BackgroundColor(b) => {
                self.background = b;
                try!(self.apply());
                Ok(())
            },
            _ => Err(Error::NotSupported)
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        // it claims support for underscore and reverse video, but I can't get
        // it to do anything -cmr
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
            _ => false
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.foreground = self.def_foreground;
        self.background = self.def_background;
        try!(self.apply());

        Ok(())
    }

    fn cursor_up(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let (x, y) = (buffer_info.dwCursorPosition.X, buffer_info.dwCursorPosition.Y);
                if y == 0 {
                    // Even though this might want to be a CursorPositionInvalid, on Unix there
                    // is no checking to see if the cursor is already on the first line.
                    // I'm not sure what the ideal behavior is, but I think it'd be silly to have
                    // cursor_up fail in this case.
                    Ok(())
                } else {
                    let pos = winapi::COORD { X: x, Y: y - 1 };
                    if kernel32::SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn delete_line(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 {
                return Err(io::Error::last_os_error().into())
            }
            let pos = buffer_info.dwCursorPosition;
            let size = buffer_info.dwSize;
            let num = (size.X - pos.X) as winapi::DWORD;
            let mut written = 0;
            if kernel32::FillConsoleOutputCharacterW(handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into())
            }
            if kernel32::FillConsoleOutputAttribute(handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into())
            }
            // Similar reasoning for not failing as in cursor_up -- it doesn't even make sense to
            // me that these APIs could have written 0, unless the terminal is width zero.
            Ok(())
        }
    }

    fn carriage_return(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let (x, y) = (buffer_info.dwCursorPosition.X, buffer_info.dwCursorPosition.Y);
                if x == 0 {
                    Err(Error::CursorDestinationInvalid)
                } else {
                    let pos = winapi::COORD { X: 0, Y: y };
                    if kernel32::SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn get_ref<'a>(&'a self) -> &'a T { &self.buf }

    fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.buf }

    fn into_inner(self) -> T where Self: Sized { self.buf }
}
<|endoftext|>