<commit_before>use std::io::{self, Error, ErrorKind, Read, Write};
use super::{Rebox, HeapAllocator, IoWriterWrapper};
use brotli::{DecompressorWriterCustomIo, CustomWrite};
use alloc_no_stdlib::{Allocator, SliceWrapper};
use brotli::enc::BrotliEncoderParams;
#[cfg(feature="validation")]
use sha2::{Sha256, Digest};
#[cfg(feature="validation")]
type Checksum = Sha256;

struct Tee<OutputA:Write, OutputB:Write>(OutputA, OutputB);
impl <OutputA:Write, OutputB:Write> Write for Tee<OutputA, OutputB> {
    fn write(&mut self, data:&[u8]) -> Result<usize, io::Error> {
        match self.0.write(data) {
            Err(err) => return Err(err),
            Ok(size) => match self.1.write_all(&data[..size]) {
                Ok(_) => Ok(size),
                Err(err) => Err(err),
            }
        }
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        match self.0.flush() {
            Err(err) => return Err(err),
            Ok(_) => {
                loop {
                    match self.1.flush() {
                        Err(e) => match e.kind() {
                            ErrorKind::Interrupted => continue,
                            _ => return Err(e),
                        },
                        Ok(e) => return Ok(e),
                    }
                }
            },
        }
    }
}

struct DecompressAndValidate<'a, OutputType:Write+'a>(
    DecompressorWriterCustomIo<io::Error,
                               IoWriterWrapper<'a, OutputType>,
                               Rebox<u8>, // buffer type
                               HeapAllocator,
                               HeapAllocator,
                               HeapAllocator>
);

impl<'a, OutputType:Write> Write for DecompressAndValidate<'a, OutputType> {
    fn write(&mut self, data:&[u8]) -> Result<usize, io::Error> {
        self.0.write(data)
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        self.0.flush()
    }
}

#[cfg(not(feature="validation"))]
fn make_sha_writer() -> io::Sink {
    io::sink()
}
#[cfg(not(feature="validation"))]
fn make_sha_reader<InputType:Read>(r:&mut InputType) -> &mut InputType {
    r
}

#[cfg(not(feature="validation"))]
fn sha_ok<InputType:Read>(_writer: &mut io::Sink, _reader: &mut InputType) -> bool {
    false
}

#[cfg(feature="validation")]
struct ShaReader<'a, InputType:Read> {
    reader:&'a mut InputType,
    checksum:Checksum,
}
#[cfg(feature="validation")]
impl<'a, InputType:Read> Read for ShaReader<'a, InputType> {
    fn read(&mut self, data: &mut [u8]) -> Result<usize, io::Error> {
        match self.reader.read(data) {
            Err(e) => Err(e),
            Ok(size) => {
                self.checksum.input(&data[..size]);
                Ok(size)
            },
        }
    }
}
#[cfg(feature="validation")]
fn make_sha_reader<InputType:Read>(r:&mut InputType) -> ShaReader<InputType> {
    ShaReader{
        reader:r,
        checksum:Checksum::default(),
    }
}

#[cfg(feature="validation")]
fn sha_ok<InputType:Read>(writer: &mut ShaWriter, reader: &mut ShaReader<InputType>) -> bool {
    core::mem::replace(&mut writer.0,
                       Checksum::default()).result() == core::mem::replace(&mut reader.checksum,
                                                                           Checksum::default()).result()
}
#[cfg(feature="validation")]
#[derive(Default)]
struct ShaWriter(Checksum);
#[cfg(feature="validation")]
impl Write for ShaWriter {
    fn write(&mut self, data:&[u8]) -> Result<usize, io::Error> {
        self.0.input(data);
        Ok(data.len())
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        Ok(())
    }
}
#[cfg(feature="validation")]
fn make_sha_writer() -> ShaWriter {
    ShaWriter::default()
}

pub fn compress_validate<InputType:Read, OutputType: Write>(r: &mut InputType,
                                                            w: &mut OutputType,
                                                            buffer_size: usize,
                                                            params: &BrotliEncoderParams,
                                                            custom_dictionary:Rebox<u8>,
                                                            num_threads: usize) -> Result<(), io::Error> {
    let mut m8 = HeapAllocator::default();
    let buffer = <HeapAllocator as Allocator<u8>>::alloc_cell(&mut m8, buffer_size);
    // FIXME: could reuse the dictionary to seed the compressor, but that violates the abstraction right now
    // also dictionaries are not very popular since they are mostly an internal concept, given their deprecation in
    // the standard brotli spec
    let mut dict = Vec::<u8>::new();

    dict.extend_from_slice(custom_dictionary.slice());
    let mut sha_writer = make_sha_writer();
    let mut sha_reader = make_sha_reader(r);
    let ret;
    {
        let validate_writer = DecompressAndValidate(DecompressorWriterCustomIo::new_with_custom_dictionary(
            IoWriterWrapper(&mut sha_writer),
            buffer,
            m8,
            HeapAllocator::default(),
            HeapAllocator::default(),
            custom_dictionary,
            Error::new(ErrorKind::InvalidData,
                       "Invalid Data")));
        let mut overarching_writer = Tee(validate_writer, w);
        ret = super::compress(&mut sha_reader, &mut overarching_writer, buffer_size, params, &dict[..], num_threads);
    }
    match ret {
        Ok(_ret) => {
            if sha_ok(&mut sha_writer, &mut sha_reader) {
                return Ok(());
            } else {
                return Err(Error::new(ErrorKind::InvalidData, "Validation failed"));
            }
        },
        Err(e) => Err(e),
    }
}
<commit_msg>inform users that they need to build with --features=validation to get validation support<commit_after>use std::io::{self, Error, ErrorKind, Read, Write};
use super::{Rebox, HeapAllocator, IoWriterWrapper};
use brotli::{DecompressorWriterCustomIo, CustomWrite};
use alloc_no_stdlib::{Allocator, SliceWrapper};
use brotli::enc::BrotliEncoderParams;
#[cfg(feature="validation")]
use sha2::{Sha256, Digest};
#[cfg(feature="validation")]
type Checksum = Sha256;

struct Tee<OutputA:Write, OutputB:Write>(OutputA, OutputB);
impl <OutputA:Write, OutputB:Write> Write for Tee<OutputA, OutputB> {
    fn write(&mut self, data:&[u8]) -> Result<usize, io::Error> {
        match self.0.write(data) {
            Err(err) => return Err(err),
            Ok(size) => match self.1.write_all(&data[..size]) {
                Ok(_) => Ok(size),
                Err(err) => Err(err),
            }
        }
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        match self.0.flush() {
            Err(err) => return Err(err),
            Ok(_) => {
                loop {
                    match self.1.flush() {
                        Err(e) => match e.kind() {
                            ErrorKind::Interrupted => continue,
                            _ => return Err(e),
                        },
                        Ok(e) => return Ok(e),
                    }
                }
            },
        }
    }
}

struct DecompressAndValidate<'a, OutputType:Write+'a>(
    DecompressorWriterCustomIo<io::Error,
                               IoWriterWrapper<'a, OutputType>,
                               Rebox<u8>, // buffer type
                               HeapAllocator,
                               HeapAllocator,
                               HeapAllocator>
);

impl<'a, OutputType:Write> Write for DecompressAndValidate<'a, OutputType> {
    fn write(&mut self, data:&[u8]) -> Result<usize, io::Error> {
        self.0.write(data)
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        self.0.flush()
    }
}

#[cfg(not(feature="validation"))]
fn make_sha_writer() -> io::Sink {
    io::sink()
}
#[cfg(not(feature="validation"))]
fn make_sha_reader<InputType:Read>(r:&mut InputType) -> &mut InputType {
    r
}

#[cfg(not(feature="validation"))]
fn sha_ok<InputType:Read>(_writer: &mut io::Sink, _reader: &mut InputType) -> bool {
    false
}

#[cfg(feature="validation")]
struct ShaReader<'a, InputType:Read> {
    reader:&'a mut InputType,
    checksum:Checksum,
}
#[cfg(feature="validation")]
impl<'a, InputType:Read> Read for ShaReader<'a, InputType> {
    fn read(&mut self, data: &mut [u8]) -> Result<usize, io::Error> {
        match self.reader.read(data) {
            Err(e) => Err(e),
            Ok(size) => {
                self.checksum.input(&data[..size]);
                Ok(size)
            },
        }
    }
}
#[cfg(feature="validation")]
fn make_sha_reader<InputType:Read>(r:&mut InputType) -> ShaReader<InputType> {
    ShaReader{
        reader:r,
        checksum:Checksum::default(),
    }
}

#[cfg(feature="validation")]
fn sha_ok<InputType:Read>(writer: &mut ShaWriter, reader: &mut ShaReader<InputType>) -> bool {
    core::mem::replace(&mut writer.0,
                       Checksum::default()).result() == core::mem::replace(&mut reader.checksum,
                                                                           Checksum::default()).result()
}
#[cfg(feature="validation")]
#[derive(Default)]
struct ShaWriter(Checksum);
#[cfg(feature="validation")]
impl Write for ShaWriter {
    fn write(&mut self, data:&[u8]) -> Result<usize, io::Error> {
        self.0.input(data);
        Ok(data.len())
    }
    fn flush(&mut self) -> Result<(), io::Error> {
        Ok(())
    }
}
#[cfg(feature="validation")]
fn make_sha_writer() -> ShaWriter {
    ShaWriter::default()
}
#[cfg(feature="validation")]
const VALIDATION_FAILED: &str = "Validation failed";
#[cfg(not(feature="validation"))]
const VALIDATION_FAILED: &str = "Validation module not enabled: build with cargo build --features=validation";

pub fn compress_validate<InputType:Read, OutputType: Write>(r: &mut InputType,
                                                            w: &mut OutputType,
                                                            buffer_size: usize,
                                                            params: &BrotliEncoderParams,
                                                            custom_dictionary:Rebox<u8>,
                                                            num_threads: usize) -> Result<(), io::Error> {
    let mut m8 = HeapAllocator::default();
    let buffer = <HeapAllocator as Allocator<u8>>::alloc_cell(&mut m8, buffer_size);
    // FIXME: could reuse the dictionary to seed the compressor, but that violates the abstraction right now
    // also dictionaries are not very popular since they are mostly an internal concept, given their deprecation in
    // the standard brotli spec
    let mut dict = Vec::<u8>::new();

    dict.extend_from_slice(custom_dictionary.slice());
    let mut sha_writer = make_sha_writer();
    let mut sha_reader = make_sha_reader(r);
    let ret;
    {
        let validate_writer = DecompressAndValidate(DecompressorWriterCustomIo::new_with_custom_dictionary(
            IoWriterWrapper(&mut sha_writer),
            buffer,
            m8,
            HeapAllocator::default(),
            HeapAllocator::default(),
            custom_dictionary,
            Error::new(ErrorKind::InvalidData,
                       "Invalid Data")));
        let mut overarching_writer = Tee(validate_writer, w);
        ret = super::compress(&mut sha_reader, &mut overarching_writer, buffer_size, params, &dict[..], num_threads);
    }
    match ret {
        Ok(_ret) => {
            if sha_ok(&mut sha_writer, &mut sha_reader) {
                return Ok(());
            } else {
                return Err(Error::new(ErrorKind::InvalidData, VALIDATION_FAILED));
            }
        },
        Err(e) => Err(e),
    }
}
<|endoftext|>


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

