<commit_before>use std::fmt;

use reflect::EnumDescriptor;
use reflect::EnumValueDescriptor;
use reflect::ProtobufValue;
use std::marker;

/// Trait implemented by all protobuf enum types.
pub trait ProtobufEnum: Eq + Sized + Copy + 'static + ProtobufValue + fmt::Debug + Default {
    /// Get enum `i32` value.
    fn value(&self) -> i32;

    /// Try to create an enum from `i32` value.
    /// Return `None` if value is unknown.
    fn from_i32(v: i32) -> Option<Self>;

    /// Get all enum values for enum type.
    fn values() -> &'static [Self] {
        panic!();
    }

    /// Get enum value descriptor.
    fn descriptor(&self) -> &'static EnumValueDescriptor {
        self.enum_descriptor()
            .value_by_number(self.value())
            .unwrap()
    }

    /// Get enum descriptor.
    fn enum_descriptor(&self) -> &'static EnumDescriptor {
        Self::enum_descriptor_static()
    }

    /// Get enum descriptor by type.
    fn enum_descriptor_static() -> &'static EnumDescriptor {
        panic!();
    }
}

/// Protobuf enums with possibly unknown values are preserved in this struct.
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct ProtobufEnumOrUnknown<E: ProtobufEnum> {
    value: i32,
    _marker: marker::PhantomData<E>,
}

impl<E: ProtobufEnum> ProtobufEnumOrUnknown<E> {
    /// Construct from typed enum
    pub fn new(e: E) -> ProtobufEnumOrUnknown<E> {
        ProtobufEnumOrUnknown::from_i32(e.value())
    }

    /// Construct from any `i32` value.
    ///
    /// Note passed value is not required to be a valid enum value.
    pub fn from_i32(value: i32) -> ProtobufEnumOrUnknown<E> {
        ProtobufEnumOrUnknown {
            value,
            _marker: marker::PhantomData,
        }
    }

    /// Get contained `i32` value of enum
    pub fn value(&self) -> i32 {
        self.value
    }

    /// Get `i32` value as typed enum. Return `None` is value is unknown.
    pub fn enum_value(&self) -> Result<E, i32> {
        E::from_i32(self.value).ok_or(self.value)
    }

    /// Get `i32` value as typed enum.
    /// Return default enum value (first value) if value is unknown.
    pub fn enum_value_or_default(&self) -> E {
        self.enum_value().unwrap_or_default()
    }

    /// Get enum descriptor by type.
    pub fn enum_descriptor_static() -> &'static EnumDescriptor {
        E::enum_descriptor_static()
    }
}

impl<E: ProtobufEnum> Default for ProtobufEnumOrUnknown<E> {
    fn default() -> ProtobufEnumOrUnknown<E> {
        ProtobufEnumOrUnknown::new(E::default())
    }
}

impl<E: ProtobufEnum> fmt::Debug for ProtobufEnumOrUnknown<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.enum_value() {
            Ok(e) => fmt::Debug::fmt(&e, f),
            Err(e) => fmt::Debug::fmt(&e, f),
        }
    }
}

impl<E: ProtobufEnum> ProtobufValue for ProtobufEnumOrUnknown<E> {
}
<commit_msg>impl From<E> for ProtobufEnumOrUnknown<E><commit_after>use std::fmt;

use reflect::EnumDescriptor;
use reflect::EnumValueDescriptor;
use reflect::ProtobufValue;
use std::marker;

/// Trait implemented by all protobuf enum types.
pub trait ProtobufEnum: Eq + Sized + Copy + 'static + ProtobufValue + fmt::Debug + Default {
    /// Get enum `i32` value.
    fn value(&self) -> i32;

    /// Try to create an enum from `i32` value.
    /// Return `None` if value is unknown.
    fn from_i32(v: i32) -> Option<Self>;

    /// Get all enum values for enum type.
    fn values() -> &'static [Self] {
        panic!();
    }

    /// Get enum value descriptor.
    fn descriptor(&self) -> &'static EnumValueDescriptor {
        self.enum_descriptor()
            .value_by_number(self.value())
            .unwrap()
    }

    /// Get enum descriptor.
    fn enum_descriptor(&self) -> &'static EnumDescriptor {
        Self::enum_descriptor_static()
    }

    /// Get enum descriptor by type.
    fn enum_descriptor_static() -> &'static EnumDescriptor {
        panic!();
    }
}

/// Protobuf enums with possibly unknown values are preserved in this struct.
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct ProtobufEnumOrUnknown<E: ProtobufEnum> {
    value: i32,
    _marker: marker::PhantomData<E>,
}

impl<E: ProtobufEnum> ProtobufEnumOrUnknown<E> {
    /// Construct from typed enum
    pub fn new(e: E) -> ProtobufEnumOrUnknown<E> {
        ProtobufEnumOrUnknown::from_i32(e.value())
    }

    /// Construct from any `i32` value.
    ///
    /// Note passed value is not required to be a valid enum value.
    pub fn from_i32(value: i32) -> ProtobufEnumOrUnknown<E> {
        ProtobufEnumOrUnknown {
            value,
            _marker: marker::PhantomData,
        }
    }

    /// Get contained `i32` value of enum
    pub fn value(&self) -> i32 {
        self.value
    }

    /// Get `i32` value as typed enum. Return `None` is value is unknown.
    pub fn enum_value(&self) -> Result<E, i32> {
        E::from_i32(self.value).ok_or(self.value)
    }

    /// Get `i32` value as typed enum.
    /// Return default enum value (first value) if value is unknown.
    pub fn enum_value_or_default(&self) -> E {
        self.enum_value().unwrap_or_default()
    }

    /// Get enum descriptor by type.
    pub fn enum_descriptor_static() -> &'static EnumDescriptor {
        E::enum_descriptor_static()
    }
}

impl<E: ProtobufEnum> From<E> for ProtobufEnumOrUnknown<E> {
    fn from(e: E) -> Self {
        ProtobufEnumOrUnknown::new(e)
    }
}

impl<E: ProtobufEnum> Default for ProtobufEnumOrUnknown<E> {
    fn default() -> ProtobufEnumOrUnknown<E> {
        ProtobufEnumOrUnknown::new(E::default())
    }
}

impl<E: ProtobufEnum> fmt::Debug for ProtobufEnumOrUnknown<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.enum_value() {
            Ok(e) => fmt::Debug::fmt(&e, f),
            Err(e) => fmt::Debug::fmt(&e, f),
        }
    }
}

impl<E: ProtobufEnum> ProtobufValue for ProtobufEnumOrUnknown<E> {
}
<|endoftext|>


<commit_before>extern crate gtk;
extern crate cairo;

extern crate ffmpeg;

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

use std::rc::Rc;
use std::cell::RefCell;

use gtk::prelude::*;
use cairo::enums::{FontSlant, FontWeight};

use ::media::Context;
use ::media::PacketNotifiable;

use super::MediaNotifiable;
use super::MediaController;

pub struct AudioController {
    media_ctl: MediaController,
    drawingarea: gtk::DrawingArea,
    message: String,
    frame: Option<ffmpeg::frame::Audio>,
    graph: Option<ffmpeg::filter::Graph>,
}

impl AudioController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<AudioController>> {
        // need a RefCell because the callbacks will use immutable versions of ac
        // when the UI controllers will get a mutable version from time to time
        let ac = Rc::new(RefCell::new(AudioController {
            media_ctl: MediaController::new(builder.get_object("audio-container").unwrap()),
            drawingarea: builder.get_object("audio-drawingarea").unwrap(),
            message: "audio place holder".to_owned(),
            frame: None,
            graph: None,
        }));

        let ac_for_cb = ac.clone();
        ac.borrow().drawingarea.connect_draw(move |_, cairo_ctx| {
            ac_for_cb.borrow().draw(&cairo_ctx);
            Inhibit(false)
        });

