<commit_before>extern crate gstreamer as gst;
use gstreamer::*;

extern crate glib;
use glib::ObjectExt;

extern crate url;
use url::Url;

use std::path::PathBuf;

use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, Mutex};

use super::{AlignedImage, AudioBuffer, AudioCaps, MediaInfo, Timestamp};

pub enum ContextMessage {
    AsyncDone,
    Eos,
    FailedToOpenMedia,
    GotVideoWidget,
    HaveAudioBuffer(AudioBuffer),
    HaveVideoWidget(glib::Value),
    InitDone,
}

pub struct Context {
    pub pipeline: gst::Pipeline,

    pub path: PathBuf,
    pub file_name: String,
    pub name: String,

    pub info: Arc<Mutex<MediaInfo>>,
}

macro_rules! assign_str_tag(
    ($target:expr, $tags:expr, $TagType:ty) => {
        if $target.is_empty() {
            if let Some(tag) = $tags.get::<$TagType>() {
                $target = tag.get().unwrap().to_owned();
            }
        }
    };
);

impl Context {
    fn new(path: PathBuf) -> Self {
        Context{
            pipeline: gst::Pipeline::new("pipeline"),

            file_name: String::from(path.file_name().unwrap().to_str().unwrap()),
            name: String::from(path.file_stem().unwrap().to_str().unwrap()),
            path: path,

            info: Arc::new(Mutex::new(MediaInfo::new())),
        }
    }

    pub fn open_media_path(
        path: PathBuf,
        ctx_tx: Sender<ContextMessage>,
        ui_rx: Receiver<ContextMessage>,
    ) -> Result<Context, String>
    {
        println!("\n\n* Attempting to open {:?}", path);

        let ctx = Context::new(path);
        ctx.build_pipeline(&ctx_tx, ui_rx);
        ctx.register_bus_inspector(ctx_tx);

        match ctx.pause() {
            Ok(_) => Ok(ctx),
            Err(error) => Err(error),
        }
    }

    pub fn get_duration(&self) -> Timestamp {
        match self.pipeline.query_duration(gst::Format::Time) {
            Some(duration) => Timestamp::from_signed_nano(duration),
            None => Timestamp::new(),
        }
    }

    pub fn play(&self) -> Result<(), String> {
        if self.pipeline.set_state(gst::State::Playing) == gst::StateChangeReturn::Failure {
            return Err("Could not set media in palying state".into());
        }
        Ok(())
    }

    pub fn pause(&self) -> Result<(), String> {
        if self.pipeline.set_state(gst::State::Paused) == gst::StateChangeReturn::Failure {
            println!("could not set media in Paused state");
            return Err("Could not set media in Paused state".into());
        }
        Ok(())
    }

    pub fn stop(&self) {
        if self.pipeline.set_state(gst::State::Null) == gst::StateChangeReturn::Failure {
            println!("Could not set media in Null state");
            //return Err("could not set media in Null state".into());
        }
    }

    // TODO: handle errors
    // Uses ctx_tx & ui_rx to allow the UI integrate the GstGtkWidget
    fn build_pipeline(&self,
        ctx_tx: &Sender<ContextMessage>,
        ui_rx: Receiver<ContextMessage>
    )
    {
        let dec = gst::ElementFactory::make("uridecodebin", "input").unwrap();
        let url = match Url::from_file_path(self.path.as_path()) {
            Ok(url) => url.into_string(),
            Err(_) => "Failed to convert path to URL".to_owned(),
        };
        dec.set_property("uri", &gst::Value::from(&url)).unwrap();
        self.pipeline.add(&dec).unwrap();

        let pipeline_clone = self.pipeline.clone();
        // Need an Arc Mutex on ctx_tx because Rust considers this method
        // as a candidate for being called by multiple threads
        let ctx_tx_arc_mtx = Arc::new(Mutex::new(ctx_tx.clone()));
        let ui_rx_arc_mtx = Arc::new(Mutex::new(ui_rx));
        dec.connect_pad_added(move |_, src_pad| {
            let ref pipeline = pipeline_clone;

            let caps = src_pad.get_current_caps().unwrap();
            let structure = caps.get_structure(0).unwrap();
            let name = structure.get_name();

            // TODO: build only one queue by stream type (audio / video)
            if name.starts_with("audio/") {
                let queue = gst::ElementFactory::make("queue", None).unwrap();
                let convert = gst::ElementFactory::make("audioconvert", None).unwrap();
                let resample = gst::ElementFactory::make("audioresample", None).unwrap();
                let sink = gst::ElementFactory::make("autoaudiosink", "audio_sink").unwrap();

                let elements = &[&queue, &convert, &resample, &sink];
                pipeline.add_many(elements).unwrap();
                gst::Element::link_many(elements).unwrap();

                for e in elements { e.sync_state_with_parent().unwrap(); }

                let sink_pad = queue.get_static_pad("sink").unwrap();
                assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);

                let mut audio_caps_arc: Arc<Option<AudioCaps>> = Arc::new(None);
                let ctx_tx_arc_mtx_clone = ctx_tx_arc_mtx.clone();
                sink_pad.add_probe(PAD_PROBE_TYPE_BUFFER, move |sink_pad, probe_info| {
                    match probe_info.data {
                        Some(PadProbeData::Buffer(ref buffer)) => {
                            // Retrieve audio caps the first time only
                            let ref mut audio_caps = Arc::get_mut(&mut audio_caps_arc).unwrap()
                                .get_or_insert_with(|| AudioCaps::from_sink_pad(&sink_pad));

                            ctx_tx_arc_mtx_clone.lock()
                                .expect("Failed to lock ctx_tx mutex, while transmitting audio buffer")
                                .send(ContextMessage::HaveAudioBuffer(
                                        AudioBuffer::from_gst_buffer(audio_caps, buffer)
                                )).expect("Failed to transmit audio buffer");
                        },
                        _ => (),
                    };

                    PadProbeReturn::Ok
                });
            } else if name.starts_with("video/") {
                let queue = gst::ElementFactory::make("queue", None).unwrap();
                let convert = gst::ElementFactory::make("videoconvert", None).unwrap();
                let scale = gst::ElementFactory::make("videoscale", None).unwrap();

                let (sink, widget_val) = if let Some(gtkglsink) = ElementFactory::make("gtkglsink", None) {
                    let glsinkbin = ElementFactory::make("glsinkbin", "video_sink").unwrap();
                    glsinkbin.set_property("sink", &gtkglsink.to_value()).unwrap();
                    let widget_val = gtkglsink.get_property("widget").unwrap();
                    (glsinkbin, widget_val)
                } else {
                    let sink = ElementFactory::make("gtksink", "video_sink").unwrap();
                    let widget_val = sink.get_property("widget").unwrap();
                    (sink, widget_val)
                };

                // Pass the video_sink for integration in the UI
                ctx_tx_arc_mtx.lock()
                    .expect("Failed to lock ctx_tx mutex, while building the video queue")
                    .send(ContextMessage::HaveVideoWidget(widget_val))
                        .expect("Failed to transmit GstGtkWidget");
                // Wait for the widget to be added to the UI, otherwise the pipeline
                // embeds it in a default window
                match ui_rx_arc_mtx.lock()
                    .expect("Failed to lock ui_rx mutex, while building the video queue")
                    .recv() {
                        Ok(message) => match message {
                            ContextMessage::GotVideoWidget => (),
                            _ => panic!("Unexpected message while waiting for GotVideoWidget"),
                        },
                        Err(error) => panic!("Error while waiting for GotVideoWidget: {:?}", error),
                    };

                let elements = &[&queue, &convert, &scale, &sink];
                pipeline.add_many(elements).unwrap();
                gst::Element::link_many(elements).unwrap();

                for e in elements { e.sync_state_with_parent().unwrap(); }

                let sink_pad = queue.get_static_pad("sink").unwrap();
                assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);
            }
        });
    }

    // Uses ctx_tx to notify the UI controllers about the inspection process
    fn register_bus_inspector(&self, ctx_tx: Sender<ContextMessage>) {
        let info_arc_mtx = self.info.clone();
        let mut init_done = false;
        let bus = self.pipeline.get_bus().unwrap();
        bus.add_watch(move |_, msg| {
            let mut keep_going = true;
            // TODO: exit when pipeline status is null
            // or can we reuse the inspector for subsequent plays?
            match msg.view() {
                MessageView::Eos(..) => {
                    ctx_tx.send(ContextMessage::Eos)
                        .expect("Failed to notify UI");
                    keep_going = false;
                },
                MessageView::Error(err) => {
                    eprintln!("Error from {}: {} ({:?})",
                        msg.get_src().get_path_string(),
                        err.get_error(), err.get_debug()
                    );
                    ctx_tx.send(ContextMessage::FailedToOpenMedia)
                        .expect("Failed to notify UI");
                    keep_going = false;
                },
                MessageView::AsyncDone(_) => {
                    if !init_done {
                        ctx_tx.send(ContextMessage::InitDone)
                            .expect("Failed to notify UI");
                        init_done = true;
                    }
                    else {
                        ctx_tx.send(ContextMessage::AsyncDone)
                            .expect("Failed to notify UI");
                    }
                },
                MessageView::Tag(msg_tag) => {
                    if !init_done {
                        let tags = msg_tag.get_tags();
                        let ref mut info = info_arc_mtx.lock()
                            .expect("Failed to lock media info while reading tag data");
                        assign_str_tag!(info.title, tags, Title);
                        assign_str_tag!(info.artist, tags, Artist);
                        assign_str_tag!(info.artist, tags, AlbumArtist);
                        assign_str_tag!(info.video_codec, tags, VideoCodec);
                        assign_str_tag!(info.audio_codec, tags, AudioCodec);

                        /*match tags.get::<PreviewImage>() {
                            // TODO: check if that happens, that would be handy for videos
                            Some(preview_tag) => println!("Found a PreviewImage tag"),
                            None => (),
                        };*/

                        // TODO: distinguish front/back cover (take the first one?)
                        if let Some(image_tag) = tags.get::<Image>() {
                            if let Some(sample) = image_tag.get() {
                                if let Some(buffer) = sample.get_buffer() {
                                    if let Some(map) = buffer.map_read() {
                                        info.thumbnail = AlignedImage::from_uknown_buffer(
                                                map.as_slice()
                                            ).ok();
                                    }
                                }
                            }
                        }
                    }
                },
                _ => (),
            };

            glib::Continue(keep_going)
        });
    }
}
<commit_msg>Move audio buffer parsing out of mutex section (#7)<commit_after>extern crate gstreamer as gst;
use gstreamer::*;

extern crate glib;
use glib::ObjectExt;

extern crate url;
use url::Url;

use std::path::PathBuf;

use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, Mutex};

use super::{AlignedImage, AudioBuffer, AudioCaps, MediaInfo, Timestamp};

pub enum ContextMessage {
    AsyncDone,
    Eos,
    FailedToOpenMedia,
    GotVideoWidget,
    HaveAudioBuffer(AudioBuffer),
    HaveVideoWidget(glib::Value),
    InitDone,
}