// external
use svgdom::{
    self,
    FuzzyEq,
};

// self
use tree::prelude::*;
use short::{
    AId,
    EId,
};
use traits::{
    GetValue,
};
use super::{
    fill,
    stroke,
};

pub(super) fn convert(
    text_elem: &svgdom::Node,
    parent: tree::NodeId,
    rtree: &mut tree::RenderTree,
) {
    let attrs = text_elem.attributes();
    let ts = attrs.get_transform(AId::Transform).unwrap_or_default();

    let text_node = rtree.append_child(parent, tree::NodeKind::Text(tree::Text {
        id: text_elem.id().clone(),
        transform: ts,
    }));

    convert_chunks(text_elem, text_node, rtree);
}

fn convert_chunks(
    text_elem: &svgdom::Node,
    parent: tree::NodeId,
    rtree: &mut tree::RenderTree,
) {
    let ref root_attrs = text_elem.attributes();
    let mut prev_x = resolve_pos(root_attrs, AId::X).unwrap_or(0.0);
    let mut prev_y = resolve_pos(root_attrs, AId::Y).unwrap_or(0.0);

    let mut chunk_node = rtree.append_child(
        parent,
        tree::NodeKind::TextChunk(tree::TextChunk {
            x: prev_x,
            y: prev_y,
            anchor: conv_text_anchor(root_attrs),
        })
    );

    for tspan in text_elem.children() {
        debug_assert!(tspan.is_tag_name(EId::Tspan));

        let text = if let Some(node) = tspan.first_child() {
            node.text().clone()
        } else {
            continue;
        };

        let ref attrs = tspan.attributes();
        let x = resolve_pos(attrs, AId::X);
        let y = resolve_pos(attrs, AId::Y);

        if x.is_some() || y.is_some() {
            let tx = x.unwrap_or(prev_x);
            let ty = y.unwrap_or(prev_y);

            if tx.fuzzy_ne(&prev_x) || ty.fuzzy_ne(&prev_y) {
                debug_assert!(rtree.get(chunk_node).children().count() > 0);

                chunk_node = rtree.append_child(
                    parent,
                    tree::NodeKind::TextChunk(tree::TextChunk {
                        x: tx,
                        y: ty,
                        anchor: conv_text_anchor(attrs),
                    })
                );
            }

            prev_x = tx;
            prev_y = ty;
        }

        let fill = fill::convert(rtree, attrs);
        let stroke = stroke::convert(rtree, attrs);
        let decoration = conv_tspan_decoration2(rtree, text_elem, &tspan);
        rtree.append_child(chunk_node, tree::NodeKind::TSpan(tree::TSpan {
            fill,
            stroke,
            font: convert_font(attrs),
            decoration,
            text,
        }));
    }

    debug_assert!(rtree.get(chunk_node).children().count() > 0);
}

fn resolve_pos(
    attrs: &svgdom::Attributes,
    aid: AId,
) -> Option<f64> {
    if let Some(ref list) = attrs.get_number_list(aid) {
        if !list.is_empty() {
            if list.len() > 1 {
                warn!("List of 'x', 'y' coordinates are not supported in a 'text' element.");
            }

            return Some(list[0]);
        }
    }

    None
}

struct TextDecoTypes {
    has_underline: bool,
    has_overline: bool,
    has_line_through: bool,
}

// 'text-decoration' defined in the 'text' element
// should be generated by 'prepare_text_decoration'.
fn conv_text_decoration(node: &svgdom::Node) -> TextDecoTypes {
    debug_assert!(node.is_tag_name(EId::Text));

    let attrs = node.attributes();

    let def = String::new();
    let text = attrs.get_string(AId::TextDecoration).unwrap_or(&def);

    TextDecoTypes {
        has_underline: text.contains("underline"),
        has_overline: text.contains("overline"),
        has_line_through: text.contains("linethrough"),
    }
}

// 'text-decoration' in 'tspan' does not depend on parent elements.
fn conv_tspan_decoration(tspan: &svgdom::Node) -> TextDecoTypes {
    debug_assert!(tspan.is_tag_name(EId::Tspan));

    let attrs = tspan.attributes();

    let has_attr = |decoration_id: svgdom::ValueId| {
        if let Some(id) = attrs.get_predef(AId::TextDecoration) {
            if id == decoration_id {
                return true;
            }
        }

        false
    };

    TextDecoTypes {
        has_underline: has_attr(svgdom::ValueId::Underline),
        has_overline: has_attr(svgdom::ValueId::Overline),
        has_line_through: has_attr(svgdom::ValueId::LineThrough),
    }
}

fn conv_tspan_decoration2(
    rtree: &tree::RenderTree,
    node: &svgdom::Node,
    tspan: &svgdom::Node
) -> tree::TextDecoration {
    let text_dec = conv_text_decoration(node);
    let tspan_dec = conv_tspan_decoration(tspan);

    let gen_style = |in_tspan: bool, in_text: bool| {
        let n = if in_tspan {
            tspan.clone()
        } else if in_text {
            node.clone()
        } else {
            return None;
        };

        let ref attrs = n.attributes();
        let fill = fill::convert(rtree, attrs);
        let stroke = stroke::convert(rtree, attrs);

        Some(tree::TextDecorationStyle {
            fill,
            stroke,
        })
    };

    tree::TextDecoration {
        underline: gen_style(tspan_dec.has_underline, text_dec.has_underline),
        overline: gen_style(tspan_dec.has_overline, text_dec.has_overline),
        line_through: gen_style(tspan_dec.has_line_through, text_dec.has_line_through),
    }
}

fn conv_text_anchor(attrs: &svgdom::Attributes) -> tree::TextAnchor {
    let av = attrs.get_predef(AId::TextAnchor).unwrap_or(svgdom::ValueId::Start);

    match av {
        svgdom::ValueId::Start => tree::TextAnchor::Start,
        svgdom::ValueId::Middle => tree::TextAnchor::Middle,
        svgdom::ValueId::End => tree::TextAnchor::End,
        _ => tree::TextAnchor::Start,
    }
}

fn convert_font(attrs: &svgdom::Attributes) -> tree::Font {
    let style = attrs.get_predef(AId::FontStyle).unwrap_or(svgdom::ValueId::Normal);
    let style = match style {
        svgdom::ValueId::Normal => tree::FontStyle::Normal,
        svgdom::ValueId::Italic => tree::FontStyle::Italic,
        svgdom::ValueId::Oblique => tree::FontStyle::Oblique,
        _ => tree::FontStyle::Normal,
    };

    let variant = attrs.get_predef(AId::FontVariant).unwrap_or(svgdom::ValueId::Normal);
    let variant = match variant {
        svgdom::ValueId::Normal => tree::FontVariant::Normal,
        svgdom::ValueId::SmallCaps => tree::FontVariant::SmallCaps,
        _ => tree::FontVariant::Normal,
    };

    let weight = attrs.get_predef(AId::FontWeight).unwrap_or(svgdom::ValueId::Normal);
    let weight = match weight {
        svgdom::ValueId::Normal => tree::FontWeight::Normal,
        svgdom::ValueId::Bold => tree::FontWeight::Bold,
        svgdom::ValueId::Bolder => tree::FontWeight::Bolder,
        svgdom::ValueId::Lighter => tree::FontWeight::Lighter,
        svgdom::ValueId::N100 => tree::FontWeight::W100,
        svgdom::ValueId::N200 => tree::FontWeight::W200,
        svgdom::ValueId::N300 => tree::FontWeight::W300,
        svgdom::ValueId::N400 => tree::FontWeight::W400,
        svgdom::ValueId::N500 => tree::FontWeight::W500,
        svgdom::ValueId::N600 => tree::FontWeight::W600,
        svgdom::ValueId::N700 => tree::FontWeight::W700,
        svgdom::ValueId::N800 => tree::FontWeight::W800,
        svgdom::ValueId::N900 => tree::FontWeight::W900,
        _ => tree::FontWeight::Normal,
    };

    let stretch = attrs.get_predef(AId::FontStretch).unwrap_or(svgdom::ValueId::Normal);
    let stretch = match stretch {
        svgdom::ValueId::Normal => tree::FontStretch::Normal,
        svgdom::ValueId::Wider => tree::FontStretch::Wider,
        svgdom::ValueId::Narrower => tree::FontStretch::Narrower,
        svgdom::ValueId::UltraCondensed => tree::FontStretch::UltraCondensed,
        svgdom::ValueId::ExtraCondensed => tree::FontStretch::ExtraCondensed,
        svgdom::ValueId::Condensed => tree::FontStretch::Condensed,
        svgdom::ValueId::SemiCondensed => tree::FontStretch::SemiCondensed,
        svgdom::ValueId::SemiExpanded => tree::FontStretch::SemiExpanded,
        svgdom::ValueId::Expanded => tree::FontStretch::Expanded,
        svgdom::ValueId::ExtraExpanded => tree::FontStretch::ExtraExpanded,
        svgdom::ValueId::UltraExpanded => tree::FontStretch::UltraExpanded,
        _ => tree::FontStretch::Normal,
    };

    // TODO: remove text nodes with font-size <= 0
    let size = attrs.get_number(AId::FontSize).unwrap_or(::DEFAULT_FONT_SIZE);
    debug_assert!(size > 0.0);

    let family = attrs.get_string(AId::FontFamily)
                      .unwrap_or(&::DEFAULT_FONT_FAMILY.to_owned()).clone();

    tree::Font {
        family,
        size,
        style,
        variant,
        weight,
        stretch,
    }
}
<commit_msg>Fixed text conversion.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// external
use svgdom::{
    self,
    FuzzyEq,
};

// self
use tree::prelude::*;
use short::{
    AId,
    EId,
};
use traits::{
    GetValue,
};
use super::{
    fill,
    stroke,
};

pub(super) fn convert(
    text_elem: &svgdom::Node,
    parent: tree::NodeId,
    rtree: &mut tree::RenderTree,
) {
    let attrs = text_elem.attributes();
    let ts = attrs.get_transform(AId::Transform).unwrap_or_default();

    let text_node = rtree.append_child(parent, tree::NodeKind::Text(tree::Text {
        id: text_elem.id().clone(),
        transform: ts,
    }));

    convert_chunks(text_elem, text_node, rtree);
}