        ac
    }

    fn build_graph(&mut self, decoder: &ffmpeg::codec::decoder::Audio) -> Result<bool, String> { // TODO: check how to return Ok() only
        match self.graph {
            Some(_) => (),
            None => {
                let mut graph = ffmpeg::filter::Graph::new();

                // TBC: fix invalid channel layout
                let channel_layout = match decoder.channel_layout().bits() {
                    0 => 1,
                    bits => bits,
                };

	            let args = format!("time_base={}:sample_rate={}:sample_fmt={}:channel_layout=0x{:x}",
		            decoder.time_base(), decoder.rate(), decoder.format().name(), channel_layout);

                let in_filter = ffmpeg::filter::find("abuffer").unwrap();
                match graph.add(&in_filter, "in", &args) {
                    Ok(_) => (),
                    Err(error) => return Err(format!("Error adding in pad: {:?}", error)),
                }

                let out_filter = ffmpeg::filter::find("abuffersink").unwrap();
                match graph.add(&out_filter, "out", "") {
                    Ok(_) => (),
                    Err(error) => return Err(format!("Error adding out pad: {:?}", error)),
                }
                {
                    let mut out_pad = graph.get("out").unwrap();
                    out_pad.set_sample_format(ffmpeg::format::Sample::I16(ffmpeg::format::sample::Type::Packed));
                }

                {
                    let in_parser;
                    match graph.output("in", 0) {
                        Ok(value) => in_parser = value,
                        Err(error) => return Err(format!("Error getting output for in pad: {:?}", error)),
                    }
                    let out_parser;
                    match in_parser.input("out", 0) {
                        Ok(value) => out_parser = value,
                        Err(error) => return Err(format!("Error getting input for out pad: {:?}", error)),
                    }
                    match out_parser.parse("anull") {
                        Ok(_) => (),
                        Err(error) => return Err(format!("Error parsing format: {:?}", error)),
                    }
                }

                match graph.validate() {
                    Ok(_) => self.graph = Some(graph),
                    Err(error) => return Err(format!("Error validating graph: {:?}", error)),
                }
            },
        }

        Ok(true)
    }

    fn convert_to_pcm16(&mut self, decoder: &ffmpeg::codec::decoder::Audio,
                        frame_in: &mut ffmpeg::frame::Audio) -> Result<ffmpeg::frame::Audio, String> {
        match self.build_graph(decoder) {
            Ok(_) => {
                let mut graph = self.graph.as_mut().unwrap();
                graph.get("in").unwrap().source().add(&frame_in).unwrap();

                let mut frame_pcm = ffmpeg::frame::Audio::empty();
                while let Ok(..) = graph.get("out").unwrap().sink().frame(&mut frame_pcm) {
                }

                Ok(frame_pcm)
            },
            Err(error) => Err(error),
        }
    }

    fn draw(&self, cr: &cairo::Context) {
        let allocation = self.drawingarea.get_allocation();
        cr.scale(allocation.width as f64, allocation.height as f64);

        cr.select_font_face("Sans", FontSlant::Normal, FontWeight::Normal);
        cr.set_font_size(0.07);

        cr.move_to(0.1, 0.53);
        cr.show_text(&self.message);
    }
}

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 MediaNotifiable for AudioController {
    fn new_media(&mut self, context: &mut Context) {
        self.frame = None;
        self.graph = None;
        self.message = match context.audio_stream.as_mut() {
            Some(stream) => {
                self.set_index(stream.index);

                self.show();
                println!("\n** Audio stream\n{:?}", &stream);
                format!("audio stream {}", self.stream_index().unwrap())
            },
            None => {
                self.hide();
                "no audio stream".to_owned()
            },
        };

        self.drawingarea.queue_draw();
    }
}

impl PacketNotifiable for AudioController {
    fn new_packet(&mut self, stream: &ffmpeg::format::stream::Stream, packet: &ffmpeg::codec::packet::Packet) {
        self.print_packet_content(stream, packet);

        let decoder = stream.codec().decoder();
        match decoder.audio() {
            Ok(mut audio) => {
                let mut frame = ffmpeg::frame::Audio::empty();
                match audio.decode(packet, &mut frame) {
                    Ok(result) => if result {
                            let planes = frame.planes();
                            println!("\tdecoded audio frame, found {} planes", planes);
                            if planes > 0 {
                                println!("\tdata len: {}", frame.data(0).len());
                                match self.convert_to_pcm16(&audio, &mut frame) {
                                    Ok(frame_pcm) => self.frame = Some(frame_pcm),
                                    Err(error) =>  println!("\tError converting to pcm: {:?}", error),
                                }
                            }
                            else {
                                println!("\tno planes found in frame");
                            }
                        }
                        else {
                            println!("\tfailed to decode audio frame");
                        }
                    ,
                    Err(error) => println!("Error decoding audio: {:?}", error),
                }
            },
            Err(error) => println!("Error getting audio decoder: {:?}", error),
        }
    }
}
<commit_msg>Audio controller: first step to separate samples (#1)<commit_after>extern crate gtk;
extern crate cairo;

extern crate ffmpeg;

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

use std::rc::Rc;
use std::cell::RefCell;

use gtk::prelude::*;
use cairo::enums::{FontSlant, FontWeight};

use ::media::Context;
use ::media::PacketNotifiable;

use super::MediaNotifiable;
use super::MediaController;

pub struct AudioController {
    media_ctl: MediaController,
    drawingarea: gtk::DrawingArea,
    message: String,
    frame: Option<ffmpeg::frame::Audio>,
    graph: Option<ffmpeg::filter::Graph>,
}

impl AudioController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<AudioController>> {
        // need a RefCell because the callbacks will use immutable versions of ac
        // when the UI controllers will get a mutable version from time to time
        let ac = Rc::new(RefCell::new(AudioController {
            media_ctl: MediaController::new(builder.get_object("audio-container").unwrap()),
            drawingarea: builder.get_object("audio-drawingarea").unwrap(),
            message: "audio place holder".to_owned(),
            frame: None,
            graph: None,
        }));

        let ac_for_cb = ac.clone();
        ac.borrow().drawingarea.connect_draw(move |_, cairo_ctx| {
            ac_for_cb.borrow().draw(&cairo_ctx);
            Inhibit(false)
        });

        ac
    }

    fn build_graph(&mut self, decoder: &ffmpeg::codec::decoder::Audio) -> Result<bool, String> { // TODO: check how to return Ok() only
        match self.graph {
            Some(_) => (),
            None => {
                let mut graph = ffmpeg::filter::Graph::new();

	            let args = format!("time_base={}:sample_rate={}:sample_fmt={}:channel_layout=0x{:x}",
		            decoder.time_base(), decoder.rate(), decoder.format().name(), decoder.channel_layout().bits());

                let in_filter = ffmpeg::filter::find("abuffer").unwrap();
                match graph.add(&in_filter, "in", &args) {
                    Ok(_) => (),
                    Err(error) => return Err(format!("Error adding in pad: {:?}", error)),
                }

                let out_filter = ffmpeg::filter::find("abuffersink").unwrap();
                match graph.add(&out_filter, "out", "") {
                    Ok(_) => (),
                    Err(error) => return Err(format!("Error adding out pad: {:?}", error)),
                }
                {
                    let mut out_pad = graph.get("out").unwrap();
                    out_pad.set_sample_format(ffmpeg::format::Sample::I16(ffmpeg::format::sample::Type::Packed));
                }

                {
                    let in_parser;
                    match graph.output("in", 0) {
                        Ok(value) => in_parser = value,
                        Err(error) => return Err(format!("Error getting output for in pad: {:?}", error)),
                    }
                    let out_parser;
                    match in_parser.input("out", 0) {
                        Ok(value) => out_parser = value,
                        Err(error) => return Err(format!("Error getting input for out pad: {:?}", error)),
                    }
                    match out_parser.parse("anull") {
                        Ok(_) => (),
                        Err(error) => return Err(format!("Error parsing format: {:?}", error)),
                    }
                }

                match graph.validate() {
                    Ok(_) => self.graph = Some(graph),
                    Err(error) => return Err(format!("Error validating graph: {:?}", error)),
                }
            },
        }

        Ok(true)
    }

    fn convert_to_pcm16(&mut self, decoder: &ffmpeg::codec::decoder::Audio,
                        frame_in: &mut ffmpeg::frame::Audio) -> Result<ffmpeg::frame::Audio, String> {
        match self.build_graph(decoder) {
            Ok(_) => {
                let mut graph = self.graph.as_mut().unwrap();
                graph.get("in").unwrap().source().add(&frame_in).unwrap();

                let mut frame_pcm = ffmpeg::frame::Audio::empty();
                while let Ok(..) = graph.get("out").unwrap().sink().frame(&mut frame_pcm) {
                }

                assert!(frame_pcm.planes() == 1); // samples converted to I16::Packed
                {
                    let mut samples_str = String::new();
                    let mut sample_iter = frame_pcm.data(0).iter();
                    // TODO: loop and handle borrowing...
                    let mut sample: u16 = 0;
                    {
                        if let Some(sample_byte) = sample_iter.next() {
                            // TODO: separate samples according to channel_layout
                            sample = *sample_byte as u16;
                        }
                    }

                    if let Some(sample_byte) = sample_iter.next() {
                        // TODO: validate this
                        sample += (*sample_byte as u16) << 8;
                        samples_str += &format!("{:x} ", sample)[..];
                    }
                    println!("Samples: {}", samples_str);
                }

                Ok(frame_pcm)
            },
            Err(error) => Err(error),
        }
    }

    fn draw(&self, cr: &cairo::Context) {
        let allocation = self.drawingarea.get_allocation();
        cr.scale(allocation.width as f64, allocation.height as f64);

        cr.select_font_face("Sans", FontSlant::Normal, FontWeight::Normal);
        cr.set_font_size(0.07);

        cr.move_to(0.1, 0.53);
        cr.show_text(&self.message);
    }
}

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 MediaNotifiable for AudioController {
    fn new_media(&mut self, context: &mut Context) {
        self.frame = None;
        self.graph = None;
        self.message = match context.audio_stream.as_mut() {
            Some(stream) => {
                self.set_index(stream.index);

                self.show();
                println!("\n** Audio stream\n{:?}", &stream);
                format!("audio stream {}", self.stream_index().unwrap())
            },
            None => {
                self.hide();
                "no audio stream".to_owned()
            },
        };

        self.drawingarea.queue_draw();
    }
}