pub struct Context {
    pub pipeline: gst::Pipeline,

    pub path: PathBuf,
    pub file_name: String,
    pub name: String,

    pub info: Arc<Mutex<MediaInfo>>,
}

macro_rules! assign_str_tag(
    ($target:expr, $tags:expr, $TagType:ty) => {
        if $target.is_empty() {
            if let Some(tag) = $tags.get::<$TagType>() {
                $target = tag.get().unwrap().to_owned();
            }
        }
    };
);

impl Context {
    fn new(path: PathBuf) -> Self {
        Context{
            pipeline: gst::Pipeline::new("pipeline"),

            file_name: String::from(path.file_name().unwrap().to_str().unwrap()),
            name: String::from(path.file_stem().unwrap().to_str().unwrap()),
            path: path,

            info: Arc::new(Mutex::new(MediaInfo::new())),
        }
    }

    pub fn open_media_path(
        path: PathBuf,
        ctx_tx: Sender<ContextMessage>,
        ui_rx: Receiver<ContextMessage>,
    ) -> Result<Context, String>
    {
        println!("\n\n* Attempting to open {:?}", path);

        let ctx = Context::new(path);
        ctx.build_pipeline(&ctx_tx, ui_rx);
        ctx.register_bus_inspector(ctx_tx);

        match ctx.pause() {
            Ok(_) => Ok(ctx),
            Err(error) => Err(error),
        }
    }

    pub fn get_duration(&self) -> Timestamp {
        match self.pipeline.query_duration(gst::Format::Time) {
            Some(duration) => Timestamp::from_signed_nano(duration),
            None => Timestamp::new(),
        }
    }

    pub fn play(&self) -> Result<(), String> {
        if self.pipeline.set_state(gst::State::Playing) == gst::StateChangeReturn::Failure {
            return Err("Could not set media in palying state".into());
        }
        Ok(())
    }

    pub fn pause(&self) -> Result<(), String> {
        if self.pipeline.set_state(gst::State::Paused) == gst::StateChangeReturn::Failure {
            println!("could not set media in Paused state");
            return Err("Could not set media in Paused state".into());
        }
        Ok(())
    }

    pub fn stop(&self) {
        if self.pipeline.set_state(gst::State::Null) == gst::StateChangeReturn::Failure {
            println!("Could not set media in Null state");
            //return Err("could not set media in Null state".into());
        }
    }

    // TODO: handle errors
    // Uses ctx_tx & ui_rx to allow the UI integrate the GstGtkWidget
    fn build_pipeline(&self,
        ctx_tx: &Sender<ContextMessage>,
        ui_rx: Receiver<ContextMessage>
    )
    {
        let dec = gst::ElementFactory::make("uridecodebin", "input").unwrap();
        let url = match Url::from_file_path(self.path.as_path()) {
            Ok(url) => url.into_string(),
            Err(_) => "Failed to convert path to URL".to_owned(),
        };
        dec.set_property("uri", &gst::Value::from(&url)).unwrap();
        self.pipeline.add(&dec).unwrap();

        let pipeline_clone = self.pipeline.clone();
        // Need an Arc Mutex on ctx_tx because Rust considers this method
        // as a candidate for being called by multiple threads
        let ctx_tx_arc_mtx = Arc::new(Mutex::new(ctx_tx.clone()));
        let ui_rx_arc_mtx = Arc::new(Mutex::new(ui_rx));
        dec.connect_pad_added(move |_, src_pad| {
            let ref pipeline = pipeline_clone;

            let caps = src_pad.get_current_caps().unwrap();
            let structure = caps.get_structure(0).unwrap();
            let name = structure.get_name();

            // TODO: build only one queue by stream type (audio / video)
            if name.starts_with("audio/") {
                let queue = gst::ElementFactory::make("queue", None).unwrap();
                let convert = gst::ElementFactory::make("audioconvert", None).unwrap();
                let resample = gst::ElementFactory::make("audioresample", None).unwrap();
                let sink = gst::ElementFactory::make("autoaudiosink", "audio_sink").unwrap();

                let elements = &[&queue, &convert, &resample, &sink];
                pipeline.add_many(elements).unwrap();
                gst::Element::link_many(elements).unwrap();

                for e in elements { e.sync_state_with_parent().unwrap(); }

                let sink_pad = queue.get_static_pad("sink").unwrap();
                assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);

                let mut audio_caps_arc: Arc<Option<AudioCaps>> = Arc::new(None);
                let ctx_tx_arc_mtx_clone = ctx_tx_arc_mtx.clone();
                sink_pad.add_probe(PAD_PROBE_TYPE_BUFFER, move |sink_pad, probe_info| {
                    match probe_info.data {
                        Some(PadProbeData::Buffer(ref buffer)) => {
                            // Retrieve audio caps the first time only
                            let ref mut audio_caps = Arc::get_mut(&mut audio_caps_arc).unwrap()
                                .get_or_insert_with(|| AudioCaps::from_sink_pad(&sink_pad));

                            let audio_buffer = AudioBuffer::from_gst_buffer(audio_caps, buffer);

                            ctx_tx_arc_mtx_clone.lock()
                                .expect("Failed to lock ctx_tx mutex, while transmitting audio buffer")
                                .send(ContextMessage::HaveAudioBuffer(audio_buffer))
                                    .expect("Failed to transmit audio buffer");
                        },
                        _ => (),
                    };

                    PadProbeReturn::Ok
                });
            } else if name.starts_with("video/") {
                let queue = gst::ElementFactory::make("queue", None).unwrap();
                let convert = gst::ElementFactory::make("videoconvert", None).unwrap();
                let scale = gst::ElementFactory::make("videoscale", None).unwrap();

                let (sink, widget_val) = if let Some(gtkglsink) = ElementFactory::make("gtkglsink", None) {
                    let glsinkbin = ElementFactory::make("glsinkbin", "video_sink").unwrap();
                    glsinkbin.set_property("sink", &gtkglsink.to_value()).unwrap();
                    let widget_val = gtkglsink.get_property("widget").unwrap();
                    (glsinkbin, widget_val)
                } else {
                    let sink = ElementFactory::make("gtksink", "video_sink").unwrap();
                    let widget_val = sink.get_property("widget").unwrap();
                    (sink, widget_val)
                };

                // Pass the video_sink for integration in the UI
                ctx_tx_arc_mtx.lock()
                    .expect("Failed to lock ctx_tx mutex, while building the video queue")
                    .send(ContextMessage::HaveVideoWidget(widget_val))
                        .expect("Failed to transmit GstGtkWidget");
                // Wait for the widget to be added to the UI, otherwise the pipeline
                // embeds it in a default window
                match ui_rx_arc_mtx.lock()
                    .expect("Failed to lock ui_rx mutex, while building the video queue")
                    .recv() {
                        Ok(message) => match message {
                            ContextMessage::GotVideoWidget => (),
                            _ => panic!("Unexpected message while waiting for GotVideoWidget"),
                        },
                        Err(error) => panic!("Error while waiting for GotVideoWidget: {:?}", error),
                    };

                let elements = &[&queue, &convert, &scale, &sink];
                pipeline.add_many(elements).unwrap();
                gst::Element::link_many(elements).unwrap();

                for e in elements { e.sync_state_with_parent().unwrap(); }

                let sink_pad = queue.get_static_pad("sink").unwrap();
                assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);
            }
        });
    }

    // Uses ctx_tx to notify the UI controllers about the inspection process
    fn register_bus_inspector(&self, ctx_tx: Sender<ContextMessage>) {
        let info_arc_mtx = self.info.clone();
        let mut init_done = false;
        let bus = self.pipeline.get_bus().unwrap();
        bus.add_watch(move |_, msg| {
            let mut keep_going = true;
            // TODO: exit when pipeline status is null
            // or can we reuse the inspector for subsequent plays?
            match msg.view() {
                MessageView::Eos(..) => {
                    ctx_tx.send(ContextMessage::Eos)
                        .expect("Failed to notify UI");
                    keep_going = false;
                },
                MessageView::Error(err) => {
                    eprintln!("Error from {}: {} ({:?})",
                        msg.get_src().get_path_string(),
                        err.get_error(), err.get_debug()
                    );
                    ctx_tx.send(ContextMessage::FailedToOpenMedia)
                        .expect("Failed to notify UI");
                    keep_going = false;
                },
                MessageView::AsyncDone(_) => {
                    if !init_done {
                        ctx_tx.send(ContextMessage::InitDone)
                            .expect("Failed to notify UI");
                        init_done = true;
                    }
                    else {
                        ctx_tx.send(ContextMessage::AsyncDone)
                            .expect("Failed to notify UI");
                    }
                },
                MessageView::Tag(msg_tag) => {
                    if !init_done {
                        let tags = msg_tag.get_tags();
                        let ref mut info = info_arc_mtx.lock()
                            .expect("Failed to lock media info while reading tag data");
                        assign_str_tag!(info.title, tags, Title);
                        assign_str_tag!(info.artist, tags, Artist);
                        assign_str_tag!(info.artist, tags, AlbumArtist);
                        assign_str_tag!(info.video_codec, tags, VideoCodec);
                        assign_str_tag!(info.audio_codec, tags, AudioCodec);

                        /*match tags.get::<PreviewImage>() {
                            // TODO: check if that happens, that would be handy for videos
                            Some(preview_tag) => println!("Found a PreviewImage tag"),
                            None => (),
                        };*/

                        // TODO: distinguish front/back cover (take the first one?)
                        if let Some(image_tag) = tags.get::<Image>() {
                            if let Some(sample) = image_tag.get() {
                                if let Some(buffer) = sample.get_buffer() {
                                    if let Some(map) = buffer.map_read() {
                                        info.thumbnail = AlignedImage::from_uknown_buffer(
                                                map.as_slice()
                                            ).ok();
                                    }
                                }
                            }
                        }
                    }
                },
                _ => (),
            };

            glib::Continue(keep_going)
        });
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
const END_TAG_LEN: u32 = 8;

#[macro_use]
macro_rules! ptr {
    ($it:expr) => { &$it as *const _ }
}

pub mod elf;
pub mod elf64;

#[repr(C)]
pub struct Info { pub length: u32
                , _pad: u32
                , tag_start: Tag
                }

impl Info {

    pub unsafe fn from(addr: usize) -> &'static Self {
        let info = &*(addr as *const Info);
        assert!(info.has_end());
        info
    }

    /// Finds the tag with the given tag type.
    ///
    /// This is actually safe since the tag types are constrained by The
    /// `TagType` enum
    pub fn get_tag(&self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags()
            .find(|t| t.ty == tag_type)
    }

    #[inline]
    pub fn mem_map(&self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::MemoryMap)
            .map(|tag| unsafe { &*(ptr!(tag) as *const MemMapTag) })
    }

    #[inline]
    pub fn elf64_sections(&self) -> Option<&'static elf64::SectionsTag> {
        self.get_tag(TagType::ELFSections)
            .map(|tag| unsafe { &*(ptr!(tag) as *const elf64::SectionsTag) })
    }

    #[inline]
    fn tags(&self) -> Tags { Tags(ptr!(self.tag_start)) }

    fn has_end(&self) -> bool {
        let end_tag_addr
            = (self as *const _) as usize +
              (self.length - END_TAG_LEN) as usize;
        let end_tag = unsafe {&*(end_tag_addr as *const Tag)};
        end_tag.ty == TagType::End && end_tag.length == 8
    }
}