fn convert_chunks(
    text_elem: &svgdom::Node,
    parent: tree::NodeId,
    rtree: &mut tree::RenderTree,
) {
    let ref root_attrs = text_elem.attributes();
    let mut prev_x = resolve_pos(root_attrs, AId::X).unwrap_or(0.0);
    let mut prev_y = resolve_pos(root_attrs, AId::Y).unwrap_or(0.0);

    let mut chunk_node = rtree.append_child(
        parent,
        tree::NodeKind::TextChunk(tree::TextChunk {
            x: prev_x,
            y: prev_y,
            anchor: conv_text_anchor(root_attrs),
        })
    );

    for tspan in text_elem.children() {
        debug_assert!(tspan.is_tag_name(EId::Tspan));

        let text = if let Some(node) = tspan.first_child() {
            node.text().clone()
        } else {
            continue;
        };

        let ref attrs = tspan.attributes();
        let x = resolve_pos(attrs, AId::X);
        let y = resolve_pos(attrs, AId::Y);

        if x.is_some() || y.is_some() {
            let tx = x.unwrap_or(prev_x);
            let ty = y.unwrap_or(prev_y);

            if tx.fuzzy_ne(&prev_x) || ty.fuzzy_ne(&prev_y) {
                if rtree.get(chunk_node).children().count() > 0 {
                    // Create new if current text chunk has children.
                    chunk_node = rtree.append_child(
                        parent,
                        tree::NodeKind::TextChunk(tree::TextChunk {
                            x: tx,
                            y: ty,
                            anchor: conv_text_anchor(attrs),
                        })
                    );
                } else {
                    // Update existing chunk.
                    let mut v = rtree.get_mut(chunk_node);
                    if let &mut tree::NodeKind::TextChunk(ref mut d) = v.value() {
                        d.x = tx;
                        d.y = ty;
                        d.anchor = conv_text_anchor(attrs);
                    }
                }
            }

            prev_x = tx;
            prev_y = ty;
        }

        let fill = fill::convert(rtree, attrs);
        let stroke = stroke::convert(rtree, attrs);
        let decoration = conv_tspan_decoration2(rtree, text_elem, &tspan);
        rtree.append_child(chunk_node, tree::NodeKind::TSpan(tree::TSpan {
            fill,
            stroke,
            font: convert_font(attrs),
            decoration,
            text,
        }));
    }

    debug_assert!(rtree.get(chunk_node).children().count() > 0);
}

fn resolve_pos(
    attrs: &svgdom::Attributes,
    aid: AId,
) -> Option<f64> {
    if let Some(ref list) = attrs.get_number_list(aid) {
        if !list.is_empty() {
            if list.len() > 1 {
                warn!("List of 'x', 'y' coordinates are not supported in a 'text' element.");
            }

            return Some(list[0]);
        }
    }

    None
}

struct TextDecoTypes {
    has_underline: bool,
    has_overline: bool,
    has_line_through: bool,
}

// 'text-decoration' defined in the 'text' element
// should be generated by 'prepare_text_decoration'.
fn conv_text_decoration(node: &svgdom::Node) -> TextDecoTypes {
    debug_assert!(node.is_tag_name(EId::Text));

    let attrs = node.attributes();

    let def = String::new();
    let text = attrs.get_string(AId::TextDecoration).unwrap_or(&def);

    TextDecoTypes {
        has_underline: text.contains("underline"),
        has_overline: text.contains("overline"),
        has_line_through: text.contains("linethrough"),
    }
}

// 'text-decoration' in 'tspan' does not depend on parent elements.
fn conv_tspan_decoration(tspan: &svgdom::Node) -> TextDecoTypes {
    debug_assert!(tspan.is_tag_name(EId::Tspan));

    let attrs = tspan.attributes();

    let has_attr = |decoration_id: svgdom::ValueId| {
        if let Some(id) = attrs.get_predef(AId::TextDecoration) {
            if id == decoration_id {
                return true;
            }
        }

        false
    };

    TextDecoTypes {
        has_underline: has_attr(svgdom::ValueId::Underline),
        has_overline: has_attr(svgdom::ValueId::Overline),
        has_line_through: has_attr(svgdom::ValueId::LineThrough),
    }
}

fn conv_tspan_decoration2(
    rtree: &tree::RenderTree,
    node: &svgdom::Node,
    tspan: &svgdom::Node
) -> tree::TextDecoration {
    let text_dec = conv_text_decoration(node);
    let tspan_dec = conv_tspan_decoration(tspan);

    let gen_style = |in_tspan: bool, in_text: bool| {
        let n = if in_tspan {
            tspan.clone()
        } else if in_text {
            node.clone()
        } else {
            return None;
        };

        let ref attrs = n.attributes();
        let fill = fill::convert(rtree, attrs);
        let stroke = stroke::convert(rtree, attrs);

        Some(tree::TextDecorationStyle {
            fill,
            stroke,
        })
    };

    tree::TextDecoration {
        underline: gen_style(tspan_dec.has_underline, text_dec.has_underline),
        overline: gen_style(tspan_dec.has_overline, text_dec.has_overline),
        line_through: gen_style(tspan_dec.has_line_through, text_dec.has_line_through),
    }
}

fn conv_text_anchor(attrs: &svgdom::Attributes) -> tree::TextAnchor {
    let av = attrs.get_predef(AId::TextAnchor).unwrap_or(svgdom::ValueId::Start);

    match av {
        svgdom::ValueId::Start => tree::TextAnchor::Start,
        svgdom::ValueId::Middle => tree::TextAnchor::Middle,
        svgdom::ValueId::End => tree::TextAnchor::End,
        _ => tree::TextAnchor::Start,
    }
}

fn convert_font(attrs: &svgdom::Attributes) -> tree::Font {
    let style = attrs.get_predef(AId::FontStyle).unwrap_or(svgdom::ValueId::Normal);
    let style = match style {
        svgdom::ValueId::Normal => tree::FontStyle::Normal,
        svgdom::ValueId::Italic => tree::FontStyle::Italic,
        svgdom::ValueId::Oblique => tree::FontStyle::Oblique,
        _ => tree::FontStyle::Normal,
    };

    let variant = attrs.get_predef(AId::FontVariant).unwrap_or(svgdom::ValueId::Normal);
    let variant = match variant {
        svgdom::ValueId::Normal => tree::FontVariant::Normal,
        svgdom::ValueId::SmallCaps => tree::FontVariant::SmallCaps,
        _ => tree::FontVariant::Normal,
    };

    let weight = attrs.get_predef(AId::FontWeight).unwrap_or(svgdom::ValueId::Normal);
    let weight = match weight {
        svgdom::ValueId::Normal => tree::FontWeight::Normal,
        svgdom::ValueId::Bold => tree::FontWeight::Bold,
        svgdom::ValueId::Bolder => tree::FontWeight::Bolder,
        svgdom::ValueId::Lighter => tree::FontWeight::Lighter,
        svgdom::ValueId::N100 => tree::FontWeight::W100,
        svgdom::ValueId::N200 => tree::FontWeight::W200,
        svgdom::ValueId::N300 => tree::FontWeight::W300,
        svgdom::ValueId::N400 => tree::FontWeight::W400,
        svgdom::ValueId::N500 => tree::FontWeight::W500,
        svgdom::ValueId::N600 => tree::FontWeight::W600,
        svgdom::ValueId::N700 => tree::FontWeight::W700,
        svgdom::ValueId::N800 => tree::FontWeight::W800,
        svgdom::ValueId::N900 => tree::FontWeight::W900,
        _ => tree::FontWeight::Normal,
    };

    let stretch = attrs.get_predef(AId::FontStretch).unwrap_or(svgdom::ValueId::Normal);
    let stretch = match stretch {
        svgdom::ValueId::Normal => tree::FontStretch::Normal,
        svgdom::ValueId::Wider => tree::FontStretch::Wider,
        svgdom::ValueId::Narrower => tree::FontStretch::Narrower,
        svgdom::ValueId::UltraCondensed => tree::FontStretch::UltraCondensed,
        svgdom::ValueId::ExtraCondensed => tree::FontStretch::ExtraCondensed,
        svgdom::ValueId::Condensed => tree::FontStretch::Condensed,
        svgdom::ValueId::SemiCondensed => tree::FontStretch::SemiCondensed,
        svgdom::ValueId::SemiExpanded => tree::FontStretch::SemiExpanded,
        svgdom::ValueId::Expanded => tree::FontStretch::Expanded,
        svgdom::ValueId::ExtraExpanded => tree::FontStretch::ExtraExpanded,
        svgdom::ValueId::UltraExpanded => tree::FontStretch::UltraExpanded,
        _ => tree::FontStretch::Normal,
    };

    // TODO: remove text nodes with font-size <= 0
    let size = attrs.get_number(AId::FontSize).unwrap_or(::DEFAULT_FONT_SIZE);
    debug_assert!(size > 0.0);

    let family = attrs.get_string(AId::FontFamily)
                      .unwrap_or(&::DEFAULT_FONT_FAMILY.to_owned()).clone();

    tree::Font {
        family,
        size,
        style,
        variant,
        weight,
        stretch,
    }
}
<|endoftext|>


<commit_before>use linux;
use log;

use log::{LogRecord, LogLevel, LogMetadata};

#[derive(Default)]
struct DMesgLogger(());

impl log::Log for DMesgLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let log_message = format!("{}: {}", record.level(), record.args());
            unsafe {
                linux::printk(cstring!("%s\n"), log_message);
            }
        }
    }

}

pub fn init() -> Result<(), log::SetLoggerError> {
    unsafe {
        log::set_logger_raw(|max_log_level| {
            static LOGGER: DMesgLogger = DMesgLogger(());
            max_log_level.set(log::LogLevelFilter::Info);
            &LOGGER
        })
    }
}
pub fn shutdown() -> Result<(), log::ShutdownLoggerError> {
    log::shutdown_logger_raw().map(|_logger| { })
}
<commit_msg>Rust String != C *char<commit_after>use linux;
use log;

use log::{LogRecord, LogLevel, LogMetadata};

#[derive(Default)]
struct DMesgLogger(());

impl log::Log for DMesgLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let log_message = format!("{}: {}\0", record.level(), record.args());
            unsafe {
                linux::printk(cstring!("%s\n"), log_message.as_ptr());
            }
        }
    }

}

pub fn init() -> Result<(), log::SetLoggerError> {
    unsafe {
        log::set_logger_raw(|max_log_level| {
            static LOGGER: DMesgLogger = DMesgLogger(());
            max_log_level.set(log::LogLevelFilter::Info);
            &LOGGER
        })
    }
}
pub fn shutdown() -> Result<(), log::ShutdownLoggerError> {
    log::shutdown_logger_raw().map(|_logger| { })
}
<|endoftext|>


<commit_before>use util::units::{Direction, Point};

/// A single actor in the game.
///
/// # Example
///
/// ```
/// use rslike::engine::Actor;
/// use rslike::util::units::Point;
///
/// let actor = Actor::new("Dog", Point::zero(), 100);
/// ```
pub struct Actor {
    name: String,
    pos: Point,
    health: i32,
    max_health: i32,
}

impl Actor {

    /// Creates a new actor.
    pub fn new(name: &'static str, pos: Point, max_health: i32) -> Actor {
        Actor {
            name: name.to_string(),
            pos: pos,
            health: max_health,
            max_health: max_health,
        }
    }

    pub fn name(&self) -> &str { &self.name }
    pub fn pos(&self) -> &Point { &self.pos }
    pub fn health(&self) -> i32 { self.health }
    pub fn max_health(&self) -> i32 { self.max_health }

    /// Moves the actor one step in the specified `Direction`
    pub fn walk(&mut self, direction: Direction) {
        self.pos = self.pos.move_dir(direction);
    }