impl PacketNotifiable for AudioController {
    fn new_packet(&mut self, stream: &ffmpeg::format::stream::Stream, packet: &ffmpeg::codec::packet::Packet) {
        self.print_packet_content(stream, packet);

        let decoder = stream.codec().decoder();
        match decoder.audio() {
            Ok(mut audio) => {
                let mut frame = ffmpeg::frame::Audio::empty();
                match audio.decode(packet, &mut frame) {
                    Ok(result) => if result {
                            let planes = frame.planes();
                            println!("\tdecoded audio frame, found {} planes", planes);
                            if planes > 0 {
                                for plane in 0..planes {
                                    println!("\tplane {}: data len: {}", plane, frame.data(plane).len());
                                }
                                match self.convert_to_pcm16(&audio, &mut frame) {
                                    Ok(frame_pcm) => self.frame = Some(frame_pcm),
                                    Err(error) =>  println!("\tError converting to pcm: {:?}", error),
                                }
                            }
                            else {
                                println!("\tno planes found in frame");
                            }
                        }
                        else {
                            println!("\tfailed to decode audio frame");
                        }
                    ,
                    Err(error) => println!("Error decoding audio: {:?}", error),
                }
            },
            Err(error) => println!("Error getting audio decoder: {:?}", error),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(asm)]

use std::env;

#[cfg(miri)]
compile_error!("`miri` cfg should not be set in build script");

fn not_in_miri() -> i32 {
    // Inline assembly definitely does not work in Miri.
    let mut dummy = 42;
    unsafe {
        asm!("/* {} */", in(reg) &mut dummy);
    }
    return dummy;
}

fn main() {
    not_in_miri();
    // Cargo calls `miri --print=cfg` to populate the `CARGO_CFG_*` env vars.
    // Make sure that the "miri" flag is set.
    assert!(env::var_os("CARGO_CFG_MIRI").is_some());
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-env-changed=MIRITESTVAR");
    println!("cargo:rustc-env=MIRITESTVAR=testval");
}
<commit_msg>looks like the asm macro is stable :D<commit_after>use std::env;

#[cfg(miri)]
compile_error!("`miri` cfg should not be set in build script");

fn not_in_miri() -> i32 {
    // Inline assembly definitely does not work in Miri.
    let mut dummy = 42;
    unsafe {
        std::arch::asm!("/* {} */", in(reg) &mut dummy);
    }
    return dummy;
}

fn main() {
    not_in_miri();
    // Cargo calls `miri --print=cfg` to populate the `CARGO_CFG_*` env vars.
    // Make sure that the "miri" flag is set.
    assert!(env::var_os("CARGO_CFG_MIRI").is_some());
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-env-changed=MIRITESTVAR");
    println!("cargo:rustc-env=MIRITESTVAR=testval");
}
<|endoftext|>


<commit_before>use super::Color;

use redox::fs::File;
use redox::io::Read;
use redox::String;
use redox::Vec;

// TODO: Follow naming convention
/// A bitmap
pub struct BmpFile {
    /// The bitmap width
    w: usize,
    /// The bitmap height
    h: usize,
    /// The data of the bitmap
    //data: Vec<[u8; 4]>,
    data: Vec<Color>,
}

impl BmpFile {
    /// Create a new bitmap
    pub fn new(width: usize, height: usize) -> Self {
        BmpFile {
            w: width,
            h: height,
            data: Vec::new(),
        }
    }

    /// Create a new empty bitmap
    pub fn default() -> Self {
        Self::new(0, 0)
    }

    /// Load bitmap from given path
    pub fn from_path(path: &str) -> Self {
        let mut data: Vec<u8> = Vec::new();
        if let Some(mut file) = File::open(path) {
            file.read_to_end(&mut data);
        }
        Self::from_data(&data)
    }

    /// Create a bitmap from some data
    pub fn from_data(file_data: &[u8]) -> Self {
        let get = |i: usize| -> u8 {
            match file_data.get(i) {
                Some(byte) => *byte,
                None => 0,
            }
        };

        let getw = |i: usize| -> u16 { (get(i) as u16) + ((get(i + 1) as u16) << 8) };

        let getd = |i: usize| -> u32 {
            (get(i) as u32) + ((get(i + 1) as u32) << 8) + ((get(i + 2) as u32) << 16) +
            ((get(i + 3) as u32) << 24)
        };

        let gets = |start: usize, len: usize| -> String {
            (start..start + len).map(|i| get(i) as char).collect::<String>()
        };

        let mut ret: BmpFile;

        if gets(0, 2) == "BM" {
            //let file_size = getd(2);
            let offset = getd(0xA);
            //let header_size = getd(0xE);
            let width = getd(0x12);
            let height = getd(0x16);
            let depth = getw(0x1C) as u32;

            let bytes = (depth + 7) / 8;
            let row_bytes = (depth * width + 31) / 32 * 4;

            let mut blue_mask = 0xFF;
            let mut green_mask = 0xFF00;
            let mut red_mask = 0xFF0000;
            let mut alpha_mask = 0xFF000000;
            if getd(0x1E) == 3 {
                red_mask = getd(0x36);
                green_mask = getd(0x3A);
                blue_mask = getd(0x3E);
                alpha_mask = getd(0x42);
            }

            let mut blue_shift = 0;
            while blue_mask > 0 && blue_shift < 32 && (blue_mask >> blue_shift) & 1 == 0 {
                blue_shift += 1;
            }

            let mut green_shift = 0;
            while green_mask > 0 && green_shift < 32 && (green_mask >> green_shift) & 1 == 0 {
                green_shift += 1;
            }

            let mut red_shift = 0;
            while red_mask > 0 && red_shift < 32 && (red_mask >> red_shift) & 1 == 0 {
                red_shift += 1;
            }

            let mut alpha_shift = 0;
            while alpha_mask > 0 && alpha_shift < 32 && (alpha_mask >> alpha_shift) & 1 == 0 {
                alpha_shift += 1;
            }

            ret = Self::new(width as usize, height as usize);

            for y in 0..height {
                for x in 0..width {
                    let pixel_offset = offset + (height - y - 1) * row_bytes + x * bytes;

                    let pixel_data = getd(pixel_offset as usize);
                    let red = ((pixel_data & red_mask) >> red_shift) as u8;
                    let green = ((pixel_data & green_mask) >> green_shift) as u8;
                    let blue = ((pixel_data & blue_mask) >> blue_shift) as u8;
                    let alpha = ((pixel_data & alpha_mask) >> alpha_shift) as u8;
                    if bytes == 3 {
                        ret.data.push(Color::rgb(red,green,blue));
                    } else if bytes == 4 {
                        ret.data.push(Color::rgba(red,green,blue,alpha));
                    }
                }
            }
        } else {
            ret = Self::default();
        }

        ret
    }

    /// Indicate if data exists
    pub fn has_data(&self) -> bool {
        !self.data.is_empty()
    }

    /// Convert to slice for drawing
    pub fn as_slice(&self) -> &[Color] {
        &self.data
    }

    pub fn width(&self) -> usize {
        self.w
    }

    pub fn height(&self) -> usize {
        self.h
    }
}
<commit_msg>Use `Deref` for `BmpFile` instead of `as_slice`<commit_after>use super::Color;

use redox::fs::File;
use redox::io::Read;
use redox::ops::Deref;
use redox::String;
use redox::Vec;

// TODO: Follow naming convention
/// A bitmap
pub struct BmpFile {
    /// The bitmap width
    w: usize,
    /// The bitmap height
    h: usize,
    /// The data of the bitmap
    //data: Vec<[u8; 4]>,
    data: Vec<Color>,
}

impl BmpFile {
    /// Create a new bitmap
    pub fn new(width: usize, height: usize) -> Self {
        BmpFile {
            w: width,
            h: height,
            data: Vec::new(),
        }
    }

    /// Create a new empty bitmap
    pub fn default() -> Self {
        Self::new(0, 0)
    }

    /// Load bitmap from given path
    pub fn from_path(path: &str) -> Self {
        let mut data: Vec<u8> = Vec::new();
        if let Some(mut file) = File::open(path) {
            file.read_to_end(&mut data);
        }
        Self::from_data(&data)
    }

    /// Create a bitmap from some data
    pub fn from_data(file_data: &[u8]) -> Self {
        let get = |i: usize| -> u8 {
            match file_data.get(i) {
                Some(byte) => *byte,
                None => 0,
            }
        };

        let getw = |i: usize| -> u16 { (get(i) as u16) + ((get(i + 1) as u16) << 8) };

        let getd = |i: usize| -> u32 {
            (get(i) as u32) + ((get(i + 1) as u32) << 8) + ((get(i + 2) as u32) << 16) +
            ((get(i + 3) as u32) << 24)
        };

        let gets = |start: usize, len: usize| -> String {
            (start..start + len).map(|i| get(i) as char).collect::<String>()
        };

        let mut ret: BmpFile;

        if gets(0, 2) == "BM" {
            //let file_size = getd(2);
            let offset = getd(0xA);
            //let header_size = getd(0xE);
            let width = getd(0x12);
            let height = getd(0x16);
            let depth = getw(0x1C) as u32;

            let bytes = (depth + 7) / 8;
            let row_bytes = (depth * width + 31) / 32 * 4;

            let mut blue_mask = 0xFF;
            let mut green_mask = 0xFF00;
            let mut red_mask = 0xFF0000;
            let mut alpha_mask = 0xFF000000;
            if getd(0x1E) == 3 {
                red_mask = getd(0x36);
                green_mask = getd(0x3A);
                blue_mask = getd(0x3E);
                alpha_mask = getd(0x42);
            }

            let mut blue_shift = 0;
            while blue_mask > 0 && blue_shift < 32 && (blue_mask >> blue_shift) & 1 == 0 {
                blue_shift += 1;
            }

            let mut green_shift = 0;
            while green_mask > 0 && green_shift < 32 && (green_mask >> green_shift) & 1 == 0 {
                green_shift += 1;
            }

            let mut red_shift = 0;
            while red_mask > 0 && red_shift < 32 && (red_mask >> red_shift) & 1 == 0 {
                red_shift += 1;
            }

            let mut alpha_shift = 0;
            while alpha_mask > 0 && alpha_shift < 32 && (alpha_mask >> alpha_shift) & 1 == 0 {
                alpha_shift += 1;
            }

            ret = Self::new(width as usize, height as usize);

            for y in 0..height {
                for x in 0..width {
                    let pixel_offset = offset + (height - y - 1) * row_bytes + x * bytes;

                    let pixel_data = getd(pixel_offset as usize);
                    let red = ((pixel_data & red_mask) >> red_shift) as u8;
                    let green = ((pixel_data & green_mask) >> green_shift) as u8;
                    let blue = ((pixel_data & blue_mask) >> blue_shift) as u8;
                    let alpha = ((pixel_data & alpha_mask) >> alpha_shift) as u8;
                    if bytes == 3 {
                        ret.data.push(Color::rgb(red,green,blue));
                    } else if bytes == 4 {
                        ret.data.push(Color::rgba(red,green,blue,alpha));
                    }
                }
            }
        } else {
            ret = Self::default();
        }

        ret
    }

    /// Indicate if data exists
    pub fn has_data(&self) -> bool {
        !self.data.is_empty()
    }

    pub fn width(&self) -> usize {
        self.w
    }

    pub fn height(&self) -> usize {
        self.h
    }
}

impl Deref for BmpFile {
    type Target = [Color];

    /// Convert to slice for drawing
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}
<|endoftext|>


<commit_before>use std::fmt::Debug;
use std::convert::TryFrom;
use regex_syntax;
use cripes::pattern::{Atom, Element};

fn from_regex<T: Atom>(s: &str) -> Element<T>
    where Element<T>: TryFrom<regex_syntax::Expr>,
          <Element<T> as TryFrom<regex_syntax::Expr>>::Err: Debug
{
    TryFrom::try_from(regex_syntax::Expr::parse(s).unwrap()).unwrap()
}

macro_rules! extract_variant {
    ((ref $input: expr) -> $variant: ident) => ( match $input {
        &Element::$variant(ref x) => x,
        _ => unreachable!() });

    (($input: expr) -> $variant: ident) => ( match $input {
        Element::$variant(x) => x,
        _ => unreachable!() });
}

/// Helper for parsing a regex and returning the inner pattern type.
macro_rules! from_regex {
    ($s: tt -> Element<$T: ty>) => ( from_regex::<$T>($s) );
    ($s: tt -> $which: ident < $T: ty>) => (extract_variant!((from_regex::<$T>($s)) -> $which));
}

mod sequence {
    use std::iter::FromIterator;
    use cripes::pattern::{Class, Element, Sequence, Union};
    use cripes::util::set::IsSubsetOf;

    #[test]
    fn test_is_subset_of() {
        let unions = (&["abc", "def", "ghi"]).iter().map(|s| Union::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        let classes = (&["abc", "def", "ghi"]).iter().map(|s| Class::<char>::from_iter(s.chars())).collect::<Vec<_>>();
        let seqs = (&["adg", "beh", "cfi"]).iter().map(|s| Sequence::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        let useq = Sequence::from_iter(unions.iter().map(|u| Element::Union(u.clone())));
        let cseq = Sequence::from_iter(classes.iter().map(|c| Element::Class(c.clone())));

        // Every sequence should be a subset of itself.
        assert!(useq.is_subset_of(&useq));
        assert!(cseq.is_subset_of(&cseq));

        // `useq` and `cseq` should be subsets of each other.
        assert!(useq.is_subset_of(&cseq));
        assert!(cseq.is_subset_of(&useq));

        // each sequence in `seqs` should be a subset of both `useq` and
        // `cseq`.
        for seq in seqs.iter() {
            for i in 0..3 {
                assert!(seq[i].is_subset_of(&cseq[i]));
                assert!(seq[i].is_subset_of(&useq[i]));
            }

            assert!(seq.is_subset_of(&cseq));
            assert!(seq.is_subset_of(&useq));
        }
    }
}

mod union {
    use super::from_regex;
    use std::iter::FromIterator;
    use cripes::pattern::{Element, Union, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf};

    #[test]
    fn test_contains() {
        let u = Union::from_iter("abc".chars().map(|c| c.into()));
        assert!(u.contains('a'));
        assert!(u.contains('b'));
        assert!(u.contains('c'));
    }

    #[test]
    fn test_is_subset_of_union() {
        let u1 = Union::<char>::from_iter("abc".chars().map(|c| c.into()));
        let u2 = Union::from_iter("abcdef".chars().map(|c| c.into()));
        assert!(u1.is_subset_of(&u2));
        assert!(! u2.is_subset_of(&u1));

        assert!(u1.is_subset_of(&Wildcard));
        assert!(u2.is_subset_of(&Wildcard));
    }
    #[test]
    fn test_is_subset_of_sequence() {
        let seq_of_classes = from_regex!("[abc][def][ghi]" -> Sequence<char>);
        let seq_of_unions = from_regex!("(a|b|c)(d|e|f)(g|h|i)" -> Sequence<char>);

        let union_of_seqs = from_regex!("adg|beh|cfi" -> Union<char>);
        println!("{:?}", union_of_seqs);
        assert_eq!(3, union_of_seqs.len());

        // check that the union would match each contained sequence.
        for seq in union_of_seqs.iter() {
            println!("{:?}", seq);
            assert!(seq.is_subset_of(&union_of_seqs));

            for i in 0..3 {
                let s = extract_variant!((ref seq) -> Sequence);
                println!("{:?} <=> {:?}", s[i], seq_of_unions[i]);
                assert!(s[i].is_subset_of(&seq_of_unions[i]));
                assert!(s[i].is_subset_of(&seq_of_classes[i]));
            }

            assert!(seq.is_subset_of(&seq_of_classes));
            assert!(seq.is_subset_of(&seq_of_unions));
        }

        assert!(union_of_seqs.is_subset_of(&seq_of_classes));
        assert!(union_of_seqs.is_subset_of(&seq_of_unions));

        assert!(! seq_of_classes.is_subset_of(&union_of_seqs));
        assert!(! seq_of_unions.is_subset_of(&union_of_seqs));

    }
}

mod wildcard {
    use std::{char, u8};
    use std::iter::FromIterator;
    use char_iter;
    use cripes::pattern::{ByteOrChar, Class, Element, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_is_subset_of() {
        let wild = Wildcard::<char>;
        assert!(wild.is_superset_of(&'a'));
        assert!(wild.is_superset_of(&Element::Atom('a')));
        assert!(!wild.is_subset_of(&Element::Atom('a')));
        assert!(wild.is_subset_of(&Wildcard));

        assert!(ByteOrChar::Byte(b'H').is_subset_of(&Wildcard));

        let cls = Class::from_iter("xyz".chars());
        assert!(wild.is_superset_of(&cls));
    }

    #[test]
    fn test_contains() {
        let wildc = Wildcard::<char>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildc.contains(c));
        }

        let wildbc = Wildcard::<ByteOrChar>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildbc.contains(c.into()));
        }
        for b in 0...u8::MAX {
            assert!(wildbc.contains(b.into()));
        }
    }
}

mod class {
    use std::char;
    use std::iter::FromIterator;