/// A Multiboot tag.
///
/// From the specification:
///
/// Boot information consists of a fixed part and a series of tags.
/// Its start is 8-bytes aligned. Fixed part is as following:
///
///<rawtext>
///
///             +-------------------+
///     u32     | total_size        |
///     u32     | reserved          |
///             +-------------------+
///</rawtext>
///
/// `total_size` contains the total size of boot information including this
/// field and terminating tag in bytes.
/// `reserved` is always set to zero and must be ignored by OS image.
///
///  Every tag begins with following fields:
///<rawtext>
///
///             +-------------------+
///     u32     | type              |
///     u32     | size              |
///             +-------------------+
///</rawtext>
/// `type` contains an identifier of contents of the rest of the tag. `size`
/// contains the size of tag including header fields but not including padding.
/// Tags follow one another padded when necessary in order for each tag to
/// start at 8-bytes aligned address. Tags are terminated by a tag of type `0`
/// and size `8`.
#[repr(C)]
#[derive(Debug)]
pub struct Tag { ty: TagType
               , length: u32
               }

/// Types of Multiboot tags
///
/// Refer to Chapter 3 of the Multiboot 2 spec
#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
pub enum TagType { /// Tag that indicates the end of multiboot tags
                   End              = 0
                 , /// Command line passed to the bootloader
                   CommandLine      = 1
                 , BootloaderName   = 2
                 , Modules          = 3
                 , BasicMemInfo     = 4
                 , BIOSBootDev      = 5
                 , MemoryMap        = 6
                 , VBEInfo          = 7
                 , FramebufferInfo  = 8
                 , ELFSections      = 9
                 , APMTable         = 10
                 }

struct Tags(*const Tag);

impl Tags {
    #[inline] fn advance(&mut self, size: u32) {
        let next_addr = self.0 as usize + size as usize;
        self.0 = (((next_addr-1) & !0x7) + 0x8) as *const _;
    }
}

impl Iterator for Tags {
    type Item = &'static Tag;

    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { &*self.0 } {
            &Tag{ ty: TagType::End, length: END_TAG_LEN } => None
          , tag => { self.advance(tag.length); Some(tag) }
        }
    }
}

#[repr(C)]
pub struct MemMapTag { ty: TagType
                     , length: u32
                     , entry_size: u32
                     , entry_version: u32
                     , first_entry: MemArea
                     }
impl MemMapTag {
    pub fn entries(&self) -> Entries {
        Entries { curr: (&self.first_entry) as *const MemArea
                , last: ((self as *const MemMapTag as u32) +
                        (self.length - self.entry_size))
                        as *const MemArea
                , size: self.entry_size
                }
    }
}

#[repr(u32)]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum MemAreaType { Available = 1
                     , ACPI      = 3
                     , Preserve  = 4
                     }

#[repr(C)]
pub struct MemArea { pub base: u64
                   , pub length: u64
                   , ty: MemAreaType
                   , _pad: u32
                   }

pub struct Entries { curr: *const MemArea
                   , last: *const MemArea
                   , size: u32
                   }

impl Iterator for Entries {
    type Item = &'static MemArea;
    fn next(&mut self) -> Option<&'static MemArea> {
        if self.curr > self.last {
            None
        } else {
            let current = unsafe { &*self.curr };
            self.curr = (self.curr as u32 + self.size) as *const MemArea;
            if current.ty == MemAreaType::Available {
                Some(current)
            } else {
                self.next()
            }
        }
    }
}
<commit_msg>Back to being clever since it didn't fix it<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
const END_TAG_LEN: u32 = 8;

#[macro_use]
macro_rules! ptr {
    ($it:expr) => { &$it as *const _ }
}

pub mod elf;
pub mod elf64;

#[repr(C)]
pub struct Info { pub length: u32
                , _pad: u32
                , tag_start: Tag
                }

impl Info {

    pub unsafe fn from(addr: usize) -> &'static Self {
        let info = &*(addr as *const Info);
        assert!(info.has_end());
        info
    }

    /// Finds the tag with the given tag type.
    ///
    /// This is actually safe since the tag types are constrained by The
    /// `TagType` enum
    pub fn get_tag(&self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags()
            .find(|t| t.ty == tag_type)
    }

    #[inline]
    pub fn mem_map(&self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::MemoryMap)
            .map(|tag| unsafe { &*(ptr!(tag) as *const MemMapTag) })
    }

    #[inline]
    pub fn elf64_sections(&self) -> Option<&'static elf64::SectionsTag> {
        self.get_tag(TagType::ELFSections)
            .map(|tag| unsafe { &*(ptr!(tag) as *const elf64::SectionsTag) })
    }

    #[inline]
    fn tags(&self) -> Tags { Tags(ptr!(self.tag_start)) }

    fn has_end(&self) -> bool {
        let end_tag_addr
            = (self as *const _) as usize +
              (self.length - END_TAG_LEN) as usize;
        let end_tag = unsafe {&*(end_tag_addr as *const Tag)};
        end_tag.ty == TagType::End && end_tag.length == 8
    }
}

/// A Multiboot tag.
///
/// From the specification:
///
/// Boot information consists of a fixed part and a series of tags.
/// Its start is 8-bytes aligned. Fixed part is as following:
///
///<rawtext>
///
///             +-------------------+
///     u32     | total_size        |
///     u32     | reserved          |
///             +-------------------+
///</rawtext>
///
/// `total_size` contains the total size of boot information including this
/// field and terminating tag in bytes.
/// `reserved` is always set to zero and must be ignored by OS image.
///
///  Every tag begins with following fields:
///<rawtext>
///
///             +-------------------+
///     u32     | type              |
///     u32     | size              |
///             +-------------------+
///</rawtext>
/// `type` contains an identifier of contents of the rest of the tag. `size`
/// contains the size of tag including header fields but not including padding.
/// Tags follow one another padded when necessary in order for each tag to
/// start at 8-bytes aligned address. Tags are terminated by a tag of type `0`
/// and size `8`.
#[repr(C)]
#[derive(Debug)]
pub struct Tag { ty: TagType
               , length: u32
               }

/// Types of Multiboot tags
///
/// Refer to Chapter 3 of the Multiboot 2 spec
#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
pub enum TagType { /// Tag that indicates the end of multiboot tags
                   End              = 0
                 , /// Command line passed to the bootloader
                   CommandLine      = 1
                 , BootloaderName   = 2
                 , Modules          = 3
                 , BasicMemInfo     = 4
                 , BIOSBootDev      = 5
                 , MemoryMap        = 6
                 , VBEInfo          = 7
                 , FramebufferInfo  = 8
                 , ELFSections      = 9
                 , APMTable         = 10
                 }

struct Tags(*const Tag);

impl Tags {
    #[inline] fn advance(&mut self, size: u32) {
        let next_addr = self.0 as usize + size as usize;
        self.0 = (((next_addr-1) & !0x7) + 0x8) as *const _;
    }
}

impl Iterator for Tags {
    type Item = &'static Tag;

    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { &*self.0 } {
            &Tag{ ty: TagType::End, length: END_TAG_LEN } => None
          , tag => { self.advance(tag.length); Some(tag) }
        }
    }
}

#[repr(C)]
pub struct MemMapTag { tag: Tag
                     , entry_size: u32
                     , entry_version: u32
                     , first_entry: MemArea
                     }
impl MemMapTag {
    pub fn entries(&self) -> Entries {
        Entries { curr: (&self.first_entry) as *const MemArea
                , last: ((self as *const MemMapTag as u32) +
                        (self.length - self.entry_size))
                        as *const MemArea
                , size: self.entry_size
                }
    }
}

#[repr(u32)]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum MemAreaType { Available = 1
                     , ACPI      = 3
                     , Preserve  = 4
                     }

#[repr(C)]
pub struct MemArea { pub base: u64
                   , pub length: u64
                   , ty: MemAreaType
                   , _pad: u32
                   }

pub struct Entries { curr: *const MemArea
                   , last: *const MemArea
                   , size: u32
                   }

impl Iterator for Entries {
    type Item = &'static MemArea;
    fn next(&mut self) -> Option<&'static MemArea> {
        if self.curr > self.last {
            None
        } else {
            let current = unsafe { &*self.curr };
            self.curr = (self.curr as u32 + self.size) as *const MemArea;
            if current.ty == MemAreaType::Available {
                Some(current)
            } else {
                self.next()
            }
        }
    }
}
<|endoftext|>


<commit_before>//! A simple example of hooking up stdin/stdout to a WebSocket stream.
//!
//! This example will connect to a server specified in the argument list and
//! then forward all data read on stdin to the server, printing out all data
//! received on stdout.
//!
//! Note that this is not currently optimized for performance, especially around
//! buffer management. Rather it's intended to show an example of working with a
//! client.
//!
//! You can use this example together with the `server` example.

extern crate futures;
extern crate tokio_core;
extern crate tokio_tungstenite;
extern crate tungstenite;
extern crate url;

use std::env;
use std::io::{self, Read, Write};
use std::net::ToSocketAddrs;
use std::thread;

use futures::sync::mpsc;
use futures::{Future, Sink, Stream};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use tungstenite::protocol::Message;

use tokio_tungstenite::client_async;

fn main() {
    // Specify the server address to which the client will be connecting.
    let connect_addr = env::args().nth(1).unwrap_or_else(|| {
        panic!("this program requires at least one argument")
    });

    // Get a first IP address of the server from the server URL.
    let url = url::Url::parse(&connect_addr).unwrap();
    let addr = url.to_socket_addrs().unwrap().next().unwrap();

    // Create the event loop and initiate the connection to the remote server.
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let tcp = TcpStream::connect(&addr, &handle);

    // Right now Tokio doesn't support a handle to stdin running on the event
    // loop, so we farm out that work to a separate thread. This thread will
    // read data from stdin and then send it to the event loop over a standard
    // futures channel.
    let (stdin_tx, stdin_rx) = mpsc::channel(0);
    thread::spawn(|| read_stdin(stdin_tx));
    let stdin_rx = stdin_rx.map_err(|_| panic!()); // errors not possible on rx

    // After the TCP connection has been established, we set up our client to
    // start forwarding data.
    //
    // First we do a WebSocket handshake on a TCP stream, i.e. do the upgrade
    // request.
    //
    // Half of the work we're going to do is to take all data we receive on
    // stdin (`stdin_rx`) and send that along the WebSocket stream (`sink`).
    // The second half is to take all the data we receive (`stream`) and then
    // write that to stdout. Currently we just write to stdout in a synchronous
    // fashion.
    //
    // Finally we set the client to terminate once either half of this work
    // finishes. If we don't have any more data to read or we won't receive any
    // more work from the remote then we can exit.
    let mut stdout = io::stdout();
    let client = tcp.and_then(|stream| {
        client_async(url, stream).and_then(|ws_stream| {
            println!("WebSocket handshake has been successfully completed");

            // `sink` is the stream of messages going out.
            // `stream` is the stream of incoming messages.
            let (sink, stream) = ws_stream.split();

            // We forward all messages, composed out of the data, entered to
            // the stdin, to the `sink`.
            let send_stdin = stdin_rx.forward(sink);
            let write_stdout = stream.for_each(|message| {
                stdout.write_all(&message.into_data()).unwrap();
                Ok(())
            });

            // Wait for either of futures to complete.
            send_stdin.map(|_| ())
                      .select(write_stdout.map(|_| ()))
                      .then(|_| Ok(()))
        }).map_err(|e| {
            println!("Error during the websocket handshake occurred: {}", e);
            io::Error::new(io::ErrorKind::Other, e)
        })
    });

    // And now that we've got our client, we execute it in the event loop!
    core.run(client).unwrap();
}