    pub fn is_dead(&self) -> bool {
        self.health <= 0
    }

    pub fn hurt(&mut self, amount: i32) {
        self.health -= amount;
    }

    pub fn heal(&mut self, amount: i32) {
        self.health += amount;
    }

    pub fn kill(&mut self) {
        self.health = 0;
    }

}
<commit_msg>Remove full-stops from one-line comments<commit_after>use util::units::{Direction, Point};

/// A single actor in the game
///
/// # Example
///
/// ```
/// use rslike::engine::Actor;
/// use rslike::util::units::Point;
///
/// let actor = Actor::new("Dog", Point::zero(), 100);
/// ```
pub struct Actor {
    name: String,
    pos: Point,
    health: i32,
    max_health: i32,
}

impl Actor {

    /// Creates a new actor
    pub fn new(name: &'static str, pos: Point, max_health: i32) -> Actor {
        Actor {
            name: name.to_string(),
            pos: pos,
            health: max_health,
            max_health: max_health,
        }
    }

    pub fn name(&self) -> &str { &self.name }
    pub fn pos(&self) -> &Point { &self.pos }
    pub fn health(&self) -> i32 { self.health }
    pub fn max_health(&self) -> i32 { self.max_health }

    /// Moves the actor one step in the specified `Direction`
    pub fn walk(&mut self, direction: Direction) {
        self.pos = self.pos.move_dir(direction);
    }

    /// Returns true if the actor's health is equal to or below zero
    pub fn is_dead(&self) -> bool {
        self.health <= 0
    }

    /// Reduces the actor's health by a specified amount
    pub fn hurt(&mut self, amount: i32) {
        self.health -= amount;
    }

    /// Increases the actor's health by a specified amount
    pub fn heal(&mut self, amount: i32) {
        self.health += amount;
    }

    /// Reduces the actor's health to zero
    pub fn kill(&mut self) {
        self.health = 0;
    }

}
<|endoftext|>


<commit_before>use regex::Regex;
use chrono::NaiveDate;
use reqwest;
use std::io::Read;
use select::document::Document;
use select::predicate::{Name, Attr};
use rustc_serialize::json::Json;
use errors::*;

// Some formats borrowed from https://github.com/amir/article-date-extractor
static FMTS: &'static [&str] = &["%A, %B %e, %Y", "%Y-%m-%dT%H:%M:%S%:z", "/%Y/%m/%d/", "/%Y/%d/%m/", "%Y-%m-%d", "%B %e, %Y", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%SZ", "%B %k, %Y, %H:%M %p", "%Y-%m-%d %H:%M:%S.000000"];

// Use lazy_static to ensure we only compile the regex once
lazy_static! {
    // Regex by Newspaper3k  - https://github.com/codelucas/newspaper/blob/master/newspaper/urls.py
    static ref RE: Regex = Regex::new(r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})").unwrap();
}

// Parse the date, trying out each format
fn parse_date(input: &str) -> Result<NaiveDate> {
    let mut result: Result<NaiveDate> = Err("None of the formats matched the date".into());

    'outer: for fmt in FMTS {
        if let Ok(v) = NaiveDate::parse_from_str(input, fmt) {
            { result = Ok(v);
              break 'outer; }
        }
    }

    result
}

// Extract date from a URL
fn extract_from_url(url: &str) -> Option<String> {
    if let Some(val) = RE.find(url) {
        return Some(val.as_str().to_string())
    } else {
        return None
    }
}

// Extract date from JSON-LD
fn extract_from_ldjson<'a>(html: &'a Document) -> Option<String> {
    let mut json_date: Option<String> = None;
    let mut ldjson = String::new();
    if let Some(ldj) = html.find(Attr("type", "application/ld+json")).next() {
        ldjson = ldj.text();
    }

    let mut decoded_ldjson = Json::from_str("{}").unwrap();

    match Json::from_str(ldjson.as_str()) {
        Ok(v) => decoded_ldjson = v,
        _ => return None,
    }

    if let Some(date_published) = decoded_ldjson.search("datePublished") {
        if let Some(date) = date_published.as_string() {
            json_date = Some(date.to_string())
        }
    } else if let Some(date_created) = decoded_ldjson.search("dateCreated") {
        if let Some(date) = date_created.as_string() {
            json_date = Some(date.to_string())
        }
    }

    json_date
}