    use char_iter;
    use cripes::pattern::{Class, ClassMember, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_contains() {
        let range = ClassMember::Range('a', 'z');
        panic_unless_eq!(26, range.len());
        assert!(range.contains('a'));
        assert!(range.contains('z'));
        assert!(! range.contains('A'));
        assert!(! range.contains('Z'));

        let atom = ClassMember::Atom('X');
        panic_unless_eq!(1, atom.len());
        assert!(atom.contains('X'));
        assert!(! atom.contains('x'));

        let class_from_members = Class::from_members([range, atom].into_iter().cloned());
        assert!(class_from_members.contains(&range));
        assert!(class_from_members.contains(&atom));
        panic_unless_eq!(27, class_from_members.len());
        for c in char_iter::new('a', 'z') {
            assert!(class_from_members.contains(c));
        }
        for c in char_iter::new('A', 'W') {
            assert!(! class_from_members.contains(c));
        }
        assert!(class_from_members.contains('X'));
        for c in char_iter::new('Y', 'Z') {
            assert!(! class_from_members.contains(c));
        }

        let cls = Class::from_iter(['a', 'b', 'c'].into_iter().cloned());
        assert_eq!(3, cls.len());

        // contains
        assert!(cls.contains('a'));
        assert!(cls.contains('b'));
        assert!(cls.contains('c'));
        for c in char_iter::new(0 as char, char::MAX) {
            if c == 'a' || c == 'b' || c == 'c' {
                assert!(cls.contains(c));
            } else {
                assert!(!cls.contains(c))
            }
        }
    }

    #[test]
    fn test_is_subset_of() {
        let cls1: Class<char> = FromIterator::from_iter(['x', 'y', 'z'].into_iter().cloned());
        assert!(Wildcard.is_superset_of(&cls1));

        let cls2: Class<char> = FromIterator::from_iter(['x', 'y'].into_iter().cloned());
        assert!(cls2.is_subset_of(&cls1));
    }
}
<commit_msg>pattern: add simple test for Sequence::reduce<commit_after>use std::fmt::Debug;
use std::convert::TryFrom;
use regex_syntax;
use cripes::pattern::{Atom, Element};

fn from_regex<T: Atom>(s: &str) -> Element<T>
    where Element<T>: TryFrom<regex_syntax::Expr>,
          <Element<T> as TryFrom<regex_syntax::Expr>>::Err: Debug
{
    TryFrom::try_from(regex_syntax::Expr::parse(s).unwrap()).unwrap()
}

macro_rules! extract_variant {
    ((ref $input: expr) -> $variant: ident) => ( match $input {
        &Element::$variant(ref x) => x,
        _ => unreachable!() });

    (($input: expr) -> $variant: ident) => ( match $input {
        Element::$variant(x) => x,
        _ => unreachable!() });
}

/// Helper for parsing a regex and returning the inner pattern type.
macro_rules! from_regex {
    ($s: tt -> Element<$T: ty>) => ( from_regex::<$T>($s) );
    ($s: tt -> $which: ident < $T: ty>) => (extract_variant!((from_regex::<$T>($s)) -> $which));
}

mod sequence {
    use std::iter::{self, FromIterator};
    use cripes::pattern::{Class, Element, Sequence, Union};
    use cripes::pattern::Reduce;
    use cripes::util::set::IsSubsetOf;