// Our helper method which will read data from stdin and send it along the
// sender provided.
fn read_stdin(mut tx: mpsc::Sender<Message>) {
    let mut stdin = io::stdin();
    loop {
        let mut buf = vec![0; 1024];
        let n = match stdin.read(&mut buf) {
            Err(_) |
            Ok(0) => break,
            Ok(n) => n,
        };
        buf.truncate(n);
        tx = tx.send(Message::binary(buf)).wait().unwrap();
    }
}
<commit_msg>Update example for new API.<commit_after>//! A simple example of hooking up stdin/stdout to a WebSocket stream.
//!
//! This example will connect to a server specified in the argument list and
//! then forward all data read on stdin to the server, printing out all data
//! received on stdout.
//!
//! Note that this is not currently optimized for performance, especially around
//! buffer management. Rather it's intended to show an example of working with a
//! client.
//!
//! You can use this example together with the `server` example.

extern crate futures;
extern crate tokio_core;
extern crate tokio_tungstenite;
extern crate tungstenite;
extern crate url;

use std::env;
use std::io::{self, Read, Write};
use std::thread;

use futures::sync::mpsc;
use futures::{Future, Sink, Stream};
use tokio_core::reactor::Core;
use tungstenite::protocol::Message;

use tokio_tungstenite::connect_async;

fn main() {
    // Specify the server address to which the client will be connecting.
    let connect_addr = env::args().nth(1).unwrap_or_else(|| {
        panic!("this program requires at least one argument")
    });

    let url = url::Url::parse(&connect_addr).unwrap();

    // Create the event loop and initiate the connection to the remote server.
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    // Right now Tokio doesn't support a handle to stdin running on the event
    // loop, so we farm out that work to a separate thread. This thread will
    // read data from stdin and then send it to the event loop over a standard
    // futures channel.
    let (stdin_tx, stdin_rx) = mpsc::channel(0);
    thread::spawn(|| read_stdin(stdin_tx));
    let stdin_rx = stdin_rx.map_err(|_| panic!()); // errors not possible on rx

    // After the TCP connection has been established, we set up our client to
    // start forwarding data.
    //
    // First we do a WebSocket handshake on a TCP stream, i.e. do the upgrade
    // request.
    //
    // Half of the work we're going to do is to take all data we receive on
    // stdin (`stdin_rx`) and send that along the WebSocket stream (`sink`).
    // The second half is to take all the data we receive (`stream`) and then
    // write that to stdout. Currently we just write to stdout in a synchronous
    // fashion.
    //
    // Finally we set the client to terminate once either half of this work
    // finishes. If we don't have any more data to read or we won't receive any
    // more work from the remote then we can exit.
    let mut stdout = io::stdout();
    let client = connect_async(url, handle.remote().clone()).and_then(|ws_stream| {
        println!("WebSocket handshake has been successfully completed");

        // `sink` is the stream of messages going out.
        // `stream` is the stream of incoming messages.
        let (sink, stream) = ws_stream.split();

        // We forward all messages, composed out of the data, entered to
        // the stdin, to the `sink`.
        let send_stdin = stdin_rx.forward(sink);
        let write_stdout = stream.for_each(|message| {
            stdout.write_all(&message.into_data()).unwrap();
            Ok(())
        });

        // Wait for either of futures to complete.
        send_stdin.map(|_| ())
                  .select(write_stdout.map(|_| ()))
                  .then(|_| Ok(()))
    }).map_err(|e| {
        println!("Error during the websocket handshake occurred: {}", e);
        io::Error::new(io::ErrorKind::Other, e)
    });

    // And now that we've got our client, we execute it in the event loop!
    core.run(client).unwrap();
}

// Our helper method which will read data from stdin and send it along the
// sender provided.
fn read_stdin(mut tx: mpsc::Sender<Message>) {
    let mut stdin = io::stdin();
    loop {
        let mut buf = vec![0; 1024];
        let n = match stdin.read(&mut buf) {
            Err(_) |
            Ok(0) => break,
            Ok(n) => n,
        };
        buf.truncate(n);
        tx = tx.send(Message::binary(buf)).wait().unwrap();
    }
}
<|endoftext|>


<commit_before><commit_msg>qualifiers implemented a little<commit_after><|endoftext|>


<commit_before>extern crate clap;
extern crate glob;
#[macro_use] extern crate log;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use std::result::Result as RResult;
use std::process::exit;

use libimagrt::runtime::Runtime;
use libimagstore::store::FileLockEntry;
use libimagutil::trace::trace_error;

mod error;
mod ui;
mod viewer;

use error::{ViewError, ViewErrorKind};
use ui::build_ui;
use viewer::Viewer;
use viewer::ViewInformation;
use viewer::stdout::StdoutViewer;

type Result<T> = RResult<T, ViewError>;

fn main() {
    let name = "imag-view";
    let version = &version!()[..];
    let about = "View entries (readonly)";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1); // we can afford not-executing destructors here
        }
    };

    debug!("Hello. Logging was just enabled");
    debug!("I already set up the Runtime object and build the commandline interface parser.");
    debug!("Lets get rollin' ...");

    info!("No implementation yet");

    let entry_id = rt.cli().value_of("id").unwrap(); // enforced by clap

    if rt.cli().is_present("versions") {
        if let Err(e) = view_versions_of(entry_id, &rt) {
            trace_error(&e);
            exit(1); // we can afford not-executing destructors here
        }
    } else {
        let entry_version   = rt.cli().value_of("version");
        let view_header     = rt.cli().is_present("view-header");
        let view_content    = rt.cli().is_present("view-content");
        let view_copy       = rt.cli().is_present("view-copy");
        let keep_copy       = rt.cli().is_present("keep-copy");

        let scmd = rt.cli().subcommand_matches("view-in");
        if scmd.is_none() {
            debug!("No commandline call");
            exit(1); // we can afford not-executing destructors here
        }
        let scmd = scmd.unwrap();

        let viewer = {
            if scmd.is_present("view-in-stdout") {
                Box::new(StdoutViewer::new())
            } else if scmd.is_present("view-in-ui") {
                warn!("Viewing in UI is currently not supported, switch to stdout");
                Box::new(StdoutViewer::new())
            } else if scmd.is_present("view-in-browser") {
                warn!("Viewing in browser is currently not supported, switch to stdout");
                Box::new(StdoutViewer::new())
            } else if scmd.is_present("view-in-texteditor") {
                warn!("Viewing in texteditor is currently not supported, switch to stdout");
                Box::new(StdoutViewer::new())
            } else if scmd.is_present("view-in-custom") {
                warn!("Viewing in custom is currently not supported, switch to stdout");
                Box::new(StdoutViewer::new())
            } else {
                Box::new(StdoutViewer::new())
            }
        };

        let entry = load_entry(entry_id, entry_version, &rt);
        if entry.is_err() {
            trace_error(&entry.err().unwrap());
            exit(1); // we can afford not-executing destructors here
        }
        let entry = entry.unwrap();

        let view_info = ViewInformation {
            entry:          entry,
            view_header:    view_header,
            view_content:   view_content,
            view_copy:      view_copy,
            keep_copy:      keep_copy,
        };

        viewer.view(view_info);
    }
}

// TODO: This is a shameless adaption of imag-store/src/util.rs
fn load_entry<'a>(id: &str,
                  version: Option<&str>,
                  rt: &'a Runtime)
    -> Result<FileLockEntry<'a>>
{
    debug!("Checking path element for version");

    let version = {
        if version.is_none() {
            let r = id.split("~").last();
            if r.is_none() {
                warn!("No version");
                return Err(ViewError::new(ViewErrorKind::NoVersion, None));
            } else {
                r.unwrap()
            }
        } else {
            version.unwrap()
        }
    };

    debug!("Building path from {:?} and {:?}", id, version);
    let mut path = rt.store().path().clone();

    if id.chars().next() == Some('/') {
        path.push(format!("{}~{}", &id[1..id.len()], version));
    } else {
        path.push(format!("{}~{}", id, version));
    }

    // the above is the adaption...

    rt.store().retrieve(path)
        .map_err(|e| ViewError::new(ViewErrorKind::StoreError, Some(Box::new(e))))
}

fn view_versions_of(id: &str, rt: &Runtime) -> Result<()> {
    use glob::glob;

    let mut path = rt.store().path().clone();

    if id.chars().next() == Some('/') {
        path.push(format!("{}~*", &id[1..id.len()]));
    } else {
        path.push(format!("{}~*", id));
    }

    if let Some(path) = path.to_str() {
        match glob(path) {
            Ok(paths) => {
                for entry in paths {
                    match entry {
                        Ok(path) => println!("{}", path.file_name().and_then(|s| s.to_str()).unwrap()),
                        Err(e)   => trace_error(e.error()),
                    }
                }
                Ok(())
            },
            Err(e) => {
                debug!("Error in pattern");
                Err(ViewError::new(ViewErrorKind::PatternError, Some(Box::new(e))))
            },
        }
    } else {
        warn!("Could not build glob() argument!");
        Err(ViewError::new(ViewErrorKind::GlobBuildError, None))
    }
}

<commit_msg>Enable lints<commit_after>#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

extern crate clap;
extern crate glob;
#[macro_use] extern crate log;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use std::result::Result as RResult;
use std::process::exit;

use libimagrt::runtime::Runtime;
use libimagstore::store::FileLockEntry;
use libimagutil::trace::trace_error;

mod error;
mod ui;
mod viewer;

use error::{ViewError, ViewErrorKind};
use ui::build_ui;
use viewer::Viewer;
use viewer::ViewInformation;
use viewer::stdout::StdoutViewer;

type Result<T> = RResult<T, ViewError>;