// Extract date from meta tags
fn extract_from_meta<'a>(html: &'a Document) -> Option<String> {
    let mut meta_date: Option<String> = None;

    'outer: for meta in html.find(Name("meta")) {
        let meta_name     = meta.attr("name").unwrap_or("").to_lowercase();
        let item_prop     = meta.attr("itemprop").unwrap_or("").to_lowercase();
        let http_equiv    = meta.attr("http-equiv").unwrap_or("").to_lowercase();
        let meta_property = meta.attr("property").unwrap_or("").to_lowercase();

        match meta_name.as_ref() {
            "pubdate"               | "publishdate"                  | "timestamp"       |
            "dc.date.issued"        | "date"                         | "sailthru.date"   |
            "article.published"     | "published-date"               | "article.created" |
            "article_date_original" | "cxenseparse:recs:publishtime" | "date_published"  => { if let Some(ct) = meta.attr("content") {
                                                                                meta_date = Some(ct.trim().to_string())
                                                                                }
                                                                                break 'outer; },
            _ => {},
        }

        match item_prop.as_ref() {
            "datepublished" | "datecreated" => { if let Some(ct) = meta.attr("content") {
                                                   meta_date = Some(ct.trim().to_string())
                                                 }
                                                 break 'outer; },
            _ => {},
        }

        match http_equiv.as_ref() {
            "date" =>  { if let Some(ct) = meta.attr("content") {
                           meta_date = Some(ct.trim().to_string())
                         }
                         break 'outer; },
            _ => {},
        }

        match meta_property.as_ref() {
            "article:published_time" | "bt:pubdate" => { if let Some(ct) = meta.attr("content") {
                                                           meta_date = Some(ct.trim().to_string())
                                                         }
                                                         break 'outer; },
            "og:image"                              => { if let Some(url) = meta.attr("content") {
                                                           meta_date = extract_from_url(url.trim())
                                                         }
                                                         break 'outer; },

            _ => {},
        }

    }

    meta_date
}

// Extract from html tags
fn extract_from_html_tag<'a>(html: &'a Document) -> Option<String> {
    lazy_static! {
        static ref TAG_RE: Regex = Regex::new(r"(?i)publishdate|pubdate|timestamp|article_date|articledate|date").unwrap();
    }

    let mut date: Option<String> = None;

    'initial: for time in html.find(Name("time")) {
        if let Some(dt) = time.attr("datetime") {
            { date = Some(dt.to_string());
              break 'initial; }
        } else if let Some("timestamp") = time.attr("class") {
            { date = Some(time.text().trim_matches('\n').to_string());
              break 'initial; }
        }
    }

    if date.is_none() {
        'outer: for tag in html.find(Name("span")) {
            if let Some("datePublished") = tag.attr("itemprop") {
                if let Some(v) = tag.attr("content") {
                    { date = Some(v.to_string());
                      break 'outer; }
                } else if tag.text() != "" {
                    { date = Some(tag.text().trim_matches('\n').to_string());
                      break 'outer; }
                }
            }
        }
    }

    // These next three loops are due to the lack of `find_all` method for select.rs library
    // See also: https://github.com/Webhose/article-date-extractor/blob/master/articleDateExtractor/__init__.py#L191
    if date.is_none() {
        'outer_first: for tag in html.find(Name("span")) {
            if TAG_RE.is_match(tag.attr("class").unwrap_or("")) {
                { date = Some(tag.text().trim_matches('\n').to_string());
                  break 'outer_first; }
            }
        }
    }

    if date.is_none() {
        'outer_second: for tag in html.find(Name("p")) {
            if TAG_RE.is_match(tag.attr("class").unwrap_or("")) {
                { date = Some(tag.text().trim_matches('\n').to_string());
                  break 'outer_second; }
            }
        }
    }

    if date.is_none() {
        'outer_third: for tag in html.find(Name("div")) {
            if TAG_RE.is_match(tag.attr("class").unwrap_or("")) {
                { date = Some(tag.text().trim_matches('\n').to_string());
                  break 'outer_third; }
            }
        }
    }

    date
}

// Try to extract the date by using each function one by one
pub fn extract_article_published_date(link: &str, html: Option<String>) -> Result<NaiveDate> {
    let mut body: String = String::new();
    let mut parsed_body: Option<Document> = None;

    if let Some(v) = extract_from_url(link) {
        return parse_date(v.as_str())
    }

    if html.is_none() {
        if let Ok(mut response) = reqwest::get(link) {
            response.read_to_string(&mut body).unwrap();
            let doc = Document::from(body.as_str());
            parsed_body = Some(doc);
        } else {
            return Err("Couldn't open the link".into())
        }
    } else {
        parsed_body = Some(Document::from(html.unwrap().as_str()))
    }

    if let Some(v) = extract_from_url(link) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_ldjson(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_meta(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_html_tag(parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else {
        return Err("Couldn't find the date to parse".into())
    }
}

// Unit tests
#[cfg(test)]
mod test {
    use super::extract_from_url;
    use super::parse_date;
    use super::extract_from_meta;
    use super::extract_from_ldjson;
    use super::extract_from_html_tag;
    use chrono::NaiveDate;
    use reqwest;
    use std::io::Read;
    use select::document::Document;

    #[test]
    fn parsing_date() {
        assert_eq!(NaiveDate::from_ymd(2015,11,30), parse_date("/2015/11/30/").unwrap());
        assert_eq!(NaiveDate::from_ymd(2015,11,30), parse_date("/2015/30/11/").unwrap());

        assert!(parse_date("bad_format").is_err());
    }

    #[test]
    fn extracting_from_url() {
        let link = "http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html";
        assert_eq!(Some("/2015/11/28/".to_string()), extract_from_url(link));

        let link = "";
        assert_eq!(None, extract_from_url(link));
    }

    #[test]
    fn extracting_from_ldjson() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some("2015-12-01T07:50:48Z".to_string()), extract_from_ldjson(&document));
    }

    #[test]
    fn extracting_from_meta() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some(("2015-11-30 23:50:48".to_string())), extract_from_meta(&document));
    }

    #[test]
    fn extracting_from_html_tag() {
        let mut response = reqwest::get("https://research.googleblog.com/2017/03/announcing-guetzli-new-open-source-jpeg.html").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some("Thursday, March 16, 2017".to_string()), extract_from_html_tag(&document));
    }
}
<commit_msg>Remove "unused variable" warnings<commit_after>use regex::Regex;
use chrono::NaiveDate;
use reqwest;
use std::io::Read;
use select::document::Document;
use select::predicate::{Name, Attr};
use rustc_serialize::json::Json;
use errors::*;

// Some formats borrowed from https://github.com/amir/article-date-extractor
static FMTS: &'static [&str] = &["%A, %B %e, %Y", "%Y-%m-%dT%H:%M:%S%:z", "/%Y/%m/%d/", "/%Y/%d/%m/", "%Y-%m-%d", "%B %e, %Y", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%SZ", "%B %k, %Y, %H:%M %p", "%Y-%m-%d %H:%M:%S.000000"];

// Use lazy_static to ensure we only compile the regex once
lazy_static! {
    // Regex by Newspaper3k  - https://github.com/codelucas/newspaper/blob/master/newspaper/urls.py
    static ref RE: Regex = Regex::new(r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})").unwrap();
}

// Parse the date, trying out each format
fn parse_date(input: &str) -> Result<NaiveDate> {
    let mut result: Result<NaiveDate> = Err("None of the formats matched the date".into());

    'outer: for fmt in FMTS {
        if let Ok(v) = NaiveDate::parse_from_str(input, fmt) {
            { result = Ok(v);
              break 'outer; }
        }
    }

    result
}

// Extract date from a URL
fn extract_from_url(url: &str) -> Option<String> {
    if let Some(val) = RE.find(url) {
        return Some(val.as_str().to_string())
    } else {
        return None
    }
}

// Extract date from JSON-LD
fn extract_from_ldjson<'a>(html: &'a Document) -> Option<String> {
    let mut json_date: Option<String> = None;
    let mut ldjson = String::new();
    if let Some(ldj) = html.find(Attr("type", "application/ld+json")).next() {
        ldjson = ldj.text();
    }

    let mut _decoded_ldjson = Json::from_str("{}").unwrap();

    match Json::from_str(ldjson.as_str()) {
        Ok(v) => _decoded_ldjson = v,
        _ => return None,
    }

    if let Some(date_published) = _decoded_ldjson.search("datePublished") {
        if let Some(date) = date_published.as_string() {
            json_date = Some(date.to_string())
        }
    } else if let Some(date_created) = _decoded_ldjson.search("dateCreated") {
        if let Some(date) = date_created.as_string() {
            json_date = Some(date.to_string())
        }
    }

    json_date
}

// Extract date from meta tags
fn extract_from_meta<'a>(html: &'a Document) -> Option<String> {
    let mut meta_date: Option<String> = None;

    'outer: for meta in html.find(Name("meta")) {
        let meta_name     = meta.attr("name").unwrap_or("").to_lowercase();
        let item_prop     = meta.attr("itemprop").unwrap_or("").to_lowercase();
        let http_equiv    = meta.attr("http-equiv").unwrap_or("").to_lowercase();
        let meta_property = meta.attr("property").unwrap_or("").to_lowercase();

        match meta_name.as_ref() {
            "pubdate"               | "publishdate"                  | "timestamp"       |
            "dc.date.issued"        | "date"                         | "sailthru.date"   |
            "article.published"     | "published-date"               | "article.created" |
            "article_date_original" | "cxenseparse:recs:publishtime" | "date_published"  => { if let Some(ct) = meta.attr("content") {
                                                                                meta_date = Some(ct.trim().to_string())
                                                                                }
                                                                                break 'outer; },
            _ => {},
        }

        match item_prop.as_ref() {
            "datepublished" | "datecreated" => { if let Some(ct) = meta.attr("content") {
                                                   meta_date = Some(ct.trim().to_string())
                                                 }
                                                 break 'outer; },
            _ => {},
        }

        match http_equiv.as_ref() {
            "date" =>  { if let Some(ct) = meta.attr("content") {
                           meta_date = Some(ct.trim().to_string())
                         }
                         break 'outer; },
            _ => {},
        }

        match meta_property.as_ref() {
            "article:published_time" | "bt:pubdate" => { if let Some(ct) = meta.attr("content") {
                                                           meta_date = Some(ct.trim().to_string())
                                                         }
                                                         break 'outer; },
            "og:image"                              => { if let Some(url) = meta.attr("content") {
                                                           meta_date = extract_from_url(url.trim())
                                                         }
                                                         break 'outer; },

            _ => {},
        }

    }

    meta_date
}

// Extract from html tags
fn extract_from_html_tag<'a>(html: &'a Document) -> Option<String> {
    lazy_static! {
        static ref TAG_RE: Regex = Regex::new(r"(?i)publishdate|pubdate|timestamp|article_date|articledate|date").unwrap();
    }

    let mut date: Option<String> = None;

    'initial: for time in html.find(Name("time")) {
        if let Some(dt) = time.attr("datetime") {
            { date = Some(dt.to_string());
              break 'initial; }
        } else if let Some("timestamp") = time.attr("class") {
            { date = Some(time.text().trim_matches('\n').to_string());
              break 'initial; }
        }
    }

    if date.is_none() {
        'outer: for tag in html.find(Name("span")) {
            if let Some("datePublished") = tag.attr("itemprop") {
                if let Some(v) = tag.attr("content") {
                    { date = Some(v.to_string());
                      break 'outer; }
                } else if tag.text() != "" {
                    { date = Some(tag.text().trim_matches('\n').to_string());
                      break 'outer; }
                }
            }
        }
    }

    // These next three loops are due to the lack of `find_all` method for select.rs library
    // See also: https://github.com/Webhose/article-date-extractor/blob/master/articleDateExtractor/__init__.py#L191
    if date.is_none() {
        'outer_first: for tag in html.find(Name("span")) {
            if TAG_RE.is_match(tag.attr("class").unwrap_or("")) {
                { date = Some(tag.text().trim_matches('\n').to_string());
                  break 'outer_first; }
            }
        }
    }

    if date.is_none() {
        'outer_second: for tag in html.find(Name("p")) {
            if TAG_RE.is_match(tag.attr("class").unwrap_or("")) {
                { date = Some(tag.text().trim_matches('\n').to_string());
                  break 'outer_second; }
            }
        }
    }

    if date.is_none() {
        'outer_third: for tag in html.find(Name("div")) {
            if TAG_RE.is_match(tag.attr("class").unwrap_or("")) {
                { date = Some(tag.text().trim_matches('\n').to_string());
                  break 'outer_third; }
            }
        }
    }

    date
}

// Try to extract the date by using each function one by one
pub fn extract_article_published_date(link: &str, html: Option<String>) -> Result<NaiveDate> {
    let mut body: String = String::new();
    let mut _parsed_body: Option<Document> = None;

    if let Some(v) = extract_from_url(link) {
        return parse_date(v.as_str())
    }

    if html.is_none() {
        if let Ok(mut response) = reqwest::get(link) {
            response.read_to_string(&mut body).unwrap();
            let doc = Document::from(body.as_str());
            _parsed_body = Some(doc);
        } else {
            return Err("Couldn't open the link".into())
        }
    } else {
        _parsed_body = Some(Document::from(html.unwrap().as_str()))
    }

    if let Some(v) = extract_from_url(link) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_ldjson(_parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_meta(_parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else if let Some(v) = extract_from_html_tag(_parsed_body.as_ref().unwrap()) {
        return parse_date(v.as_str())
    } else {
        return Err("Couldn't find the date to parse".into())
    }
}

// Unit tests
#[cfg(test)]
mod test {
    use super::extract_from_url;
    use super::parse_date;
    use super::extract_from_meta;
    use super::extract_from_ldjson;
    use super::extract_from_html_tag;
    use chrono::NaiveDate;
    use reqwest;
    use std::io::Read;
    use select::document::Document;

    #[test]
    fn parsing_date() {
        assert_eq!(NaiveDate::from_ymd(2015,11,30), parse_date("/2015/11/30/").unwrap());
        assert_eq!(NaiveDate::from_ymd(2015,11,30), parse_date("/2015/30/11/").unwrap());

        assert!(parse_date("bad_format").is_err());
    }

    #[test]
    fn extracting_from_url() {
        let link = "http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html";
        assert_eq!(Some("/2015/11/28/".to_string()), extract_from_url(link));

        let link = "";
        assert_eq!(None, extract_from_url(link));
    }

    #[test]
    fn extracting_from_ldjson() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some("2015-12-01T07:50:48Z".to_string()), extract_from_ldjson(&document));
    }

    #[test]
    fn extracting_from_meta() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some(("2015-11-30 23:50:48".to_string())), extract_from_meta(&document));
    }

    #[test]
    fn extracting_from_html_tag() {
        let mut response = reqwest::get("https://research.googleblog.com/2017/03/announcing-guetzli-new-open-source-jpeg.html").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Some("Thursday, March 16, 2017".to_string()), extract_from_html_tag(&document));
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Erik Nordstrøm <erik@nordstroem.no>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

use self::Color::*;
use self::Rank::*;
use std::slice::Iter;
use std::mem;

extern crate rand;
use self::rand::Rng;

#[derive(Copy, Clone)]
pub enum Color
{
    Spades,   // ♠,
    Hearts,   // ♥,
    Diamonds, // ♦,
    Clubs,    // ♣
}

#[derive(Copy, Clone)]
pub enum Rank
{
    A     = 1,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    J,
    Q,
    K
}

impl Color
{
    // https://stackoverflow.com/a/21376984
    fn iterator () -> Iter<'static, Color>
    {
        static COLORS: [Color; 4] = [Spades, Hearts, Diamonds, Clubs];
        COLORS.into_iter()
    }
}

impl Rank
{
    fn iterator () -> Iter<'static, Rank>
    {
        static RANKS: [Rank; 13] =
            [ A, Two, Three, Four, Five, Six, Seven,
              Eight, Nine, Ten, J, Q, K ] ;
        RANKS.into_iter()
    }
}

pub struct Card
{
    pub color:     Color,
    pub rank:      Rank,
    pub id:        u8,
    pub facing_up: bool
}

pub fn shuffled_deck () -> Box<[Card; 52]>
{
    // https://stackoverflow.com/a/31361031
    let all_cards = unsafe
    {
        let mut _all_cards: [Card; 52] = mem::uninitialized();

        let mut ids_available: Vec<u8> = (0..52).collect();

        // https://stackoverflow.com/a/26035435
        let mut ids_avail_slice = ids_available.as_mut_slice();
        rand::thread_rng().shuffle(ids_avail_slice);
        let mut id_iter = ids_avail_slice.iter_mut();

        for color in Color::iterator()
        {
            for rank in Rank::iterator()
            {

                let curr_id = *(id_iter.next().unwrap());

                let card = Card
                {
                    color:     *color,
                    rank:      *rank,
                    id:        curr_id,
                    facing_up: false
                };

                _all_cards[curr_id as usize] = card;
            }
        }

        _all_cards
    };

    Box::new(all_cards)
}
<commit_msg>ids_avail_slice does not need to be mutable.<commit_after>/*
 * Copyright (c) 2017 Erik Nordstrøm <erik@nordstroem.no>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

use self::Color::*;
use self::Rank::*;
use std::slice::Iter;
use std::mem;

extern crate rand;
use self::rand::Rng;

#[derive(Copy, Clone)]
pub enum Color
{
    Spades,   // ♠,
    Hearts,   // ♥,
    Diamonds, // ♦,
    Clubs,    // ♣
}

#[derive(Copy, Clone)]
pub enum Rank
{
    A     = 1,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    J,
    Q,
    K
}

impl Color
{
    // https://stackoverflow.com/a/21376984
    fn iterator () -> Iter<'static, Color>
    {
        static COLORS: [Color; 4] = [Spades, Hearts, Diamonds, Clubs];
        COLORS.into_iter()
    }
}

impl Rank
{
    fn iterator () -> Iter<'static, Rank>
    {
        static RANKS: [Rank; 13] =
            [ A, Two, Three, Four, Five, Six, Seven,
              Eight, Nine, Ten, J, Q, K ] ;
        RANKS.into_iter()
    }
}

pub struct Card
{
    pub color:     Color,
    pub rank:      Rank,
    pub id:        u8,
    pub facing_up: bool
}

pub fn shuffled_deck () -> Box<[Card; 52]>
{
    // https://stackoverflow.com/a/31361031
    let all_cards = unsafe
    {
        let mut _all_cards: [Card; 52] = mem::uninitialized();

        let mut ids_available: Vec<u8> = (0..52).collect();

        // https://stackoverflow.com/a/26035435
        let ids_avail_slice = ids_available.as_mut_slice();
        rand::thread_rng().shuffle(ids_avail_slice);
        let mut id_iter = ids_avail_slice.iter_mut();

        for color in Color::iterator()
        {
            for rank in Rank::iterator()
            {

                let curr_id = *(id_iter.next().unwrap());

                let card = Card
                {
                    color:     *color,
                    rank:      *rank,
                    id:        curr_id,
                    facing_up: false
                };

                _all_cards[curr_id as usize] = card;
            }
        }

        _all_cards
    };

    Box::new(all_cards)
}
<|endoftext|>


<commit_before>use bifrost_hasher::hash_str;
use index::client::IndexComps::Vectorized;
use index::EntryKey;
use ram::cell::Cell;
use ram::schema::{Field, IndexType, Schema};
use ram::types::{Id, Value};
use smallvec::SmallVec;

type FieldName = String;
type Component = [u8; 8];
const UNSETTLED: Component = [0u8; 8];

// Define index rules
// Index can be applied on scala value and scala arrays for both Ranged and Hashed
// Only scala can be use vectorization index
// String for ranged will take first 64-bit, hash will hash the string
// Index on nested fields are allowed

pub struct RangedIndexMeta {
    key: EntryKey,
}

pub struct HashedIndexMeta {
    id: Id,
}

pub struct VectorizedMeta {
    cell_id: Id,
    feature: Component,
    details: Option<VectorizedDetails>,
}

pub struct VectorizedDetails {
    vec_id: Id,
    index: u32,
}

pub enum IndexMeta {
    Ranged(RangedIndexMeta),
    Hashed(HashedIndexMeta),
    Vectorized(VectorizedMeta),
}

pub enum IndexComps {
    Ranged(Component),
    Hashed(Component),
    Vectorized(Component),
}

pub struct IndexRes {
    id: FieldName,
    meta: Vec<IndexMeta>,
}

pub fn make_indices(cell: &Cell, schema: &Schema) {}

pub fn remove_indies(indices: Vec<IndexMeta>) {}

pub fn ensure_indices(cell: &Cell, schema: &Schema, old_indices: Vec<IndexRes>) {}

pub fn probe_cell_indices(cell: &Cell, schema: &Schema) -> Vec<IndexRes> {
    let mut res = vec![];
    probe_field_indices(
        cell.id(),
        &"".to_string(),
        &schema.fields,
        schema.id,
        &cell.data,
        &mut res,
    );
    res
}

fn probe_field_indices(
    id: Id,
    prefix: &FieldName,
    field: &Field,
    schema_id: u32,
    value: &Value,
    metas: &mut Vec<IndexRes>,
) {
    let mut field_name = format!("{} -> {}", prefix, field.name);
    if let &Some(ref fields) = &field.sub_fields {
        for field in fields {
            let value = &value[field.name_id];
            probe_field_indices(id, &field_name, field, schema_id, value, metas);
        }
    } else {
        let mut components = vec![];
        if let &Value::Array(ref array) = value {
            for val in array {
                probe_field_indices(id, &field_name, field, schema_id, value, metas);
            }
        } else if let &Value::PrimArray(ref array) = value {
            for index in &field.indices {
                match index {
                    &IndexType::Ranged => components.append(
                        &mut array
                            .features()
                            .into_iter()
                            .map(|vec| IndexComps::Ranged(vec))
                            .collect(),
                    ),
                    &IndexType::Hashed => components.append(
                        &mut array
                            .hashes()
                            .into_iter()
                            .map(|vec| IndexComps::Hashed(vec))
                            .collect(),
                    ),
                    &IndexType::Vectorized => components.append(
                        &mut array
                            .features()
                            .into_iter()
                            .map(|vec| IndexComps::Vectorized(vec))
                            .collect(),
                    ),
                }
            }
        } else {
            for index in &field.indices {
                match index {
                    &IndexType::Ranged => components.push(IndexComps::Ranged(value.feature())),
                    &IndexType::Hashed => components.push(IndexComps::Hashed(value.hash())),
                    &IndexType::Vectorized => {
                        components.push(IndexComps::Vectorized(value.feature()))
                    }
                }
            }
        }
        let mut metas = vec![];
        for comp in components {
            match comp {
                IndexComps::Hashed(c) => {
                    if c == UNSETTLED {
                        continue;
                    }
                    let id = Id::from_obj(&(schema_id, field_name.clone(), c));
                    metas.push(IndexMeta::Hashed(HashedIndexMeta { id }));
                }
                IndexComps::Ranged(c) => {
                    if c == UNSETTLED {
                        continue;
                    }
                    let mut key = EntryKey::new();
                    let field = hash_str(&field_name);
                    key.extend_from_slice(&id.to_binary()); // Id
                    key.extend_from_slice(&c); // value
                    key.extend_from_slice(&field.to_be_bytes()); // field
                    key.extend_from_slice(&schema_id.to_be_bytes()); // schema id
                    metas.push(IndexMeta::Ranged(RangedIndexMeta { key }));
                }
                IndexComps::Vectorized(c) => {
                    if c == UNSETTLED {
                        continue;
                    }
                    metas.push(IndexMeta::Vectorized(VectorizedMeta {
                        cell_id: id,
                        feature: c,
                        details: None,
                    }));
                },
            }
        }
    }
}
<commit_msg>fix prefixing<commit_after>use bifrost_hasher::hash_str;
use index::client::IndexComps::Vectorized;
use index::EntryKey;
use ram::cell::Cell;
use ram::schema::{Field, IndexType, Schema};
use ram::types::{Id, Value};
use smallvec::SmallVec;

type FieldName = String;
type Component = [u8; 8];
const UNSETTLED: Component = [0u8; 8];

// Define index rules
// Index can be applied on scala value and scala arrays for both Ranged and Hashed
// Only scala can be use vectorization index
// String for ranged will take first 64-bit, hash will hash the string
// Index on nested fields are allowed

pub struct RangedIndexMeta {
    key: EntryKey,
}

pub struct HashedIndexMeta {
    id: Id,
}

pub struct VectorizedMeta {
    cell_id: Id,
    feature: Component,
    details: Option<VectorizedDetails>,
}

pub struct VectorizedDetails {
    vec_id: Id,
    index: u32,
}

pub enum IndexMeta {
    Ranged(RangedIndexMeta),
    Hashed(HashedIndexMeta),
    Vectorized(VectorizedMeta),
}

pub enum IndexComps {
    Ranged(Component),
    Hashed(Component),
    Vectorized(Component),
}

pub struct IndexRes {
    id: FieldName,
    meta: Vec<IndexMeta>,
}

pub fn make_indices(cell: &Cell, schema: &Schema) {}

pub fn remove_indies(indices: Vec<IndexMeta>) {}

pub fn ensure_indices(cell: &Cell, schema: &Schema, old_indices: Vec<IndexRes>) {}

pub fn probe_cell_indices(cell: &Cell, schema: &Schema) -> Vec<IndexRes> {
    let mut res = vec![];
    probe_field_indices(
        cell.id(),
        &"".to_string(),
        &schema.fields,
        schema.id,
        &cell.data,
        &mut res,
    );
    res
}

fn probe_field_indices(
    id: Id,
    prefix: &FieldName,
    field: &Field,
    schema_id: u32,
    value: &Value,
    metas: &mut Vec<IndexRes>,
) {
    if let &Some(ref fields) = &field.sub_fields {
        for field in fields {
            let value = &value[field.name_id];
            let prefix = format!("{} -> {}", prefix, field.name);
            probe_field_indices(
                id,
                &prefix,
                field,
                schema_id,
                value,
                metas,
            );
        }
    } else {
        let mut components = vec![];
        if let &Value::Array(ref array) = value {
            for val in array {
                probe_field_indices(id, &prefix, field, schema_id, value, metas);
            }
        } else if let &Value::PrimArray(ref array) = value {
            for index in &field.indices {
                match index {
                    &IndexType::Ranged => components.append(
                        &mut array
                            .features()
                            .into_iter()
                            .map(|vec| IndexComps::Ranged(vec))
                            .collect(),
                    ),
                    &IndexType::Hashed => components.append(
                        &mut array
                            .hashes()
                            .into_iter()
                            .map(|vec| IndexComps::Hashed(vec))
                            .collect(),
                    ),
                    &IndexType::Vectorized => components.append(
                        &mut array
                            .features()
                            .into_iter()
                            .map(|vec| IndexComps::Vectorized(vec))
                            .collect(),
                    ),
                }
            }
        } else {
            for index in &field.indices {
                match index {
                    &IndexType::Ranged => components.push(IndexComps::Ranged(value.feature())),
                    &IndexType::Hashed => components.push(IndexComps::Hashed(value.hash())),
                    &IndexType::Vectorized => {
                        components.push(IndexComps::Vectorized(value.feature()))
                    }
                }
            }
        }
        let mut metas = vec![];
        for comp in components {
            match comp {
                IndexComps::Hashed(c) => {
                    if c == UNSETTLED {
                        continue;
                    }
                    let id = Id::from_obj(&(schema_id, prefix.clone(), c));
                    metas.push(IndexMeta::Hashed(HashedIndexMeta { id }));
                }
                IndexComps::Ranged(c) => {
                    if c == UNSETTLED {
                        continue;
                    }
                    let mut key = EntryKey::new();
                    let field = hash_str(&prefix);
                    key.extend_from_slice(&id.to_binary()); // Id
                    key.extend_from_slice(&c); // value
                    key.extend_from_slice(&field.to_be_bytes()); // field
                    key.extend_from_slice(&schema_id.to_be_bytes()); // schema id
                    metas.push(IndexMeta::Ranged(RangedIndexMeta { key }));
                }
                IndexComps::Vectorized(c) => {
                    if c == UNSETTLED {
                        continue;
                    }
                    metas.push(IndexMeta::Vectorized(VectorizedMeta {
                        cell_id: id,
                        feature: c,
                        details: None,
                    }));
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>
//! library to `gerrit-rust` tool

#![feature(custom_derive)]
#![feature(plugin)]
#![feature(proc_macro)]

#[macro_use] extern crate log;
#[macro_use] extern crate quick_error;
#[macro_use] extern crate serde_derive;
extern crate curl;
extern crate git2;
extern crate semver;
extern crate serde;
extern crate serde_json;
extern crate url;

pub mod call;
pub mod changes;
pub mod config;
pub mod entities;
pub mod error;
pub mod gerrit;
<commit_msg>remove some feature settings not needed since a long time<commit_after>
//! library to `gerrit-rust` tool

#[macro_use] extern crate log;
#[macro_use] extern crate quick_error;
#[macro_use] extern crate serde_derive;
extern crate curl;
extern crate git2;
extern crate semver;
extern crate serde;
extern crate serde_json;
extern crate url;

pub mod call;
pub mod changes;
pub mod config;
pub mod entities;
pub mod error;
pub mod gerrit;
<|endoftext|>


<commit_before>//! An iterator over incoming signals.
//!
//! This provides a higher abstraction over the signals, providing
//! the [`SignalsInfo`] structure which is able to iterate over the
//! incoming signals. The structure is parametrized by an
//! [`Exfiltrator`][self::exfiltrator::Exfiltrator], which specifies what information is returned
//! for each delivered signal. Note that some exfiltrators are behind a feature flag.
//!
//! The [`Signals`] is a type alias for the common case when it is enough to get the signal number.
//!
//! This module (and everything in it) is turned by the `iterator` feature. It is **on** by
//! default, the possibility to turn off is mostly possible for very special purposes (compiling on
//! `<rustc-1.36`, minimizing the amount of code compiled, …). In a sense, this is the highest
//! level abstraction of the crate and the API expected to be used by most of the people.
//!
//! # Examples
//!
//! ```rust
//! extern crate libc;
//! extern crate signal_hook;
//!
//! use std::io::Error;
//!
//! use signal_hook::consts::signal::*;
//! use signal_hook::iterator::Signals;
//!
//! fn main() -> Result<(), Error> {
//!     let mut signals = Signals::new(&[
//!         SIGHUP,
//!         SIGTERM,
//!         SIGINT,
//!         SIGQUIT,
//! #       SIGUSR1,
//!     ])?;
//! #   // A trick to terminate the example when run as doc-test. Not part of the real code.
//! #   signal_hook::low_level::raise(SIGUSR1).unwrap();
//!     'outer: loop {
//!         // Pick up signals that arrived since last time
//!         for signal in signals.pending() {
//!             match signal as libc::c_int {
//!                 SIGHUP => {
//!                     // Reload configuration
//!                     // Reopen the log file
//!                 }
//!                 SIGTERM | SIGINT | SIGQUIT => {
//!                     break 'outer;
//!                 },
//! #               SIGUSR1 => return Ok(()),
//!                 _ => unreachable!(),
//!             }
//!         }
//!         // Do some bit of work ‒ something with upper limit on waiting, so we don't block
//!         // forever with a SIGTERM already waiting.
//!     }
//!     println!("Terminating. Bye bye");
//!     Ok(())
//! }
//! ```

pub mod backend;
pub mod exfiltrator;

use std::borrow::Borrow;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::io::{Error, ErrorKind, Read};
use std::os::unix::net::UnixStream;

use libc::{self, c_int};

pub use self::backend::{Handle, Pending};
use self::backend::{PollResult, RefSignalIterator, SignalDelivery};
use self::exfiltrator::{Exfiltrator, SignalOnly};

/// The main structure of the module, representing interest in some signals.
///
/// Unlike the helpers in other modules, this registers the signals when created and unregisters
/// them on drop. It provides the pending signals during its lifetime, either in batches or as an
/// infinite iterator.
///
/// Most users will want to use it through the [`Signals`] type alias for simplicity.
///
/// # Multiple threads
///
/// Instances of this struct can be [sent][std::marker::Send] to other threads. In a multithreaded
/// application this can be used to dedicate a separate thread for signal handling. In this case
/// you should get a [`Handle`] using the [`handle`][Signals::handle] method before sending the
/// `Signals` instance to a background thread. With the handle you will be able to shut down the
/// background thread later, or to operatively add more signals.
///
/// The controller handle can be shared between as many threads as you like using its
/// [`clone`][Handle::clone] method.
///
/// # Exfiltrators
///
/// The [`SignalOnly]` provides only the signal number. There are further exfiltrators available in
/// the [`exfiltrator`] module. Note that some of them are behind feature flags that need to be
/// enabled.
///
/// # Examples
///
/// ```rust
/// # extern crate signal_hook;
/// #
/// # use std::io::Error;
/// # use std::thread;
/// use signal_hook::consts::signal::*;
/// use signal_hook::iterator::Signals;
///
/// #
/// # fn main() -> Result<(), Error> {
/// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?;
/// let handle = signals.handle();
/// let thread = thread::spawn(move || {
///     for signal in &mut signals {
///         match signal {
///             SIGUSR1 => {},
///             SIGUSR2 => {},
///             _ => unreachable!(),
///         }
///     }
/// });
///
/// // Some time later...
/// handle.close();
/// thread.join().unwrap();
/// # Ok(())
/// # }
/// ```
pub struct SignalsInfo<E: Exfiltrator = SignalOnly>(SignalDelivery<UnixStream, E>);

impl<E: Exfiltrator> SignalsInfo<E> {
    /// Creates the `Signals` structure.
    ///
    /// This registers all the signals listed. The same restrictions (panics, errors) apply as
    /// for the [`Handle::add_signal`] method.
    pub fn new<I, S>(signals: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = S>,
        S: Borrow<c_int>,
        E: Default,
    {
        Self::with_exfiltrator(signals, E::default())
    }

    /// An advanced constructor with explicit [`Exfiltrator`].
    pub fn with_exfiltrator<I, S>(signals: I, exfiltrator: E) -> Result<Self, Error>
    where
        I: IntoIterator<Item = S>,
        S: Borrow<c_int>,
    {
        let (read, write) = UnixStream::pair()?;
        Ok(SignalsInfo(SignalDelivery::with_pipe(
            read,
            write,
            exfiltrator,
            signals,
        )?))
    }

    /// Registers another signal to the set watched by this [`Signals`] instance.
    ///
    /// The same restrictions (panics, errors) apply as for the [`Handle::add_signal`]
    /// method.
    pub fn add_signal(&self, signal: c_int) -> Result<(), Error> {
        self.handle().add_signal(signal)
    }

    /// Returns an iterator of already received signals.
    ///
    /// This returns an iterator over all the signal numbers of the signals received since last
    /// time they were read (out of the set registered by this `Signals` instance). Note that they
    /// are returned in arbitrary order and a signal instance may returned only once even if it was
    /// received multiple times.
    ///
    /// This method returns immediately (does not block) and may produce an empty iterator if there
    /// are no signals ready.
    pub fn pending(&mut self) -> Pending<E> {
        self.0.pending()
    }

    /// Block until the stream contains some bytes.
    ///
    /// Returns true if it was possible to read a byte and false otherwise.
    fn has_signals(read: &mut UnixStream) -> Result<bool, Error> {
        loop {
            match read.read(&mut [0u8]) {
                Ok(num_read) => break Ok(num_read > 0),
                // If we get an EINTR error it is fine to retry reading from the stream.
                // Otherwise we should pass on the error to the caller.
                Err(error) => {
                    if error.kind() != ErrorKind::Interrupted {
                        break Err(error);
                    }
                }
            }
        }
    }

    /// Waits for some signals to be available and returns an iterator.
    ///
    /// This is similar to [`pending`][Signals::pending]. If there are no signals available, it
    /// tries to wait for some to arrive. However, due to implementation details, this still can
    /// produce an empty iterator.
    ///
    /// This can block for arbitrary long time. If the [`Handle::close`] method is used in
    /// another thread this method will return immediately.
    ///
    /// Note that the blocking is done in this method, not in the iterator.
    pub fn wait(&mut self) -> Pending<E> {
        match self.0.poll_pending(&mut Self::has_signals) {
            Ok(Some(pending)) => pending,
            // Because of the blocking has_signals method the poll_pending method
            // only returns None if the instance is closed. But we want to return
            // a possibly empty pending object anyway.
            Ok(None) => self.pending(),
            // Users can't manipulate the internal file descriptors and the way we use them
            // shouldn't produce any errors. So it is OK to panic.
            Err(error) => panic!("Unexpected error: {}", error),
        }
    }

    /// Is it closed?
    ///
    /// See [`close`][Handle::close].
    pub fn is_closed(&self) -> bool {
        self.handle().is_closed()
    }

    /// Get an infinite iterator over arriving signals.
    ///
    /// The iterator's `next()` blocks as necessary to wait for signals to arrive. This is adequate
    /// if you want to designate a thread solely to handling signals. If multiple signals come at
    /// the same time (between two values produced by the iterator), they will be returned in
    /// arbitrary order. Multiple instances of the same signal may be collated.
    ///
    /// This is also the iterator returned by `IntoIterator` implementation on `&mut Signals`.
    ///
    /// This iterator terminates only if explicitly [closed][Handle::close].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # extern crate libc;
    /// # extern crate signal_hook;
    /// #
    /// # use std::io::Error;
    /// # use std::thread;
    /// #
    /// use signal_hook::consts::signal::*;
    /// use signal_hook::iterator::Signals;
    ///
    /// # fn main() -> Result<(), Error> {
    /// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?;
    /// let handle = signals.handle();
    /// thread::spawn(move || {
    ///     for signal in signals.forever() {
    ///         match signal {
    ///             SIGUSR1 => {},
    ///             SIGUSR2 => {},
    ///             _ => unreachable!(),
    ///         }
    ///     }
    /// });
    /// handle.close();
    /// # Ok(())
    /// # }
    /// ```
    pub fn forever(&mut self) -> Forever<E> {
        Forever(RefSignalIterator::new(&mut self.0))
    }

    /// Get a shareable handle to a [`Handle`] for this instance.
    ///
    /// This can be used to add further signals or close the [`Signals`] instance.
    pub fn handle(&self) -> Handle {
        self.0.handle()
    }
}

impl<E> Debug for SignalsInfo<E>
where
    E: Debug + Exfiltrator,
    E::Storage: Debug,
{
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        fmt.debug_tuple("Signals").field(&self.0).finish()
    }
}

impl<'a, E: Exfiltrator> IntoIterator for &'a mut SignalsInfo<E> {
    type Item = E::Output;
    type IntoIter = Forever<'a, E>;
    fn into_iter(self) -> Self::IntoIter {
        self.forever()
    }
}

/// An infinit iterator of arriving signals.
pub struct Forever<'a, E: Exfiltrator>(RefSignalIterator<'a, UnixStream, E>);

impl<'a, E: Exfiltrator> Iterator for Forever<'a, E> {
    type Item = E::Output;

    fn next(&mut self) -> Option<E::Output> {
        match self.0.poll_signal(&mut SignalsInfo::<E>::has_signals) {
            PollResult::Signal(result) => Some(result),
            PollResult::Closed => None,
            PollResult::Pending => unreachable!(
                "Because of the blocking has_signals method the \
                poll_signal method never returns Poll::Pending but blocks until a signal arrived"
            ),
            // Users can't manipulate the internal file descriptors and the way we use them
            // shouldn't produce any errors. So it is OK to panic.
            PollResult::Err(error) => panic!("Unexpected error: {}", error),
        }
    }
}

/// A type alias for an iterator returning just the signal numbers.
///
/// This is the simplified version for most of the use cases. For advanced usages, the
/// [`SignalsInfo`] with explicit [`Exfiltrator`] type can be used.
pub type Signals = SignalsInfo<SignalOnly>;
<commit_msg>typo: Explicit path to SignalInfo::pending<commit_after>//! An iterator over incoming signals.
//!
//! This provides a higher abstraction over the signals, providing
//! the [`SignalsInfo`] structure which is able to iterate over the
//! incoming signals. The structure is parametrized by an
//! [`Exfiltrator`][self::exfiltrator::Exfiltrator], which specifies what information is returned
//! for each delivered signal. Note that some exfiltrators are behind a feature flag.
//!
//! The [`Signals`] is a type alias for the common case when it is enough to get the signal number.
//!
//! This module (and everything in it) is turned by the `iterator` feature. It is **on** by
//! default, the possibility to turn off is mostly possible for very special purposes (compiling on
//! `<rustc-1.36`, minimizing the amount of code compiled, …). In a sense, this is the highest
//! level abstraction of the crate and the API expected to be used by most of the people.
//!
//! # Examples
//!
//! ```rust
//! extern crate libc;
//! extern crate signal_hook;
//!
//! use std::io::Error;
//!
//! use signal_hook::consts::signal::*;
//! use signal_hook::iterator::Signals;
//!
//! fn main() -> Result<(), Error> {
//!     let mut signals = Signals::new(&[
//!         SIGHUP,
//!         SIGTERM,
//!         SIGINT,
//!         SIGQUIT,
//! #       SIGUSR1,
//!     ])?;
//! #   // A trick to terminate the example when run as doc-test. Not part of the real code.
//! #   signal_hook::low_level::raise(SIGUSR1).unwrap();
//!     'outer: loop {
//!         // Pick up signals that arrived since last time
//!         for signal in signals.pending() {
//!             match signal as libc::c_int {
//!                 SIGHUP => {
//!                     // Reload configuration
//!                     // Reopen the log file
//!                 }
//!                 SIGTERM | SIGINT | SIGQUIT => {
//!                     break 'outer;
//!                 },
//! #               SIGUSR1 => return Ok(()),
//!                 _ => unreachable!(),
//!             }
//!         }
//!         // Do some bit of work ‒ something with upper limit on waiting, so we don't block
//!         // forever with a SIGTERM already waiting.
//!     }
//!     println!("Terminating. Bye bye");
//!     Ok(())
//! }
//! ```

pub mod backend;
pub mod exfiltrator;

use std::borrow::Borrow;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::io::{Error, ErrorKind, Read};
use std::os::unix::net::UnixStream;

use libc::{self, c_int};

pub use self::backend::{Handle, Pending};
use self::backend::{PollResult, RefSignalIterator, SignalDelivery};
use self::exfiltrator::{Exfiltrator, SignalOnly};

/// The main structure of the module, representing interest in some signals.
///
/// Unlike the helpers in other modules, this registers the signals when created and unregisters
/// them on drop. It provides the pending signals during its lifetime, either in batches or as an
/// infinite iterator.
///
/// Most users will want to use it through the [`Signals`] type alias for simplicity.
///
/// # Multiple threads
///
/// Instances of this struct can be [sent][std::marker::Send] to other threads. In a multithreaded
/// application this can be used to dedicate a separate thread for signal handling. In this case
/// you should get a [`Handle`] using the [`handle`][Signals::handle] method before sending the
/// `Signals` instance to a background thread. With the handle you will be able to shut down the
/// background thread later, or to operatively add more signals.
///
/// The controller handle can be shared between as many threads as you like using its
/// [`clone`][Handle::clone] method.
///
/// # Exfiltrators
///
/// The [`SignalOnly]` provides only the signal number. There are further exfiltrators available in
/// the [`exfiltrator`] module. Note that some of them are behind feature flags that need to be
/// enabled.
///
/// # Examples
///
/// ```rust
/// # extern crate signal_hook;
/// #
/// # use std::io::Error;
/// # use std::thread;
/// use signal_hook::consts::signal::*;
/// use signal_hook::iterator::Signals;
///
/// #
/// # fn main() -> Result<(), Error> {
/// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?;
/// let handle = signals.handle();
/// let thread = thread::spawn(move || {
///     for signal in &mut signals {
///         match signal {
///             SIGUSR1 => {},
///             SIGUSR2 => {},
///             _ => unreachable!(),
///         }
///     }
/// });
///
/// // Some time later...
/// handle.close();
/// thread.join().unwrap();
/// # Ok(())
/// # }
/// ```
pub struct SignalsInfo<E: Exfiltrator = SignalOnly>(SignalDelivery<UnixStream, E>);

impl<E: Exfiltrator> SignalsInfo<E> {
    /// Creates the `Signals` structure.
    ///
    /// This registers all the signals listed. The same restrictions (panics, errors) apply as
    /// for the [`Handle::add_signal`] method.
    pub fn new<I, S>(signals: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = S>,
        S: Borrow<c_int>,
        E: Default,
    {
        Self::with_exfiltrator(signals, E::default())
    }

    /// An advanced constructor with explicit [`Exfiltrator`].
    pub fn with_exfiltrator<I, S>(signals: I, exfiltrator: E) -> Result<Self, Error>
    where
        I: IntoIterator<Item = S>,
        S: Borrow<c_int>,
    {
        let (read, write) = UnixStream::pair()?;
        Ok(SignalsInfo(SignalDelivery::with_pipe(
            read,
            write,
            exfiltrator,
            signals,
        )?))
    }

    /// Registers another signal to the set watched by this [`Signals`] instance.
    ///
    /// The same restrictions (panics, errors) apply as for the [`Handle::add_signal`]
    /// method.
    pub fn add_signal(&self, signal: c_int) -> Result<(), Error> {
        self.handle().add_signal(signal)
    }

    /// Returns an iterator of already received signals.
    ///
    /// This returns an iterator over all the signal numbers of the signals received since last
    /// time they were read (out of the set registered by this `Signals` instance). Note that they
    /// are returned in arbitrary order and a signal instance may returned only once even if it was
    /// received multiple times.
    ///
    /// This method returns immediately (does not block) and may produce an empty iterator if there
    /// are no signals ready.
    pub fn pending(&mut self) -> Pending<E> {
        self.0.pending()
    }

    /// Block until the stream contains some bytes.
    ///
    /// Returns true if it was possible to read a byte and false otherwise.
    fn has_signals(read: &mut UnixStream) -> Result<bool, Error> {
        loop {
            match read.read(&mut [0u8]) {
                Ok(num_read) => break Ok(num_read > 0),
                // If we get an EINTR error it is fine to retry reading from the stream.
                // Otherwise we should pass on the error to the caller.
                Err(error) => {
                    if error.kind() != ErrorKind::Interrupted {
                        break Err(error);
                    }
                }
            }
        }
    }

    /// Waits for some signals to be available and returns an iterator.
    ///
    /// This is similar to [`pending`][SignalsInfo::pending]. If there are no signals available, it
    /// tries to wait for some to arrive. However, due to implementation details, this still can
    /// produce an empty iterator.
    ///
    /// This can block for arbitrary long time. If the [`Handle::close`] method is used in
    /// another thread this method will return immediately.
    ///
    /// Note that the blocking is done in this method, not in the iterator.
    pub fn wait(&mut self) -> Pending<E> {
        match self.0.poll_pending(&mut Self::has_signals) {
            Ok(Some(pending)) => pending,
            // Because of the blocking has_signals method the poll_pending method
            // only returns None if the instance is closed. But we want to return
            // a possibly empty pending object anyway.
            Ok(None) => self.pending(),
            // Users can't manipulate the internal file descriptors and the way we use them
            // shouldn't produce any errors. So it is OK to panic.
            Err(error) => panic!("Unexpected error: {}", error),
        }
    }

    /// Is it closed?
    ///
    /// See [`close`][Handle::close].
    pub fn is_closed(&self) -> bool {
        self.handle().is_closed()
    }

    /// Get an infinite iterator over arriving signals.
    ///
    /// The iterator's `next()` blocks as necessary to wait for signals to arrive. This is adequate
    /// if you want to designate a thread solely to handling signals. If multiple signals come at
    /// the same time (between two values produced by the iterator), they will be returned in
    /// arbitrary order. Multiple instances of the same signal may be collated.
    ///
    /// This is also the iterator returned by `IntoIterator` implementation on `&mut Signals`.
    ///
    /// This iterator terminates only if explicitly [closed][Handle::close].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # extern crate libc;
    /// # extern crate signal_hook;
    /// #
    /// # use std::io::Error;
    /// # use std::thread;
    /// #
    /// use signal_hook::consts::signal::*;
    /// use signal_hook::iterator::Signals;
    ///
    /// # fn main() -> Result<(), Error> {
    /// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?;
    /// let handle = signals.handle();
    /// thread::spawn(move || {
    ///     for signal in signals.forever() {
    ///         match signal {
    ///             SIGUSR1 => {},
    ///             SIGUSR2 => {},
    ///             _ => unreachable!(),
    ///         }
    ///     }
    /// });
    /// handle.close();
    /// # Ok(())
    /// # }
    /// ```
    pub fn forever(&mut self) -> Forever<E> {
        Forever(RefSignalIterator::new(&mut self.0))
    }

    /// Get a shareable handle to a [`Handle`] for this instance.
    ///
    /// This can be used to add further signals or close the [`Signals`] instance.
    pub fn handle(&self) -> Handle {
        self.0.handle()
    }
}

impl<E> Debug for SignalsInfo<E>
where
    E: Debug + Exfiltrator,
    E::Storage: Debug,
{
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        fmt.debug_tuple("Signals").field(&self.0).finish()
    }
}

impl<'a, E: Exfiltrator> IntoIterator for &'a mut SignalsInfo<E> {
    type Item = E::Output;
    type IntoIter = Forever<'a, E>;
    fn into_iter(self) -> Self::IntoIter {
        self.forever()
    }
}

/// An infinit iterator of arriving signals.
pub struct Forever<'a, E: Exfiltrator>(RefSignalIterator<'a, UnixStream, E>);

impl<'a, E: Exfiltrator> Iterator for Forever<'a, E> {
    type Item = E::Output;

    fn next(&mut self) -> Option<E::Output> {
        match self.0.poll_signal(&mut SignalsInfo::<E>::has_signals) {
            PollResult::Signal(result) => Some(result),
            PollResult::Closed => None,
            PollResult::Pending => unreachable!(
                "Because of the blocking has_signals method the \
                poll_signal method never returns Poll::Pending but blocks until a signal arrived"
            ),
            // Users can't manipulate the internal file descriptors and the way we use them
            // shouldn't produce any errors. So it is OK to panic.
            PollResult::Err(error) => panic!("Unexpected error: {}", error),
        }
    }
}

/// A type alias for an iterator returning just the signal numbers.
///
/// This is the simplified version for most of the use cases. For advanced usages, the
/// [`SignalsInfo`] with explicit [`Exfiltrator`] type can be used.
pub type Signals = SignalsInfo<SignalOnly>;
<|endoftext|>