    #[test]
    fn test_reduce() {
        let seq_of_seqs = Sequence::from_iter(["abc", "def", "ghi"].iter().map(|s| Sequence::from_iter(s.chars().map(|c| c.into())).into()));
        let flat_seq = Sequence::<char>::from_iter("abcdefghi".chars().map(|c| c.into()));

        assert!(seq_of_seqs != flat_seq);
        assert_eq!(Some(Element::Sequence(flat_seq)), seq_of_seqs.reduce());

        // Empty sequences should reduce to `None`
        assert_eq!(None, Sequence::<char>::from_iter(iter::empty()).reduce());

        // Unit-length sequences should reduce to their only element.
        assert_eq!(Some(Element::Wildcard), Sequence::<char>::new(vec![Element::Wildcard]).reduce());
    }

    #[test]
    fn test_is_subset_of() {
        let unions = (&["abc", "def", "ghi"]).iter().map(|s| Union::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        let classes = (&["abc", "def", "ghi"]).iter().map(|s| Class::<char>::from_iter(s.chars())).collect::<Vec<_>>();
        let seqs = (&["adg", "beh", "cfi"]).iter().map(|s| Sequence::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        let useq = Sequence::from_iter(unions.iter().map(|u| Element::Union(u.clone())));
        let cseq = Sequence::from_iter(classes.iter().map(|c| Element::Class(c.clone())));

        // Every sequence should be a subset of itself.
        assert!(useq.is_subset_of(&useq));
        assert!(cseq.is_subset_of(&cseq));

        // `useq` and `cseq` should be subsets of each other.
        assert!(useq.is_subset_of(&cseq));
        assert!(cseq.is_subset_of(&useq));

        // each sequence in `seqs` should be a subset of both `useq` and
        // `cseq`.
        for seq in seqs.iter() {
            for i in 0..3 {
                assert!(seq[i].is_subset_of(&cseq[i]));
                assert!(seq[i].is_subset_of(&useq[i]));
            }

            assert!(seq.is_subset_of(&cseq));
            assert!(seq.is_subset_of(&useq));
        }
    }
}

mod union {
    use super::from_regex;
    use std::iter::FromIterator;
    use cripes::pattern::{Element, Union, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf};

    #[test]
    fn test_contains() {
        let u = Union::from_iter("abc".chars().map(|c| c.into()));
        assert!(u.contains('a'));
        assert!(u.contains('b'));
        assert!(u.contains('c'));
    }

    #[test]
    fn test_is_subset_of_union() {
        let u1 = Union::<char>::from_iter("abc".chars().map(|c| c.into()));
        let u2 = Union::from_iter("abcdef".chars().map(|c| c.into()));
        assert!(u1.is_subset_of(&u2));
        assert!(! u2.is_subset_of(&u1));

        assert!(u1.is_subset_of(&Wildcard));
        assert!(u2.is_subset_of(&Wildcard));
    }
    #[test]
    fn test_is_subset_of_sequence() {
        let seq_of_classes = from_regex!("[abc][def][ghi]" -> Sequence<char>);
        let seq_of_unions = from_regex!("(a|b|c)(d|e|f)(g|h|i)" -> Sequence<char>);

        let union_of_seqs = from_regex!("adg|beh|cfi" -> Union<char>);
        println!("{:?}", union_of_seqs);
        assert_eq!(3, union_of_seqs.len());

        // check that the union would match each contained sequence.
        for seq in union_of_seqs.iter() {
            println!("{:?}", seq);
            assert!(seq.is_subset_of(&union_of_seqs));

            for i in 0..3 {
                let s = extract_variant!((ref seq) -> Sequence);
                println!("{:?} <=> {:?}", s[i], seq_of_unions[i]);
                assert!(s[i].is_subset_of(&seq_of_unions[i]));
                assert!(s[i].is_subset_of(&seq_of_classes[i]));
            }

            assert!(seq.is_subset_of(&seq_of_classes));
            assert!(seq.is_subset_of(&seq_of_unions));
        }

        assert!(union_of_seqs.is_subset_of(&seq_of_classes));
        assert!(union_of_seqs.is_subset_of(&seq_of_unions));

        assert!(! seq_of_classes.is_subset_of(&union_of_seqs));
        assert!(! seq_of_unions.is_subset_of(&union_of_seqs));

    }
}

mod wildcard {
    use std::{char, u8};
    use std::iter::FromIterator;
    use char_iter;
    use cripes::pattern::{ByteOrChar, Class, Element, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_is_subset_of() {
        let wild = Wildcard::<char>;
        assert!(wild.is_superset_of(&'a'));
        assert!(wild.is_superset_of(&Element::Atom('a')));
        assert!(!wild.is_subset_of(&Element::Atom('a')));
        assert!(wild.is_subset_of(&Wildcard));

        assert!(ByteOrChar::Byte(b'H').is_subset_of(&Wildcard));

        let cls = Class::from_iter("xyz".chars());
        assert!(wild.is_superset_of(&cls));
    }

    #[test]
    fn test_contains() {
        let wildc = Wildcard::<char>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildc.contains(c));
        }

        let wildbc = Wildcard::<ByteOrChar>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildbc.contains(c.into()));
        }
        for b in 0...u8::MAX {
            assert!(wildbc.contains(b.into()));
        }
    }
}

mod class {
    use std::char;
    use std::iter::FromIterator;