fn main() {
    let name = "imag-view";
    let version = &version!()[..];
    let about = "View entries (readonly)";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1); // we can afford not-executing destructors here
        }
    };

    debug!("Hello. Logging was just enabled");
    debug!("I already set up the Runtime object and build the commandline interface parser.");
    debug!("Lets get rollin' ...");

    info!("No implementation yet");

    let entry_id = rt.cli().value_of("id").unwrap(); // enforced by clap

    if rt.cli().is_present("versions") {
        if let Err(e) = view_versions_of(entry_id, &rt) {
            trace_error(&e);
            exit(1); // we can afford not-executing destructors here
        }
    } else {
        let entry_version   = rt.cli().value_of("version");
        let view_header     = rt.cli().is_present("view-header");
        let view_content    = rt.cli().is_present("view-content");
        let view_copy       = rt.cli().is_present("view-copy");
        let keep_copy       = rt.cli().is_present("keep-copy");

        let scmd = rt.cli().subcommand_matches("view-in");
        if scmd.is_none() {
            debug!("No commandline call");
            exit(1); // we can afford not-executing destructors here
        }
        let scmd = scmd.unwrap();

        let viewer = {
            if scmd.is_present("view-in-stdout") {
                Box::new(StdoutViewer::new())
            } else if scmd.is_present("view-in-ui") {
                warn!("Viewing in UI is currently not supported, switch to stdout");
                Box::new(StdoutViewer::new())
            } else if scmd.is_present("view-in-browser") {
                warn!("Viewing in browser is currently not supported, switch to stdout");
                Box::new(StdoutViewer::new())
            } else if scmd.is_present("view-in-texteditor") {
                warn!("Viewing in texteditor is currently not supported, switch to stdout");
                Box::new(StdoutViewer::new())
            } else if scmd.is_present("view-in-custom") {
                warn!("Viewing in custom is currently not supported, switch to stdout");
                Box::new(StdoutViewer::new())
            } else {
                Box::new(StdoutViewer::new())
            }
        };

        let entry = load_entry(entry_id, entry_version, &rt);
        if entry.is_err() {
            trace_error(&entry.err().unwrap());
            exit(1); // we can afford not-executing destructors here
        }
        let entry = entry.unwrap();

        let view_info = ViewInformation {
            entry:          entry,
            view_header:    view_header,
            view_content:   view_content,
            view_copy:      view_copy,
            keep_copy:      keep_copy,
        };

        viewer.view(view_info);
    }
}

// TODO: This is a shameless adaption of imag-store/src/util.rs
fn load_entry<'a>(id: &str,
                  version: Option<&str>,
                  rt: &'a Runtime)
    -> Result<FileLockEntry<'a>>
{
    debug!("Checking path element for version");

    let version = {
        if version.is_none() {
            let r = id.split("~").last();
            if r.is_none() {
                warn!("No version");
                return Err(ViewError::new(ViewErrorKind::NoVersion, None));
            } else {
                r.unwrap()
            }
        } else {
            version.unwrap()
        }
    };

    debug!("Building path from {:?} and {:?}", id, version);
    let mut path = rt.store().path().clone();

    if id.chars().next() == Some('/') {
        path.push(format!("{}~{}", &id[1..id.len()], version));
    } else {
        path.push(format!("{}~{}", id, version));
    }

    // the above is the adaption...

    rt.store().retrieve(path)
        .map_err(|e| ViewError::new(ViewErrorKind::StoreError, Some(Box::new(e))))
}

fn view_versions_of(id: &str, rt: &Runtime) -> Result<()> {
    use glob::glob;

    let mut path = rt.store().path().clone();

    if id.chars().next() == Some('/') {
        path.push(format!("{}~*", &id[1..id.len()]));
    } else {
        path.push(format!("{}~*", id));
    }

    if let Some(path) = path.to_str() {
        match glob(path) {
            Ok(paths) => {
                for entry in paths {
                    match entry {
                        Ok(path) => println!("{}", path.file_name().and_then(|s| s.to_str()).unwrap()),
                        Err(e)   => trace_error(e.error()),
                    }
                }
                Ok(())
            },
            Err(e) => {
                debug!("Error in pattern");
                Err(ViewError::new(ViewErrorKind::PatternError, Some(Box::new(e))))
            },
        }
    } else {
        warn!("Could not build glob() argument!");
        Err(ViewError::new(ViewErrorKind::GlobBuildError, None))
    }
}

<|endoftext|>


<commit_before><commit_msg>Use ubuntu mono font<commit_after><|endoftext|>


<commit_before>use std::error::Error;

#[cfg(feature = "script_helper")]
fn main() -> Result<(), Box<dyn Error>> {
    use handlebars::Handlebars;
    #[macro_use]
    extern crate serde_json;

    let mut handlebars = Handlebars::new();

    handlebars.register_template_file("tpl", "./examples/script/template.hbs")?;
    handlebars.register_script_helper_file("score", "./examples/script/goals.rhai")?;

    let data = json! {[
        [{
            "name": "Dortmund",
            "goals": ["Haaland", "Guerreiro", "Hazard", "Guerreiro"]
        }, {
            "name": "Schalke",
            "goals": []
        }],
        [{
            "name": "RB Leipzig",
            "goals": ["Poulsen"]
        }, {
            "name": "SC Feriburg",
            "goals": ["Gulde"]
        }]
    ]};
    println!("{}", handlebars.render("tpl", &data)?);
    Ok(())
}

#[cfg(not(feature = "script_helper"))]
fn main() -> Result<(), Box<dyn Error>> {
    println!("Please enable feature flag script_helper for this example");
    Ok(())
}
<commit_msg>(fix) allow unused imports for script example<commit_after>#![allow(unused_imports)]

use handlebars::Handlebars;
use std::error::Error;
#[macro_use]
extern crate serde_json;

#[cfg(feature = "script_helper")]
fn main() -> Result<(), Box<dyn Error>> {
    let mut handlebars = Handlebars::new();

    handlebars.register_template_file("tpl", "./examples/script/template.hbs")?;
    handlebars.register_script_helper_file("score", "./examples/script/goals.rhai")?;

    let data = json! {[
        [{
            "name": "Dortmund",
            "goals": ["Haaland", "Guerreiro", "Hazard", "Guerreiro"]
        }, {
            "name": "Schalke",
            "goals": []
        }],
        [{
            "name": "RB Leipzig",
            "goals": ["Poulsen"]
        }, {
            "name": "SC Feriburg",
            "goals": ["Gulde"]
        }]
    ]};
    println!("{}", handlebars.render("tpl", &data)?);
    Ok(())
}

#[cfg(not(feature = "script_helper"))]
fn main() -> Result<(), Box<dyn Error>> {
    println!("Please enable feature flag script_helper for this example");
    Ok(())
}
<|endoftext|>


<commit_before>use lazycell::AtomicLazyCell;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::error::Error;

/// An abstraction for regex patterns.
///
/// * Allows swapping out the regex implementation because it's only in this module.
/// * Makes regexes serializable and deserializable using just the pattern string.
/// * Lazily compiles regexes on first use to improve initialization time.
#[derive(Debug)]
pub struct Regex {
    regex_str: String,
    regex: AtomicLazyCell<regex_impl::Regex>,
}

/// A region contains text positions for capture groups in a match result.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Region {
    region: regex_impl::Region,
}

impl Regex {
    /// Create a new regex from the pattern string.
    ///
    /// Note that the regex compilation happens on first use, which is why this method does not
    /// return a result.
    pub fn new(regex_str: String) -> Self {
        Self {
            regex_str,
            regex: AtomicLazyCell::new(),
        }
    }

    /// Check whether the pattern compiles as a valid regex or not.
    pub fn try_compile(regex_str: &str) -> Option<Box<dyn Error + Send + Sync + 'static>> {
        regex_impl::Regex::new(regex_str).err()
    }

    /// Return the regex pattern.
    pub fn regex_str(&self) -> &str {
        &self.regex_str
    }

    /// Check if the regex matches the given text.
    pub fn is_match(&self, text: &str) -> bool {
        self.regex().is_match(text)
    }

    /// Search for the pattern in the given text from begin/end positions.
    ///
    /// If a region is passed, it is used for storing match group positions. The argument allows
    /// the [`Region`] to be reused between searches, which makes a significant performance
    /// difference.
    ///
    /// [`Region`]: struct.Region.html
    pub fn search(
        &self,
        text: &str,
        begin: usize,
        end: usize,
        region: Option<&mut Region>,
    ) -> bool {
        self.regex()
            .search(text, begin, end, region.map(|r| &mut r.region))
    }

    fn regex(&self) -> &regex_impl::Regex {
        if let Some(regex) = self.regex.borrow() {
            regex
        } else {
            let regex =
                regex_impl::Regex::new(&self.regex_str).expect("regex string should be pre-tested");
            self.regex.fill(regex).ok();
            self.regex.borrow().unwrap()
        }
    }
}

impl Clone for Regex {
    fn clone(&self) -> Self {
        Regex {
            regex_str: self.regex_str.clone(),
            regex: AtomicLazyCell::new(),
        }
    }
}

impl PartialEq for Regex {
    fn eq(&self, other: &Regex) -> bool {
        self.regex_str == other.regex_str
    }
}

impl Eq for Regex {}

impl Serialize for Regex {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.regex_str)
    }
}

impl<'de> Deserialize<'de> for Regex {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let regex_str = String::deserialize(deserializer)?;
        Ok(Regex::new(regex_str))
    }
}

impl Region {
    pub fn new() -> Self {
        Self {
            region: regex_impl::new_region(),
        }
    }

    /// Get the start/end positions of the capture group with given index.
    ///
    /// If there is no match for that group or the index does not correspond to a group, `None` is
    /// returned. The index 0 returns the whole match.
    pub fn pos(&self, index: usize) -> Option<(usize, usize)> {
        self.region.pos(index)
    }
}

#[cfg(feature = "regex-onig")]
mod regex_impl {
    pub use onig::Region;
    use onig::{MatchParam, RegexOptions, SearchOptions, Syntax};
    use std::error::Error;

    #[derive(Debug)]
    pub struct Regex {
        regex: onig::Regex,
    }

    pub fn new_region() -> Region {
        Region::with_capacity(8)
    }

    impl Regex {
        pub fn new(regex_str: &str) -> Result<Regex, Box<dyn Error + Send + Sync + 'static>> {
            let result = onig::Regex::with_options(
                regex_str,
                RegexOptions::REGEX_OPTION_CAPTURE_GROUP,
                Syntax::default(),
            );
            match result {
                Ok(regex) => Ok(Regex { regex }),
                Err(error) => Err(Box::new(error)),
            }
        }

        pub fn is_match(&self, text: &str) -> bool {
            self.regex
                .match_with_options(text, 0, SearchOptions::SEARCH_OPTION_NONE, None)
                .is_some()
        }