    use char_iter;
    use cripes::pattern::{Class, ClassMember, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_contains() {
        let range = ClassMember::Range('a', 'z');
        panic_unless_eq!(26, range.len());
        assert!(range.contains('a'));
        assert!(range.contains('z'));
        assert!(! range.contains('A'));
        assert!(! range.contains('Z'));

        let atom = ClassMember::Atom('X');
        panic_unless_eq!(1, atom.len());
        assert!(atom.contains('X'));
        assert!(! atom.contains('x'));

        let class_from_members = Class::from_members([range, atom].into_iter().cloned());
        assert!(class_from_members.contains(&range));
        assert!(class_from_members.contains(&atom));
        panic_unless_eq!(27, class_from_members.len());
        for c in char_iter::new('a', 'z') {
            assert!(class_from_members.contains(c));
        }
        for c in char_iter::new('A', 'W') {
            assert!(! class_from_members.contains(c));
        }
        assert!(class_from_members.contains('X'));
        for c in char_iter::new('Y', 'Z') {
            assert!(! class_from_members.contains(c));
        }

        let cls = Class::from_iter(['a', 'b', 'c'].into_iter().cloned());
        assert_eq!(3, cls.len());

        // contains
        assert!(cls.contains('a'));
        assert!(cls.contains('b'));
        assert!(cls.contains('c'));
        for c in char_iter::new(0 as char, char::MAX) {
            if c == 'a' || c == 'b' || c == 'c' {
                assert!(cls.contains(c));
            } else {
                assert!(!cls.contains(c))
            }
        }
    }

    #[test]
    fn test_is_subset_of() {
        let cls1: Class<char> = FromIterator::from_iter(['x', 'y', 'z'].into_iter().cloned());
        assert!(Wildcard.is_superset_of(&cls1));

        let cls2: Class<char> = FromIterator::from_iter(['x', 'y'].into_iter().cloned());
        assert!(cls2.is_subset_of(&cls1));
    }
}
<|endoftext|>


<commit_before>use std::{
    collections::BTreeMap,
    convert::TryInto as _,
    env, fmt, fs,
    path::{Path, PathBuf},
    str::FromStr,
};

use chrono::{Datelike as _, Month, TimeZone as _, Utc};
use glob::glob;
use regex::Regex;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Date {
    year: u32,
    month: u32,
}

impl Date {
    fn months_since(self, other: Date) -> Option<u32> {
        let self_chrono = Utc.ymd(self.year.try_into().unwrap(), self.month, 1);
        let other_chrono = Utc.ymd(other.year.try_into().unwrap(), other.month, 1);
        let duration_since = self_chrono.signed_duration_since(other_chrono);
        let months_since = duration_since.num_days() / 30;
        if months_since < 0 {
            None
        } else {
            Some(months_since.try_into().unwrap())
        }
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:04}-{:02}", self.year, self.month)
    }
}

fn make_date_regex() -> Regex {
    Regex::new(
        r"(?x) # insignificant whitespace mode
        (<!--\s*
          date-check:\s*
          (?P<m1>\D+)\s+
          (?P<y1>\d{4})\s*-->
        )
        |
        (<!--\s*
          date-check\s*-->\s+
          (?P<m2>\D+)\s+
          (?P<y2>\d{4})\b
        )
    ",
    )
    .unwrap()
}

fn collect_dates_from_file(date_regex: &Regex, text: &str) -> Vec<(usize, Date)> {
    let mut line = 1;
    let mut end_of_last_cap = 0;
    date_regex
        .captures_iter(text)
        .filter_map(|cap| {
            if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = (
                cap.name("m1"),
                cap.name("y1"),
                cap.name("m2"),
                cap.name("y2"),
            ) {
                let year = year.as_str().parse().expect("year");
                let month = Month::from_str(month.as_str())
                    .expect("month")
                    .number_from_month();
                Some((cap.get(0).expect("all").range(), Date { year, month }))
            } else {
                None
            }
        })
        .map(|(byte_range, date)| {
            line += text[end_of_last_cap..byte_range.end]
                .chars()
                .filter(|c| *c == '\n')
                .count();
            end_of_last_cap = byte_range.end;
            (line, date)
        })
        .collect()
}

fn collect_dates(paths: impl Iterator<Item = PathBuf>) -> BTreeMap<PathBuf, Vec<(usize, Date)>> {
    let date_regex = make_date_regex();
    let mut data = BTreeMap::new();
    for path in paths {
        let text = fs::read_to_string(&path).unwrap();
        let dates = collect_dates_from_file(&date_regex, &text);
        if !dates.is_empty() {
            data.insert(path, dates);
        }
    }
    data
}

fn filter_dates(
    current_month: Date,
    min_months_since: u32,
    dates_by_file: impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)>,
) -> impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)> {
    dates_by_file
        .map(move |(path, dates)| {
            (
                path,
                dates
                    .into_iter()
                    .filter(|(_, date)| {
                        current_month
                            .months_since(*date)
                            .expect("found date that is after current month")
                            >= min_months_since
                    })
                    .collect::<Vec<_>>(),
            )
        })
        .filter(|(_, dates)| !dates.is_empty())
}

fn main() {
    let root_dir = env::args()
        .nth(1)
        .expect("expect root Markdown directory as CLI argument");
    let root_dir_path = Path::new(&root_dir);
    let glob_pat = format!("{}/**/*.md", root_dir);
    let today_chrono = Utc::today();
    let current_month = Date {
        year: today_chrono.year_ce().1,
        month: today_chrono.month(),
    };

    let dates_by_file = collect_dates(glob(&glob_pat).unwrap().map(Result::unwrap));
    let dates_by_file: BTreeMap<_, _> =
        filter_dates(current_month, 6, dates_by_file.into_iter()).collect();

    if dates_by_file.is_empty() {
        println!("empty");
    } else {
        println!("Date Reference Triage for {}", current_month);
        println!("## Procedure");
        println!();
        println!(
            "Each of these dates should be checked to see if the docs they annotate are \
             up-to-date. Each date should be updated (in the Markdown file where it appears) to \
             use the current month ({current_month}), or removed if the docs it annotates are not \
             expected to fall out of date quickly.",
            current_month = current_month
        );
        println!();
        println!(
            "Please check off each date once a PR to update it (and, if applicable, its \
             surrounding docs) has been merged. Please also mention that you are working on a \
             particular set of dates so duplicate work is avoided."
        );
        println!();
        println!("Finally, once all the dates have been updated, please close this issue.");
        println!();
        println!("## Dates");
        println!();

        for (path, dates) in dates_by_file {
            println!(
                "- [ ] {}",
                path.strip_prefix(&root_dir_path).unwrap().display()
            );
            for (line, date) in dates {
                println!("  - [ ] line {}: {}", line, date);
            }
        }
        println!();
    }
}

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

    #[test]
    fn test_months_since() {
        let date1 = Date {
            year: 2020,
            month: 3,
        };
        let date2 = Date {
            year: 2021,
            month: 1,
        };
        assert_eq!(date2.months_since(date1), Some(10));
    }

    #[test]
    fn test_date_regex() {
        let regex = &make_date_regex();
        assert!(regex.is_match("<!-- date-check: jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: january 2021 -->"));
        assert!(regex.is_match("<!-- date-check: Jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: January 2021 -->"));
        assert!(regex.is_match("<!-- date-check --> jan 2021"));
        assert!(regex.is_match("<!-- date-check --> january 2021"));
        assert!(regex.is_match("<!-- date-check --> Jan 2021"));
        assert!(regex.is_match("<!-- date-check --> January 2021"));

        assert!(regex.is_match("<!-- date-check --> jan 2021 "));
        assert!(regex.is_match("<!-- date-check --> jan 2021."));
    }

    #[test]
    fn test_date_regex_fail() {
        let regexes = &make_date_regex();
        assert!(!regexes.is_match("<!-- date-check: jan 221 -->"));
        assert!(!regexes.is_match("<!-- date-check: jan 20221 -->"));
        assert!(!regexes.is_match("<!-- date-check: 01 2021 -->"));
        assert!(!regexes.is_match("<!-- date-check --> jan 221"));
        assert!(!regexes.is_match("<!-- date-check --> jan 20222"));
        assert!(!regexes.is_match("<!-- date-check --> 01 2021"));
    }

    #[test]
    fn test_collect_dates_from_file() {
        let text = r"
Test1
<!-- date-check: jan 2021 -->
Test2
Foo<!-- date-check: february 2021
-->
Test3
Test4
Foo<!-- date-check: Mar 2021 -->Bar
<!-- date-check:April 2021
-->
Test5
Test6
Test7
<!-- date-check:

may 2021 -->
Test8
Test1
<!-- date-check -->  jan 2021
Test2
Foo<!-- date-check
--> february 2021
Test3
Test4
Foo<!-- date-check -->  mar 2021 Bar
<!-- date-check
--> apr 2021
Test5
Test6
Test7
<!-- date-check

 --> may 2021
Test8
 <!--
   date-check
 --> june 2021.
        ";
        assert_eq!(
            collect_dates_from_file(&make_date_regex(), text),
            vec![
                (
                    3,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    6,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    9,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    11,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    17,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    20,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    23,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    26,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    28,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    34,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    38,
                    Date {
                        year: 2021,
                        month: 6,
                    }
                ),
            ],
        );
    }
}
<commit_msg>date-check: be more strict<commit_after>use std::{
    collections::BTreeMap,
    convert::TryInto as _,
    env, fmt, fs,
    path::{Path, PathBuf},
    str::FromStr,
};

use chrono::{Datelike as _, Month, TimeZone as _, Utc};
use glob::glob;
use regex::Regex;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Date {
    year: u32,
    month: u32,
}

impl Date {
    fn months_since(self, other: Date) -> Option<u32> {
        let self_chrono = Utc.ymd(self.year.try_into().unwrap(), self.month, 1);
        let other_chrono = Utc.ymd(other.year.try_into().unwrap(), other.month, 1);
        let duration_since = self_chrono.signed_duration_since(other_chrono);
        let months_since = duration_since.num_days() / 30;
        if months_since < 0 {
            None
        } else {
            Some(months_since.try_into().unwrap())
        }
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:04}-{:02}", self.year, self.month)
    }
}

fn make_date_regex() -> Regex {
    Regex::new(
        r"(?x) # insignificant whitespace mode
        (<!--\s*
          date-check:\s*
          (?P<m1>[[:alpha:]]+)\s+
          (?P<y1>\d{4})\s*-->
        )
        |
        (<!--\s*
          date-check\s*-->\s+
          (?P<m2>[[:alpha:]]+)\s+
          (?P<y2>\d{4})\b
        )
    ",
    )
    .unwrap()
}

fn collect_dates_from_file(date_regex: &Regex, text: &str) -> Vec<(usize, Date)> {
    let mut line = 1;
    let mut end_of_last_cap = 0;
    date_regex
        .captures_iter(text)
        .filter_map(|cap| {
            if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = (
                cap.name("m1"),
                cap.name("y1"),
                cap.name("m2"),
                cap.name("y2"),
            ) {
                let year = year.as_str().parse().expect("year");
                let month = Month::from_str(month.as_str())
                    .expect("month")
                    .number_from_month();
                Some((cap.get(0).expect("all").range(), Date { year, month }))
            } else {
                None
            }
        })
        .map(|(byte_range, date)| {
            line += text[end_of_last_cap..byte_range.end]
                .chars()
                .filter(|c| *c == '\n')
                .count();
            end_of_last_cap = byte_range.end;
            (line, date)
        })
        .collect()
}

fn collect_dates(paths: impl Iterator<Item = PathBuf>) -> BTreeMap<PathBuf, Vec<(usize, Date)>> {
    let date_regex = make_date_regex();
    let mut data = BTreeMap::new();
    for path in paths {
        let text = fs::read_to_string(&path).unwrap();
        let dates = collect_dates_from_file(&date_regex, &text);
        if !dates.is_empty() {
            data.insert(path, dates);
        }
    }
    data
}

fn filter_dates(
    current_month: Date,
    min_months_since: u32,
    dates_by_file: impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)>,
) -> impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)> {
    dates_by_file
        .map(move |(path, dates)| {
            (
                path,
                dates
                    .into_iter()
                    .filter(|(_, date)| {
                        current_month
                            .months_since(*date)
                            .expect("found date that is after current month")
                            >= min_months_since
                    })
                    .collect::<Vec<_>>(),
            )
        })
        .filter(|(_, dates)| !dates.is_empty())
}