        pub fn search(
            &self,
            text: &str,
            begin: usize,
            end: usize,
            region: Option<&mut Region>,
        ) -> bool {
            let matched = self.regex.search_with_param(
                text,
                begin,
                end,
                SearchOptions::SEARCH_OPTION_NONE,
                region,
                MatchParam::default(),
            );

            // If there's an error during search, treat it as non-matching.
            // For example, in case of catastrophic backtracking, onig should
            // fail with a "retry-limit-in-match over" error eventually.
            if let Ok(Some(_)) = matched {
                true
            } else {
                false
            }
        }
    }
}

// If both regex-fancy and regex-onig are requested, this condition makes regex-onig win.
#[cfg(all(feature = "regex-fancy", not(feature = "regex-onig")))]
mod regex_impl {
    use std::error::Error;

    #[derive(Debug)]
    pub struct Regex {
        regex: fancy_regex::Regex,
    }

    #[derive(Clone, Debug, Eq, PartialEq)]
    pub struct Region {
        positions: Vec<Option<(usize, usize)>>,
    }

    pub fn new_region() -> Region {
        Region {
            positions: Vec::with_capacity(8),
        }
    }

    impl Regex {
        pub fn new(regex_str: &str) -> Result<Regex, Box<dyn Error + Send + Sync + 'static>> {
            let result = fancy_regex::Regex::new(regex_str);
            match result {
                Ok(regex) => Ok(Regex { regex }),
                Err(error) => Err(Box::new(error)),
            }
        }

        pub fn is_match(&self, text: &str) -> bool {
            // Errors are treated as non-matches
            self.regex.is_match(text).unwrap_or(false)
        }

        pub fn search(
            &self,
            text: &str,
            begin: usize,
            end: usize,
            region: Option<&mut Region>,
        ) -> bool {
            // If there's an error during search, treat it as non-matching.
            // For example, in case of catastrophic backtracking, fancy-regex should
            // fail with an error eventually.
            if let Ok(Some(captures)) = self.regex.captures_from_pos(&text[..end], begin) {
                if let Some(region) = region {
                    region.init_from_captures(&captures);
                }
                true
            } else {
                false
            }
        }
    }

    impl Region {
        fn init_from_captures(&mut self, captures: &fancy_regex::Captures) {
            self.positions.clear();
            for i in 0..captures.len() {
                let pos = captures.get(i).map(|m| (m.start(), m.end()));
                self.positions.push(pos);
            }
        }

        pub fn pos(&self, i: usize) -> Option<(usize, usize)> {
            if i < self.positions.len() {
                self.positions[i]
            } else {
                None
            }
        }
    }
}

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

    #[test]
    fn caches_compiled_regex() {
        let regex = Regex::new(String::from(r"\w+"));

        assert!(!regex.regex.filled());
        assert!(regex.is_match("test"));
        assert!(regex.regex.filled());
    }

    #[test]
    fn serde_as_string() {
        let pattern: Regex = serde_json::from_str("\"just a string\"").unwrap();
        assert_eq!(pattern.regex_str(), "just a string");
        let back_to_str = serde_json::to_string(&pattern).unwrap();
        assert_eq!(back_to_str, "\"just a string\"");
    }
}
<commit_msg>Fix all clippy::new-without-default lints<commit_after>use lazycell::AtomicLazyCell;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::error::Error;

/// An abstraction for regex patterns.
///
/// * Allows swapping out the regex implementation because it's only in this module.
/// * Makes regexes serializable and deserializable using just the pattern string.
/// * Lazily compiles regexes on first use to improve initialization time.
#[derive(Debug)]
pub struct Regex {
    regex_str: String,
    regex: AtomicLazyCell<regex_impl::Regex>,
}

/// A region contains text positions for capture groups in a match result.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Region {
    region: regex_impl::Region,
}

impl Regex {
    /// Create a new regex from the pattern string.
    ///
    /// Note that the regex compilation happens on first use, which is why this method does not
    /// return a result.
    pub fn new(regex_str: String) -> Self {
        Self {
            regex_str,
            regex: AtomicLazyCell::new(),
        }
    }

    /// Check whether the pattern compiles as a valid regex or not.
    pub fn try_compile(regex_str: &str) -> Option<Box<dyn Error + Send + Sync + 'static>> {
        regex_impl::Regex::new(regex_str).err()
    }

    /// Return the regex pattern.
    pub fn regex_str(&self) -> &str {
        &self.regex_str
    }

    /// Check if the regex matches the given text.
    pub fn is_match(&self, text: &str) -> bool {
        self.regex().is_match(text)
    }

    /// Search for the pattern in the given text from begin/end positions.
    ///
    /// If a region is passed, it is used for storing match group positions. The argument allows
    /// the [`Region`] to be reused between searches, which makes a significant performance
    /// difference.
    ///
    /// [`Region`]: struct.Region.html
    pub fn search(
        &self,
        text: &str,
        begin: usize,
        end: usize,
        region: Option<&mut Region>,
    ) -> bool {
        self.regex()
            .search(text, begin, end, region.map(|r| &mut r.region))
    }

    fn regex(&self) -> &regex_impl::Regex {
        if let Some(regex) = self.regex.borrow() {
            regex
        } else {
            let regex =
                regex_impl::Regex::new(&self.regex_str).expect("regex string should be pre-tested");
            self.regex.fill(regex).ok();
            self.regex.borrow().unwrap()
        }
    }
}

impl Clone for Regex {
    fn clone(&self) -> Self {
        Regex {
            regex_str: self.regex_str.clone(),
            regex: AtomicLazyCell::new(),
        }
    }
}

impl PartialEq for Regex {
    fn eq(&self, other: &Regex) -> bool {
        self.regex_str == other.regex_str
    }
}

impl Eq for Regex {}

impl Serialize for Regex {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.regex_str)
    }
}

impl<'de> Deserialize<'de> for Regex {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let regex_str = String::deserialize(deserializer)?;
        Ok(Regex::new(regex_str))
    }
}

impl Region {
    pub fn new() -> Self {
        Self {
            region: regex_impl::new_region(),
        }
    }

    /// Get the start/end positions of the capture group with given index.
    ///
    /// If there is no match for that group or the index does not correspond to a group, `None` is
    /// returned. The index 0 returns the whole match.
    pub fn pos(&self, index: usize) -> Option<(usize, usize)> {
        self.region.pos(index)
    }
}

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

#[cfg(feature = "regex-onig")]
mod regex_impl {
    pub use onig::Region;
    use onig::{MatchParam, RegexOptions, SearchOptions, Syntax};
    use std::error::Error;

    #[derive(Debug)]
    pub struct Regex {
        regex: onig::Regex,
    }

    pub fn new_region() -> Region {
        Region::with_capacity(8)
    }

    impl Regex {
        pub fn new(regex_str: &str) -> Result<Regex, Box<dyn Error + Send + Sync + 'static>> {
            let result = onig::Regex::with_options(
                regex_str,
                RegexOptions::REGEX_OPTION_CAPTURE_GROUP,
                Syntax::default(),
            );
            match result {
                Ok(regex) => Ok(Regex { regex }),
                Err(error) => Err(Box::new(error)),
            }
        }

        pub fn is_match(&self, text: &str) -> bool {
            self.regex
                .match_with_options(text, 0, SearchOptions::SEARCH_OPTION_NONE, None)
                .is_some()
        }

        pub fn search(
            &self,
            text: &str,
            begin: usize,
            end: usize,
            region: Option<&mut Region>,
        ) -> bool {
            let matched = self.regex.search_with_param(
                text,
                begin,
                end,
                SearchOptions::SEARCH_OPTION_NONE,
                region,
                MatchParam::default(),
            );

            // If there's an error during search, treat it as non-matching.
            // For example, in case of catastrophic backtracking, onig should
            // fail with a "retry-limit-in-match over" error eventually.
            if let Ok(Some(_)) = matched {
                true
            } else {
                false
            }
        }
    }
}

// If both regex-fancy and regex-onig are requested, this condition makes regex-onig win.
#[cfg(all(feature = "regex-fancy", not(feature = "regex-onig")))]
mod regex_impl {
    use std::error::Error;

    #[derive(Debug)]
    pub struct Regex {
        regex: fancy_regex::Regex,
    }

    #[derive(Clone, Debug, Eq, PartialEq)]
    pub struct Region {
        positions: Vec<Option<(usize, usize)>>,
    }

    pub fn new_region() -> Region {
        Region {
            positions: Vec::with_capacity(8),
        }
    }

    impl Regex {
        pub fn new(regex_str: &str) -> Result<Regex, Box<dyn Error + Send + Sync + 'static>> {
            let result = fancy_regex::Regex::new(regex_str);
            match result {
                Ok(regex) => Ok(Regex { regex }),
                Err(error) => Err(Box::new(error)),
            }
        }

        pub fn is_match(&self, text: &str) -> bool {
            // Errors are treated as non-matches
            self.regex.is_match(text).unwrap_or(false)
        }

        pub fn search(
            &self,
            text: &str,
            begin: usize,
            end: usize,
            region: Option<&mut Region>,
        ) -> bool {
            // If there's an error during search, treat it as non-matching.
            // For example, in case of catastrophic backtracking, fancy-regex should
            // fail with an error eventually.
            if let Ok(Some(captures)) = self.regex.captures_from_pos(&text[..end], begin) {
                if let Some(region) = region {
                    region.init_from_captures(&captures);
                }
                true
            } else {
                false
            }
        }
    }

    impl Region {
        fn init_from_captures(&mut self, captures: &fancy_regex::Captures) {
            self.positions.clear();
            for i in 0..captures.len() {
                let pos = captures.get(i).map(|m| (m.start(), m.end()));
                self.positions.push(pos);
            }
        }

        pub fn pos(&self, i: usize) -> Option<(usize, usize)> {
            if i < self.positions.len() {
                self.positions[i]
            } else {
                None
            }
        }
    }
}

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

    #[test]
    fn caches_compiled_regex() {
        let regex = Regex::new(String::from(r"\w+"));

        assert!(!regex.regex.filled());
        assert!(regex.is_match("test"));
        assert!(regex.regex.filled());
    }

    #[test]
    fn serde_as_string() {
        let pattern: Regex = serde_json::from_str("\"just a string\"").unwrap();
        assert_eq!(pattern.regex_str(), "just a string");
        let back_to_str = serde_json::to_string(&pattern).unwrap();
        assert_eq!(back_to_str, "\"just a string\"");
    }
}
<|endoftext|>


<commit_before>//! Oneof-related codegen functions.

use std::collections::HashSet;

use protobuf::descriptor::field_descriptor_proto;
use protobuf::descriptor::file_options;
use protobuf::reflect::FieldDescriptor;
use protobuf_parse::ProtobufAbsPath;

use crate::customize::ctx::CustomizeElemCtx;
use crate::customize::Customize;
use crate::gen::code_writer::CodeWriter;
use crate::gen::code_writer::Visibility;
use crate::gen::field::rust_variant_name_for_protobuf_oneof_field_name;
use crate::gen::field::FieldElem;
use crate::gen::field::FieldGen;
use crate::gen::file_and_mod::FileAndMod;
use crate::gen::inside::protobuf_crate_path;
use crate::gen::message::MessageGen;
use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof;
use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof_field;
use crate::gen::rust_name::RustIdent;
use crate::gen::rust_name::RustIdentWithPath;
use crate::gen::rust_name::RustPath;
use crate::gen::rust_types_values::make_path;
use crate::gen::rust_types_values::RustType;
use crate::gen::scope::OneofVariantWithContext;
use crate::gen::scope::OneofWithContext;
use crate::gen::scope::RootScope;
use crate::gen::scope::WithScope;

// oneof one { ... }
#[derive(Clone)]
pub(crate) struct OneofField<'a> {
    pub elem: FieldElem<'a>,
    pub oneof_variant_rust_name: RustIdent,
    pub oneof_field_name: RustIdent,
    pub type_name: RustIdentWithPath,
    pub boxed: bool,
}

impl<'a> OneofField<'a> {
    // Detecting recursion: if oneof fields contains a self-reference
    // or another message which has a reference to self,
    // put oneof variant into a box.
    fn need_boxed(
        field: &FieldDescriptor,
        root_scope: &RootScope,
        owner_name: &ProtobufAbsPath,
    ) -> bool {
        let mut visited_messages = HashSet::new();
        let mut fields = vec![field.clone()];
        while let Some(field) = fields.pop() {
            if field.proto().field_type() == field_descriptor_proto::Type::TYPE_MESSAGE {
                let message_name = ProtobufAbsPath::from(field.proto().type_name());
                if !visited_messages.insert(message_name.clone()) {
                    continue;
                }
                if message_name == *owner_name {
                    return true;
                }
                let message = root_scope.find_message(&message_name);
                fields.extend(
                    message
                        .message
                        .fields()
                        .into_iter()
                        .filter(|f| f.containing_oneof().is_some()),
                );
            }
        }
        false
    }

    pub fn parse(
        oneof: &OneofWithContext<'a>,
        field: &FieldDescriptor,
        elem: FieldElem<'a>,
        root_scope: &RootScope,
    ) -> OneofField<'a> {
        let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute());

        OneofField {
            elem,
            type_name: oneof.rust_name(),
            boxed,
            oneof_variant_rust_name: rust_variant_name_for_protobuf_oneof_field_name(field.name()),
            oneof_field_name: oneof.field_name(),
        }
    }

    pub fn rust_type(&self, reference: &FileAndMod) -> RustType {
        let t = self.elem.rust_storage_elem_type(reference);

        if self.boxed {
            RustType::Uniq(Box::new(t))
        } else {
            t
        }
    }

    pub fn variant_path(&self, reference: &RustPath) -> RustIdentWithPath {
        make_path(
            reference,
            &self
                .type_name
                .to_path()
                .with_ident(self.oneof_variant_rust_name.clone()),
        )
    }
}

#[derive(Clone)]
pub(crate) struct OneofVariantGen<'a> {
    oneof: &'a OneofGen<'a>,
    _variant: OneofVariantWithContext<'a>,
    oneof_field: OneofField<'a>,
    pub field: FieldGen<'a>,
    _path: String,
}

impl<'a> OneofVariantGen<'a> {
    fn parse(
        oneof: &'a OneofGen<'a>,
        variant: OneofVariantWithContext<'a>,
        field: &'a FieldGen,
        _root_scope: &RootScope,
    ) -> OneofVariantGen<'a> {
        OneofVariantGen {
            oneof,
            _variant: variant.clone(),
            field: field.clone(),
            _path: format!(
                "{}::{}",
                oneof.type_name_relative(
                    &oneof
                        .oneof
                        .message
                        .scope
                        .rust_path_to_file()
                        .clone()
                        .into_path()
                ),
                field.rust_name
            ),
            oneof_field: OneofField::parse(
                variant.oneof,
                &field.proto_field.field,
                field.elem().clone(),
                oneof.message.root_scope,
            ),
        }
    }

    pub fn rust_type(&self, reference: &FileAndMod) -> RustType {
        self.oneof_field.rust_type(reference)
    }

    pub fn path(&self, reference: &FileAndMod) -> RustPath {
        RustPath::from(format!(
            "{}::{}",
            self.oneof
                .type_name_relative(&reference.relative_mod.clone().into_path()),
            self.oneof_field.oneof_variant_rust_name,
        ))
    }
}

pub(crate) struct OneofGen<'a> {
    // Message containing this oneof
    message: &'a MessageGen<'a>,
    pub oneof: OneofWithContext<'a>,
    customize: CustomizeElemCtx<'a>,
    lite_runtime: bool,
}

impl<'a> OneofGen<'a> {
    pub fn parse(
        message: &'a MessageGen,
        oneof: OneofWithContext<'a>,
        parent_customize: &CustomizeElemCtx<'a>,
    ) -> OneofGen<'a> {
        let customize = parent_customize.child(&Customize::default(), &oneof.oneof);
        let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| {
            oneof
                .message
                .file_descriptor()
                .options
                .get_or_default()
                .optimize_for()
                == file_options::OptimizeMode::LITE_RUNTIME
        });
        OneofGen {
            message,
            oneof,
            customize,
            lite_runtime,
        }
    }

    pub fn type_name_relative(&self, source: &RustPath) -> RustIdentWithPath {
        make_path(source, &self.oneof.rust_name())
    }

    pub fn variants_except_group(&'a self) -> Vec<OneofVariantGen<'a>> {
        self.oneof
            .variants()
            .into_iter()
            .filter_map(|v| {
                let field = self
                    .message
                    .fields
                    .iter()
                    .filter(|f| f.proto_field.name() == v.field.name())
                    .next()
                    .expect(&format!("field not found by name: {}", v.field.name()));
                match field.proto_type {
                    field_descriptor_proto::Type::TYPE_GROUP => None,
                    _ => Some(OneofVariantGen::parse(
                        self,
                        v,
                        field,
                        self.message.root_scope,
                    )),
                }
            })
            .collect()
    }

    pub fn full_storage_type(&self) -> RustType {
        RustType::Option(Box::new(RustType::Oneof(
            self.type_name_relative(
                &self
                    .oneof
                    .message
                    .scope
                    .file_and_mod(self.customize.for_elem.clone())
                    .relative_mod
                    .into_path(),
            )
            .clone(),
        )))
    }

    fn file_and_mod(&self) -> FileAndMod {
        let mut file_and_mod = self
            .message
            .message
            .scope
            .file_and_mod(self.customize.for_elem.clone());
        file_and_mod
            .relative_mod
            .push_ident(self.message.message.mod_name());
        file_and_mod
    }

    fn write_enum(&self, w: &mut CodeWriter) {
        let derive = vec!["Clone", "PartialEq", "Debug"];
        w.derive(&derive);
        w.write_line("#[non_exhaustive]");
        write_protoc_insertion_point_for_oneof(w, &self.customize.for_elem, &self.oneof.oneof);
        w.pub_enum(&self.oneof.rust_name().ident.to_string(), |w| {
            for variant in self.variants_except_group() {
                write_protoc_insertion_point_for_oneof_field(
                    w,
                    &self.customize.for_children,
                    &variant.field.proto_field.field,
                );
                w.write_line(&format!(
                    "{}({}),",
                    variant.oneof_field.oneof_variant_rust_name,
                    &variant
                        .rust_type(&self.file_and_mod())
                        .to_code(&self.customize.for_elem)
                ));
            }
        });
    }

    fn write_impl_oneof(&self, w: &mut CodeWriter) {
        w.impl_for_block(
            &format!("{}::Oneof", protobuf_crate_path(&self.customize.for_elem)),
            self.oneof.rust_name().ident.to_string(),
            |_w| {
                // nothing here yet
            },
        );
    }

    fn write_impl_oneof_full_fn_descriptor(&self, w: &mut CodeWriter) {
        let sig = format!(
            "descriptor() -> {}::reflect::OneofDescriptor",
            protobuf_crate_path(&self.customize.for_elem),
        );
        w.def_fn(&sig, |w| {
            // TODO: generate it
            w.unimplemented();
        });
    }

    fn write_impl_oneof_full(&self, w: &mut CodeWriter) {
        w.impl_for_block(
            &format!(
                "{}::OneofFull",
                protobuf_crate_path(&self.customize.for_elem)
            ),
            self.oneof.rust_name().ident.to_string(),
            |w| self.write_impl_oneof_full_fn_descriptor(w),
        )
    }

    fn write_generated_oneof_descriptor_data(&self, w: &mut CodeWriter) {
        let sig = format!(
            "generated_oneof_descriptor_data() -> {}::reflect::GeneratedOneofDescriptorData",
            protobuf_crate_path(&self.customize.for_elem)
        );
        w.fn_block(
            Visibility::Path(self.oneof.message.scope.rust_path_to_file().to_reverse()),
            &sig,
            |w| {
                w.write_line(&format!(
                    "{}::reflect::GeneratedOneofDescriptorData::new_2::<{}>(\"{}\", {})",
                    protobuf_crate_path(&self.customize.for_elem),
                    &self.oneof.rust_name().ident,
                    &format!(
                        "{}.{}",
                        self.message.message.message.name_to_package(),
                        self.oneof.oneof.name()
                    ),
                    1234567, // TODO
                ));
            },
        );
    }

    fn write_impl_self(&self, w: &mut CodeWriter) {
        w.impl_self_block(&format!("{}", &self.oneof.rust_name().ident), |w| {
            if !self.lite_runtime {
                self.write_generated_oneof_descriptor_data(w);
            }
        });
    }

    pub fn write(&self, w: &mut CodeWriter) {
        self.write_enum(w);
        w.write_line("");
        self.write_impl_oneof(w);
        if !self.lite_runtime {
            w.write_line("");
            self.write_impl_oneof_full(w);
        }
        w.write_line("");
        self.write_impl_self(w);
    }
}
<commit_msg>Working on oneof descriptor<commit_after>//! Oneof-related codegen functions.

use std::collections::HashSet;

use protobuf::descriptor::field_descriptor_proto;
use protobuf::descriptor::file_options;
use protobuf::reflect::FieldDescriptor;
use protobuf_parse::ProtobufAbsPath;

use crate::customize::ctx::CustomizeElemCtx;
use crate::customize::Customize;
use crate::gen::code_writer::CodeWriter;
use crate::gen::code_writer::Visibility;
use crate::gen::field::rust_variant_name_for_protobuf_oneof_field_name;
use crate::gen::field::FieldElem;
use crate::gen::field::FieldGen;
use crate::gen::file_and_mod::FileAndMod;
use crate::gen::inside::protobuf_crate_path;
use crate::gen::message::MessageGen;
use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof;
use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof_field;
use crate::gen::rust_name::RustIdent;
use crate::gen::rust_name::RustIdentWithPath;
use crate::gen::rust_name::RustPath;
use crate::gen::rust_types_values::make_path;
use crate::gen::rust_types_values::RustType;
use crate::gen::scope::OneofVariantWithContext;
use crate::gen::scope::OneofWithContext;
use crate::gen::scope::RootScope;
use crate::gen::scope::WithScope;