fn main() {
    let root_dir = env::args()
        .nth(1)
        .expect("expect root Markdown directory as CLI argument");
    let root_dir_path = Path::new(&root_dir);
    let glob_pat = format!("{}/**/*.md", root_dir);
    let today_chrono = Utc::today();
    let current_month = Date {
        year: today_chrono.year_ce().1,
        month: today_chrono.month(),
    };

    let dates_by_file = collect_dates(glob(&glob_pat).unwrap().map(Result::unwrap));
    let dates_by_file: BTreeMap<_, _> =
        filter_dates(current_month, 6, dates_by_file.into_iter()).collect();

    if dates_by_file.is_empty() {
        println!("empty");
    } else {
        println!("Date Reference Triage for {}", current_month);
        println!("## Procedure");
        println!();
        println!(
            "Each of these dates should be checked to see if the docs they annotate are \
             up-to-date. Each date should be updated (in the Markdown file where it appears) to \
             use the current month ({current_month}), or removed if the docs it annotates are not \
             expected to fall out of date quickly.",
            current_month = current_month
        );
        println!();
        println!(
            "Please check off each date once a PR to update it (and, if applicable, its \
             surrounding docs) has been merged. Please also mention that you are working on a \
             particular set of dates so duplicate work is avoided."
        );
        println!();
        println!("Finally, once all the dates have been updated, please close this issue.");
        println!();
        println!("## Dates");
        println!();

        for (path, dates) in dates_by_file {
            println!(
                "- [ ] {}",
                path.strip_prefix(&root_dir_path).unwrap().display()
            );
            for (line, date) in dates {
                println!("  - [ ] line {}: {}", line, date);
            }
        }
        println!();
    }
}

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

    #[test]
    fn test_months_since() {
        let date1 = Date {
            year: 2020,
            month: 3,
        };
        let date2 = Date {
            year: 2021,
            month: 1,
        };
        assert_eq!(date2.months_since(date1), Some(10));
    }

    #[test]
    fn test_date_regex() {
        let regex = &make_date_regex();
        assert!(regex.is_match("<!-- date-check: jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: january 2021 -->"));
        assert!(regex.is_match("<!-- date-check: Jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: January 2021 -->"));
        assert!(regex.is_match("<!-- date-check --> jan 2021"));
        assert!(regex.is_match("<!-- date-check --> january 2021"));
        assert!(regex.is_match("<!-- date-check --> Jan 2021"));
        assert!(regex.is_match("<!-- date-check --> January 2021"));

        assert!(regex.is_match("<!-- date-check --> jan 2021 "));
        assert!(regex.is_match("<!-- date-check --> jan 2021."));
    }

    #[test]
    fn test_date_regex_fail() {
        let regexes = &make_date_regex();
        assert!(!regexes.is_match("<!-- date-check: jan 221 -->"));
        assert!(!regexes.is_match("<!-- date-check: jan 20221 -->"));
        assert!(!regexes.is_match("<!-- date-check: 01 2021 -->"));
        assert!(!regexes.is_match("<!-- date-check --> jan 221"));
        assert!(!regexes.is_match("<!-- date-check --> jan 20222"));
        assert!(!regexes.is_match("<!-- date-check --> 01 2021"));
    }

    #[test]
    fn test_collect_dates_from_file() {
        let text = r"
Test1
<!-- date-check: jan 2021 -->
Test2
Foo<!-- date-check: february 2021
-->
Test3
Test4
Foo<!-- date-check: Mar 2021 -->Bar
<!-- date-check:April 2021
-->
Test5
Test6
Test7
<!-- date-check:

may 2021 -->
Test8
Test1
<!-- date-check -->  jan 2021
Test2
Foo<!-- date-check
--> february 2021
Test3
Test4
Foo<!-- date-check -->  mar 2021 Bar
<!-- date-check
--> apr 2021
Test5
Test6
Test7
<!-- date-check

 --> may 2021
Test8
 <!--
   date-check
 --> june 2021.
        ";
        assert_eq!(
            collect_dates_from_file(&make_date_regex(), text),
            vec![
                (
                    3,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    6,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    9,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    11,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    17,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    20,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    23,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    26,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    28,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    34,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    38,
                    Date {
                        year: 2021,
                        month: 6,
                    }
                ),
            ],
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Mark Lee
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Idiomatic Rust wrapper for `CommonCrypto`'s `CCDigestCtx` struct.

use commoncrypto_sys::{
    CCDigestCreate, CCDigestCtx, CCDigestDestroy, CCDigestFinal, CCDigestGetOutputSizeFromRef,
    CCDigestReset, CCDigestUpdate,
};
use std::io;

pub use commoncrypto_sys::CCDigestAlgorithm;

const MAX_DIGEST_SIZE: usize = 64;

macro_rules! err_from_ccdigest_retval {
    ($func_name: expr, $val: expr) => {
        Err(io::Error::new(
            io::ErrorKind::Other,
            format!("{} returned nonzero: {}", $func_name, $val),
        ))
    };
}

#[derive(PartialEq, Copy, Clone, Debug)]
enum State {
    Reset,
    Updated,
    Finalized,
}

/// Generates cryptographic hashes.
#[derive(Debug)]
pub struct Hasher {
    ctx: *mut CCDigestCtx,
    state: State,
}

impl Hasher {
    /// Creates a new `Hasher` which will use the given cryptographic `algorithm`.
    pub fn new(algorithm: CCDigestAlgorithm) -> Hasher {
        let ctx: *mut CCDigestCtx;
        unsafe {
            ctx = CCDigestCreate(algorithm);
        }
        Hasher {
            ctx: ctx,
            state: State::Reset,
        }
    }

    fn init(&mut self) {
        match self.state {
            State::Reset => return,
            State::Updated => {
                let _ = self.finish();
            }
            State::Finalized => (),
        }
        unsafe { CCDigestReset(self.ctx) };
        self.state = State::Reset;
    }

    /// Feeds data into the hasher.
    pub fn update(&mut self, data: &[u8]) -> io::Result<usize> {
        if self.state == State::Finalized {
            self.init();
        }
        let result = unsafe { CCDigestUpdate(self.ctx, data.as_ptr() as *mut _, data.len()) };
        if result == 0 {
            self.state = State::Updated;
            Ok(data.len())
        } else {
            err_from_ccdigest_retval!("CCDigestCreate", result)
        }
    }

    /// Finalizes digest operations and produces the digest output.
    pub fn finish(&mut self) -> io::Result<Vec<u8>> {
        if self.state == State::Finalized {
            self.init();
        }
        let expected_len = unsafe { CCDigestGetOutputSizeFromRef(self.ctx) };
        let mut md = vec![0; MAX_DIGEST_SIZE];
        let result = unsafe { CCDigestFinal(self.ctx, md.as_mut_ptr()) };
        if result == 0 {
            self.state = State::Finalized;
            md.truncate(expected_len);
            Ok(md)
        } else {
            err_from_ccdigest_retval!("CCDigestFinal", result)
        }
    }
}

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

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

impl Drop for Hasher {
    fn drop(&mut self) {
        if self.state != State::Finalized {
            let _ = self.finish();
        }
        unsafe { CCDigestDestroy(self.ctx) }
    }
}
<commit_msg>chore: fix clippy warning<commit_after>// Copyright (c) 2016 Mark Lee
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Idiomatic Rust wrapper for `CommonCrypto`'s `CCDigestCtx` struct.

use commoncrypto_sys::{
    CCDigestCreate, CCDigestCtx, CCDigestDestroy, CCDigestFinal, CCDigestGetOutputSizeFromRef,
    CCDigestReset, CCDigestUpdate,
};
use std::io;

pub use commoncrypto_sys::CCDigestAlgorithm;

const MAX_DIGEST_SIZE: usize = 64;

macro_rules! err_from_ccdigest_retval {
    ($func_name: expr, $val: expr) => {
        Err(io::Error::new(
            io::ErrorKind::Other,
            format!("{} returned nonzero: {}", $func_name, $val),
        ))
    };
}

#[derive(PartialEq, Copy, Clone, Debug)]
enum State {
    Reset,
    Updated,
    Finalized,
}

/// Generates cryptographic hashes.
#[derive(Debug)]
pub struct Hasher {
    ctx: *mut CCDigestCtx,
    state: State,
}

impl Hasher {
    /// Creates a new `Hasher` which will use the given cryptographic `algorithm`.
    pub fn new(algorithm: CCDigestAlgorithm) -> Hasher {
        let ctx: *mut CCDigestCtx;
        unsafe {
            ctx = CCDigestCreate(algorithm);
        }
        Hasher {
            ctx,
            state: State::Reset,
        }
    }

    fn init(&mut self) {
        match self.state {
            State::Reset => return,
            State::Updated => {
                let _ = self.finish();
            }
            State::Finalized => (),
        }
        unsafe { CCDigestReset(self.ctx) };
        self.state = State::Reset;
    }

    /// Feeds data into the hasher.
    pub fn update(&mut self, data: &[u8]) -> io::Result<usize> {
        if self.state == State::Finalized {
            self.init();
        }
        let result = unsafe { CCDigestUpdate(self.ctx, data.as_ptr() as *mut _, data.len()) };
        if result == 0 {
            self.state = State::Updated;
            Ok(data.len())
        } else {
            err_from_ccdigest_retval!("CCDigestCreate", result)
        }
    }

    /// Finalizes digest operations and produces the digest output.
    pub fn finish(&mut self) -> io::Result<Vec<u8>> {
        if self.state == State::Finalized {
            self.init();
        }
        let expected_len = unsafe { CCDigestGetOutputSizeFromRef(self.ctx) };
        let mut md = vec![0; MAX_DIGEST_SIZE];
        let result = unsafe { CCDigestFinal(self.ctx, md.as_mut_ptr()) };
        if result == 0 {
            self.state = State::Finalized;
            md.truncate(expected_len);
            Ok(md)
        } else {
            err_from_ccdigest_retval!("CCDigestFinal", result)
        }
    }
}

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

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

impl Drop for Hasher {
    fn drop(&mut self) {
        if self.state != State::Finalized {
            let _ = self.finish();
        }
        unsafe { CCDigestDestroy(self.ctx) }
    }
}
<|endoftext|>