// oneof one { ... }
#[derive(Clone)]
pub(crate) struct OneofField<'a> {
    pub elem: FieldElem<'a>,
    pub oneof_variant_rust_name: RustIdent,
    pub oneof_field_name: RustIdent,
    pub type_name: RustIdentWithPath,
    pub boxed: bool,
}

impl<'a> OneofField<'a> {
    // Detecting recursion: if oneof fields contains a self-reference
    // or another message which has a reference to self,
    // put oneof variant into a box.
    fn need_boxed(
        field: &FieldDescriptor,
        root_scope: &RootScope,
        owner_name: &ProtobufAbsPath,
    ) -> bool {
        let mut visited_messages = HashSet::new();
        let mut fields = vec![field.clone()];
        while let Some(field) = fields.pop() {
            if field.proto().field_type() == field_descriptor_proto::Type::TYPE_MESSAGE {
                let message_name = ProtobufAbsPath::from(field.proto().type_name());
                if !visited_messages.insert(message_name.clone()) {
                    continue;
                }
                if message_name == *owner_name {
                    return true;
                }
                let message = root_scope.find_message(&message_name);
                fields.extend(
                    message
                        .message
                        .fields()
                        .into_iter()
                        .filter(|f| f.containing_oneof().is_some()),
                );
            }
        }
        false
    }

    pub fn parse(
        oneof: &OneofWithContext<'a>,
        field: &FieldDescriptor,
        elem: FieldElem<'a>,
        root_scope: &RootScope,
    ) -> OneofField<'a> {
        let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute());

        OneofField {
            elem,
            type_name: oneof.rust_name(),
            boxed,
            oneof_variant_rust_name: rust_variant_name_for_protobuf_oneof_field_name(field.name()),
            oneof_field_name: oneof.field_name(),
        }
    }

    pub fn rust_type(&self, reference: &FileAndMod) -> RustType {
        let t = self.elem.rust_storage_elem_type(reference);

        if self.boxed {
            RustType::Uniq(Box::new(t))
        } else {
            t
        }
    }

    pub fn variant_path(&self, reference: &RustPath) -> RustIdentWithPath {
        make_path(
            reference,
            &self
                .type_name
                .to_path()
                .with_ident(self.oneof_variant_rust_name.clone()),
        )
    }
}

#[derive(Clone)]
pub(crate) struct OneofVariantGen<'a> {
    oneof: &'a OneofGen<'a>,
    _variant: OneofVariantWithContext<'a>,
    oneof_field: OneofField<'a>,
    pub field: FieldGen<'a>,
    _path: String,
}

impl<'a> OneofVariantGen<'a> {
    fn parse(
        oneof: &'a OneofGen<'a>,
        variant: OneofVariantWithContext<'a>,
        field: &'a FieldGen,
        _root_scope: &RootScope,
    ) -> OneofVariantGen<'a> {
        OneofVariantGen {
            oneof,
            _variant: variant.clone(),
            field: field.clone(),
            _path: format!(
                "{}::{}",
                oneof.type_name_relative(
                    &oneof
                        .oneof
                        .message
                        .scope
                        .rust_path_to_file()
                        .clone()
                        .into_path()
                ),
                field.rust_name
            ),
            oneof_field: OneofField::parse(
                variant.oneof,
                &field.proto_field.field,
                field.elem().clone(),
                oneof.message.root_scope,
            ),
        }
    }

    pub fn rust_type(&self, reference: &FileAndMod) -> RustType {
        self.oneof_field.rust_type(reference)
    }

    pub fn path(&self, reference: &FileAndMod) -> RustPath {
        RustPath::from(format!(
            "{}::{}",
            self.oneof
                .type_name_relative(&reference.relative_mod.clone().into_path()),
            self.oneof_field.oneof_variant_rust_name,
        ))
    }
}

pub(crate) struct OneofGen<'a> {
    // Message containing this oneof
    message: &'a MessageGen<'a>,
    pub oneof: OneofWithContext<'a>,
    customize: CustomizeElemCtx<'a>,
    lite_runtime: bool,
}

impl<'a> OneofGen<'a> {
    pub fn parse(
        message: &'a MessageGen,
        oneof: OneofWithContext<'a>,
        parent_customize: &CustomizeElemCtx<'a>,
    ) -> OneofGen<'a> {
        let customize = parent_customize.child(&Customize::default(), &oneof.oneof);
        let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| {
            oneof
                .message
                .file_descriptor()
                .options
                .get_or_default()
                .optimize_for()
                == file_options::OptimizeMode::LITE_RUNTIME
        });
        OneofGen {
            message,
            oneof,
            customize,
            lite_runtime,
        }
    }

    pub fn type_name_relative(&self, source: &RustPath) -> RustIdentWithPath {
        make_path(source, &self.oneof.rust_name())
    }

    pub fn variants_except_group(&'a self) -> Vec<OneofVariantGen<'a>> {
        self.oneof
            .variants()
            .into_iter()
            .filter_map(|v| {
                let field = self
                    .message
                    .fields
                    .iter()
                    .filter(|f| f.proto_field.name() == v.field.name())
                    .next()
                    .expect(&format!("field not found by name: {}", v.field.name()));
                match field.proto_type {
                    field_descriptor_proto::Type::TYPE_GROUP => None,
                    _ => Some(OneofVariantGen::parse(
                        self,
                        v,
                        field,
                        self.message.root_scope,
                    )),
                }
            })
            .collect()
    }

    pub fn full_storage_type(&self) -> RustType {
        RustType::Option(Box::new(RustType::Oneof(
            self.type_name_relative(
                &self
                    .oneof
                    .message
                    .scope
                    .file_and_mod(self.customize.for_elem.clone())
                    .relative_mod
                    .into_path(),
            )
            .clone(),
        )))
    }

    fn file_and_mod(&self) -> FileAndMod {
        let mut file_and_mod = self
            .message
            .message
            .scope
            .file_and_mod(self.customize.for_elem.clone());
        file_and_mod
            .relative_mod
            .push_ident(self.message.message.mod_name());
        file_and_mod
    }

    fn write_enum(&self, w: &mut CodeWriter) {
        let derive = vec!["Clone", "PartialEq", "Debug"];
        w.derive(&derive);
        w.write_line("#[non_exhaustive]");
        write_protoc_insertion_point_for_oneof(w, &self.customize.for_elem, &self.oneof.oneof);
        w.pub_enum(&self.oneof.rust_name().ident.to_string(), |w| {
            for variant in self.variants_except_group() {
                write_protoc_insertion_point_for_oneof_field(
                    w,
                    &self.customize.for_children,
                    &variant.field.proto_field.field,
                );
                w.write_line(&format!(
                    "{}({}),",
                    variant.oneof_field.oneof_variant_rust_name,
                    &variant
                        .rust_type(&self.file_and_mod())
                        .to_code(&self.customize.for_elem)
                ));
            }
        });
    }

    fn write_impl_oneof(&self, w: &mut CodeWriter) {
        w.impl_for_block(
            &format!("{}::Oneof", protobuf_crate_path(&self.customize.for_elem)),
            self.oneof.rust_name().ident.to_string(),
            |_w| {
                // nothing here yet
            },
        );
    }

    fn write_impl_oneof_full_fn_descriptor(&self, w: &mut CodeWriter) {
        let sig = format!(
            "descriptor() -> {}::reflect::OneofDescriptor",
            protobuf_crate_path(&self.customize.for_elem),
        );
        w.def_fn(&sig, |w| {
            w.lazy_static(
                "descriptor",
                &format!(
                    "{}::reflect::OneofDescriptor",
                    protobuf_crate_path(&self.customize.for_elem),
                ),
                &protobuf_crate_path(&self.customize.for_elem).to_string(),
            );
            // self.oneof.message.rust_name_to_file().
            // TODO: generate it
            w.unimplemented();
        });
    }

    fn write_impl_oneof_full(&self, w: &mut CodeWriter) {
        w.impl_for_block(
            &format!(
                "{}::OneofFull",
                protobuf_crate_path(&self.customize.for_elem)
            ),
            self.oneof.rust_name().ident.to_string(),
            |w| self.write_impl_oneof_full_fn_descriptor(w),
        )
    }

    fn write_generated_oneof_descriptor_data(&self, w: &mut CodeWriter) {
        let sig = format!(
            "generated_oneof_descriptor_data() -> {}::reflect::GeneratedOneofDescriptorData",
            protobuf_crate_path(&self.customize.for_elem)
        );
        w.fn_block(
            Visibility::Path(self.oneof.message.scope.rust_path_to_file().to_reverse()),
            &sig,
            |w| {
                w.write_line(&format!(
                    "{}::reflect::GeneratedOneofDescriptorData::new_2::<{}>(\"{}\", {})",
                    protobuf_crate_path(&self.customize.for_elem),
                    &self.oneof.rust_name().ident,
                    &format!(
                        "{}.{}",
                        self.message.message.message.name_to_package(),
                        self.oneof.oneof.name()
                    ),
                    1234567, // TODO
                ));
            },
        );
    }

    fn write_impl_self(&self, w: &mut CodeWriter) {
        w.impl_self_block(&format!("{}", &self.oneof.rust_name().ident), |w| {
            if !self.lite_runtime {
                self.write_generated_oneof_descriptor_data(w);
            }
        });
    }

    pub fn write(&self, w: &mut CodeWriter) {
        self.write_enum(w);
        w.write_line("");
        self.write_impl_oneof(w);
        if !self.lite_runtime {
            w.write_line("");
            self.write_impl_oneof_full(w);
        }
        w.write_line("");
        self.write_impl_self(w);
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate nix;
#[macro_use]
extern crate lazy_static;
extern crate sema;

use sema::Semaphore;
use nix::sys::signal::{
    sigaction,
    SigAction,
    SockFlag,
    SigSet,
};
use nix::sys::signal::signal::SIGWINCH;

use std::sync::atomic::{
    AtomicUsize,
    ATOMIC_USIZE_INIT,
    Ordering,
};
use std::thread;

lazy_static! {
    static ref SIGNAL_SEMA: Semaphore = {
        Semaphore::new(0)
    };
}

static SIGNAL_MASK: AtomicUsize = ATOMIC_USIZE_INIT;

fn main() {
    let sa = SigAction::new(sighandler, SockFlag::empty(), SigSet::empty());
    unsafe { sigaction(SIGWINCH, &sa).unwrap() };
    let t = thread::spawn(move || {
        loop {
            SIGNAL_SEMA.wait();
            let last = SIGNAL_MASK.fetch_and(!(1<<SIGWINCH), Ordering::SeqCst);
            if last & (1<<SIGWINCH) != 0 {
                println!("Caught SIGWINCH");
            }
        }
    });
    t.join().unwrap();
}

extern fn sighandler(signum: i32) {
    SIGNAL_MASK.fetch_or((1<<signum), Ordering::SeqCst);
    SIGNAL_SEMA.post();
}

<commit_msg>remove examples. (not pertinent to library atm, should make new ones)'<commit_after><|endoftext|>