<commit_before>use cairo;
use cssparser;
use glib::translate::*;
use glib_sys;
use libc;

use std::ptr;
use std::rc::Rc;

use bbox::RsvgBbox;
use drawing_ctx;
use error::*;
use gradient;
use node::NodeType;
use parsers::{Parse, ParseError};
use pattern;
use util::utf8_cstr;

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PaintServerSpread(pub cairo::enums::Extend);

impl Parse for PaintServerSpread {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<PaintServerSpread, AttributeError> {
        match s {
            "pad" => Ok(PaintServerSpread(cairo::enums::Extend::Pad)),
            "reflect" => Ok(PaintServerSpread(cairo::enums::Extend::Reflect)),
            "repeat" => Ok(PaintServerSpread(cairo::enums::Extend::Repeat)),
            _ => Err(AttributeError::Parse(ParseError::new(
                "expected 'pad' | 'reflect' | 'repeat'",
            ))),
        }
    }
}

impl Default for PaintServerSpread {
    fn default() -> PaintServerSpread {
        PaintServerSpread(cairo::enums::Extend::Pad)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum PaintServer {
    Inherit,
    Iri {
        iri: String,
        alternate: Option<cssparser::Color>,
    },
    SolidColor(cssparser::Color),
}

impl PaintServer {
    pub fn parse_input<'i, 't>(
        input: &mut cssparser::Parser<'i, 't>,
    ) -> Result<Self, AttributeError> {
        if input.try(|i| i.expect_ident_matching("inherit")).is_ok() {
            Ok(PaintServer::Inherit)
        } else if let Ok(url) = input.try(|i| i.expect_url()) {
            Ok(PaintServer::Iri {
                iri: String::from(url.as_ref()),
                alternate: PaintServer::parse_fallback(input),
            })
        } else {
            cssparser::Color::parse(input)
                .map(PaintServer::SolidColor)
                .map_err(AttributeError::from)
        }
    }

    fn parse_fallback<'i, 't>(input: &mut cssparser::Parser<'i, 't>) -> Option<cssparser::Color> {
        if input.try(|i| i.expect_ident_matching("none")).is_ok() {
            None
        } else {
            input.try(|i| cssparser::Color::parse(i)).ok()
        }
    }
}

impl Parse for PaintServer {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<PaintServer, AttributeError> {
        let mut input = cssparser::ParserInput::new(s);
        PaintServer::parse_input(&mut cssparser::Parser::new(&mut input))
    }
}

fn _set_source_rsvg_solid_color(
    ctx: *mut drawing_ctx::RsvgDrawingCtx,
    color: &cssparser::Color,
    opacity: u8,
    current_color: &cssparser::RGBA,
) {
    let rgba = match *color {
        cssparser::Color::RGBA(ref rgba) => rgba,
        cssparser::Color::CurrentColor => current_color,
    };

    drawing_ctx::get_cairo_context(ctx).set_source_rgba(
        f64::from(rgba.red_f32()),
        f64::from(rgba.green_f32()),
        f64::from(rgba.blue_f32()),
        f64::from(rgba.alpha_f32()) * (f64::from(opacity) / 255.0),
    );
}

/// Parses the paint specification, creating a new paint server object.
/// Return value: (nullable): The newly created paint server, or NULL on error.
///
/// # Arguments
///
/// * `str` - The SVG paint specification string to parse.
#[no_mangle]
pub extern "C" fn rsvg_paint_server_parse(
    inherit: *mut glib_sys::gboolean,
    str: *const libc::c_char,
) -> *const PaintServer {
    if !inherit.is_null() {
        unsafe {
            *inherit = true.to_glib();
        }
    }

    let paint_server = PaintServer::parse(unsafe { utf8_cstr(str) }, ());

    if let Ok(PaintServer::Inherit) = paint_server {
        if !inherit.is_null() {
            unsafe {
                *inherit = false.to_glib();
            }
        }
    }

    match paint_server {
        Ok(m) => Rc::into_raw(Rc::new(m)),
        Err(_) => ptr::null_mut(),
    }
}

/// Increase references counter of `PaintServer`.
///
/// # Arguments
///
/// * `paint_server` - must be constructed with `rsvg_paint_server_parse`.
#[no_mangle]
pub extern "C" fn rsvg_paint_server_ref(paint_server: *const PaintServer) {
    if paint_server.is_null() {
        return;
    }

    let server: Rc<PaintServer> = unsafe { Rc::from_raw(paint_server) };

    // forget about references
    Rc::into_raw(server.clone());
    Rc::into_raw(server);
}

/// Decrease references counter of `PaintServer`.
///
/// # Arguments
///
/// * `paint_server` - must be constructed with `rsvg_paint_server_parse`.
#[no_mangle]
pub extern "C" fn rsvg_paint_server_unref(paint_server: *const PaintServer) {
    if paint_server.is_null() {
        return;
    }

    // drop reference
    unsafe { Rc::from_raw(paint_server) };
}

pub fn _set_source_rsvg_paint_server(
    c_ctx: *mut drawing_ctx::RsvgDrawingCtx,
    ps: &PaintServer,
    opacity: u8,
    bbox: &RsvgBbox,
    current_color: &cssparser::RGBA,
) -> bool {
    let mut had_paint_server = false;

    match *ps {
        PaintServer::Iri {
            ref iri,
            ref alternate,
        } => {
            if let Some(acquired) = drawing_ctx::get_acquired_node(c_ctx, iri.as_str()) {
                let node = acquired.get();

                if node.get_type() == NodeType::LinearGradient
                    || node.get_type() == NodeType::RadialGradient
                {
                    had_paint_server = gradient::gradient_resolve_fallbacks_and_set_pattern(
                        &node,
                        c_ctx,
                        opacity,
                        bbox,
                    );
                } else if node.get_type() == NodeType::Pattern {
                    had_paint_server =
                        pattern::pattern_resolve_fallbacks_and_set_pattern(&node, c_ctx, bbox);
                }
            }

            if !had_paint_server && alternate.is_some() {
                _set_source_rsvg_solid_color(
                    c_ctx,
                    alternate.as_ref().unwrap(),
                    opacity,
                    current_color,
                );
                had_paint_server = true;
            }
        }

        PaintServer::SolidColor(color) => {
            _set_source_rsvg_solid_color(c_ctx, &color, opacity, current_color);
            had_paint_server = true;
        }

        _ => {}
    };

    had_paint_server
}

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

    #[test]
    fn parses_spread_method() {
        assert_eq!(
            PaintServerSpread::parse("pad", ()),
            Ok(PaintServerSpread(cairo::enums::Extend::Pad))
        );

        assert_eq!(
            PaintServerSpread::parse("reflect", ()),
            Ok(PaintServerSpread(cairo::enums::Extend::Reflect))
        );

        assert_eq!(
            PaintServerSpread::parse("repeat", ()),
            Ok(PaintServerSpread(cairo::enums::Extend::Repeat))
        );

        assert!(PaintServerSpread::parse("foobar", ()).is_err());
    }

    #[test]
    fn parses_inherit() {
        assert_eq!(PaintServer::parse("inherit", ()), Ok(PaintServer::Inherit));
    }

    #[test]
    fn parses_solid_color() {
        assert_eq!(
            PaintServer::parse("rgb(255, 128, 64, 0.5)", ()),
            Ok(PaintServer::SolidColor(cssparser::Color::RGBA(
                cssparser::RGBA::new(255, 128, 64, 128)
            )))
        );

        assert_eq!(
            PaintServer::parse("currentColor", ()),
            Ok(PaintServer::SolidColor(cssparser::Color::CurrentColor))
        );
    }

    #[test]
    fn parses_iri() {
        assert_eq!(
            PaintServer::parse("url(#link)", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) none", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) #ff8040", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::RGBA(cssparser::RGBA::new(
                    255,
                    128,
                    64,
                    255
                ))),
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) rgb(255, 128, 64, 0.5)", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::RGBA(cssparser::RGBA::new(
                    255,
                    128,
                    64,
                    128
                ))),
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) currentColor", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::CurrentColor),
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) inherit", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );
    }

    #[test]
    fn paint_server_refs_and_unrefs() {
        let rc = Rc::new(PaintServer::parse("#ffffff", ()).unwrap());
        let weak = Rc::downgrade(&rc);
        let ps = Rc::into_raw(rc);

        rsvg_paint_server_ref(ps);
        assert!(weak.upgrade().is_some());

        rsvg_paint_server_unref(ps);
        assert!(weak.upgrade().is_some());

        rsvg_paint_server_unref(ps);
        assert!(weak.upgrade().is_none());
    }
}
<commit_msg>paint_server: fix fallback parsing error handling<commit_after>use cairo;
use cssparser;
use glib::translate::*;
use glib_sys;
use libc;

use std::ptr;
use std::rc::Rc;

use bbox::RsvgBbox;
use drawing_ctx;
use error::*;
use gradient;
use node::NodeType;
use parsers::{Parse, ParseError};
use pattern;
use util::utf8_cstr;

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PaintServerSpread(pub cairo::enums::Extend);

impl Parse for PaintServerSpread {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<PaintServerSpread, AttributeError> {
        match s {
            "pad" => Ok(PaintServerSpread(cairo::enums::Extend::Pad)),
            "reflect" => Ok(PaintServerSpread(cairo::enums::Extend::Reflect)),
            "repeat" => Ok(PaintServerSpread(cairo::enums::Extend::Repeat)),
            _ => Err(AttributeError::Parse(ParseError::new(
                "expected 'pad' | 'reflect' | 'repeat'",
            ))),
        }
    }
}

impl Default for PaintServerSpread {
    fn default() -> PaintServerSpread {
        PaintServerSpread(cairo::enums::Extend::Pad)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum PaintServer {
    Inherit,
    Iri {
        iri: String,
        alternate: Option<cssparser::Color>,
    },
    SolidColor(cssparser::Color),
}

impl PaintServer {
    pub fn parse_input<'i, 't>(
        input: &mut cssparser::Parser<'i, 't>,
    ) -> Result<Self, AttributeError> {
        if input.try(|i| i.expect_ident_matching("inherit")).is_ok() {
            Ok(PaintServer::Inherit)
        } else if let Ok(url) = input.try(|i| i.expect_url()) {
            let alternate = if !input.is_exhausted() {
                if input.try(|i| i.expect_ident_matching("none")).is_ok() {
                    None
                } else {
                    Some(input.try(|i| cssparser::Color::parse(i))?)
                }
            } else {
                None
            };

            Ok(PaintServer::Iri {
                iri: String::from(url.as_ref()),
                alternate: alternate,
            })
        } else {
            cssparser::Color::parse(input)
                .map(PaintServer::SolidColor)
                .map_err(AttributeError::from)
        }
    }
}

impl Parse for PaintServer {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<PaintServer, AttributeError> {
        let mut input = cssparser::ParserInput::new(s);
        PaintServer::parse_input(&mut cssparser::Parser::new(&mut input))
    }
}

fn _set_source_rsvg_solid_color(
    ctx: *mut drawing_ctx::RsvgDrawingCtx,
    color: &cssparser::Color,
    opacity: u8,
    current_color: &cssparser::RGBA,
) {
    let rgba = match *color {
        cssparser::Color::RGBA(ref rgba) => rgba,
        cssparser::Color::CurrentColor => current_color,
    };

    drawing_ctx::get_cairo_context(ctx).set_source_rgba(
        f64::from(rgba.red_f32()),
        f64::from(rgba.green_f32()),
        f64::from(rgba.blue_f32()),
        f64::from(rgba.alpha_f32()) * (f64::from(opacity) / 255.0),
    );
}

/// Parses the paint specification, creating a new paint server object.
/// Return value: (nullable): The newly created paint server, or NULL on error.
///
/// # Arguments
///
/// * `str` - The SVG paint specification string to parse.
#[no_mangle]
pub extern "C" fn rsvg_paint_server_parse(
    inherit: *mut glib_sys::gboolean,
    str: *const libc::c_char,
) -> *const PaintServer {
    if !inherit.is_null() {
        unsafe {
            *inherit = true.to_glib();
        }
    }

    let paint_server = PaintServer::parse(unsafe { utf8_cstr(str) }, ());

    if let Ok(PaintServer::Inherit) = paint_server {
        if !inherit.is_null() {
            unsafe {
                *inherit = false.to_glib();
            }
        }
    }

    match paint_server {
        Ok(m) => Rc::into_raw(Rc::new(m)),
        Err(_) => ptr::null_mut(),
    }
}

/// Increase references counter of `PaintServer`.
///
/// # Arguments
///
/// * `paint_server` - must be constructed with `rsvg_paint_server_parse`.
#[no_mangle]
pub extern "C" fn rsvg_paint_server_ref(paint_server: *const PaintServer) {
    if paint_server.is_null() {
        return;
    }

    let server: Rc<PaintServer> = unsafe { Rc::from_raw(paint_server) };

    // forget about references
    Rc::into_raw(server.clone());
    Rc::into_raw(server);
}

/// Decrease references counter of `PaintServer`.
///
/// # Arguments
///
/// * `paint_server` - must be constructed with `rsvg_paint_server_parse`.
#[no_mangle]
pub extern "C" fn rsvg_paint_server_unref(paint_server: *const PaintServer) {
    if paint_server.is_null() {
        return;
    }

    // drop reference
    unsafe { Rc::from_raw(paint_server) };
}

pub fn _set_source_rsvg_paint_server(
    c_ctx: *mut drawing_ctx::RsvgDrawingCtx,
    ps: &PaintServer,
    opacity: u8,
    bbox: &RsvgBbox,
    current_color: &cssparser::RGBA,
) -> bool {
    let mut had_paint_server = false;

    match *ps {
        PaintServer::Iri {
            ref iri,
            ref alternate,
        } => {
            if let Some(acquired) = drawing_ctx::get_acquired_node(c_ctx, iri.as_str()) {
                let node = acquired.get();

                if node.get_type() == NodeType::LinearGradient
                    || node.get_type() == NodeType::RadialGradient
                {
                    had_paint_server = gradient::gradient_resolve_fallbacks_and_set_pattern(
                        &node,
                        c_ctx,
                        opacity,
                        bbox,
                    );
                } else if node.get_type() == NodeType::Pattern {
                    had_paint_server =
                        pattern::pattern_resolve_fallbacks_and_set_pattern(&node, c_ctx, bbox);
                }
            }

            if !had_paint_server && alternate.is_some() {
                _set_source_rsvg_solid_color(
                    c_ctx,
                    alternate.as_ref().unwrap(),
                    opacity,
                    current_color,
                );
                had_paint_server = true;
            }
        }

        PaintServer::SolidColor(color) => {
            _set_source_rsvg_solid_color(c_ctx, &color, opacity, current_color);
            had_paint_server = true;
        }

        _ => {}
    };

    had_paint_server
}

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

    #[test]
    fn parses_spread_method() {
        assert_eq!(
            PaintServerSpread::parse("pad", ()),
            Ok(PaintServerSpread(cairo::enums::Extend::Pad))
        );

        assert_eq!(
            PaintServerSpread::parse("reflect", ()),
            Ok(PaintServerSpread(cairo::enums::Extend::Reflect))
        );

        assert_eq!(
            PaintServerSpread::parse("repeat", ()),
            Ok(PaintServerSpread(cairo::enums::Extend::Repeat))
        );

        assert!(PaintServerSpread::parse("foobar", ()).is_err());
    }

    #[test]
    fn parses_inherit() {
        assert_eq!(PaintServer::parse("inherit", ()), Ok(PaintServer::Inherit));
    }

    #[test]
    fn parses_solid_color() {
        assert_eq!(
            PaintServer::parse("rgb(255, 128, 64, 0.5)", ()),
            Ok(PaintServer::SolidColor(cssparser::Color::RGBA(
                cssparser::RGBA::new(255, 128, 64, 128)
            )))
        );

        assert_eq!(
            PaintServer::parse("currentColor", ()),
            Ok(PaintServer::SolidColor(cssparser::Color::CurrentColor))
        );
    }

    #[test]
    fn parses_iri() {
        assert_eq!(
            PaintServer::parse("url(#link)", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) none", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) #ff8040", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::RGBA(cssparser::RGBA::new(
                    255,
                    128,
                    64,
                    255
                ))),
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) rgb(255, 128, 64, 0.5)", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::RGBA(cssparser::RGBA::new(
                    255,
                    128,
                    64,
                    128
                ))),
            },)
        );

        assert_eq!(
            PaintServer::parse("url(#link) currentColor", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::CurrentColor),
            },)
        );

        assert!(PaintServer::parse("url(#link) invalid", ()).is_err());
    }

    #[test]
    fn paint_server_refs_and_unrefs() {
        let rc = Rc::new(PaintServer::parse("#ffffff", ()).unwrap());
        let weak = Rc::downgrade(&rc);
        let ps = Rc::into_raw(rc);

        rsvg_paint_server_ref(ps);
        assert!(weak.upgrade().is_some());

        rsvg_paint_server_unref(ps);
        assert!(weak.upgrade().is_some());

        rsvg_paint_server_unref(ps);
        assert!(weak.upgrade().is_none());
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2020-present, Gregory Szorc
// All rights reserved.
//
// This software may be modified and distributed under the terms
// of the BSD license. See the LICENSE file for details.

use {
    crate::compressor::CCtx,
    pyo3::{exceptions::PyNotImplementedError, prelude::*},
    std::sync::Arc,
};

#[pyclass(module = "zstandard.backend_rust")]
pub struct ZstdCompressionWriter {
    cctx: Arc<CCtx<'static>>,
    writer: PyObject,
    source_size: u64,
    write_size: usize,
    write_return_read: bool,
    closefd: bool,
    entered: bool,
    closing: bool,
    closed: bool,
    bytes_compressed: usize,
}

impl ZstdCompressionWriter {
    pub fn new(
        py: Python,
        cctx: Arc<CCtx<'static>>,
        writer: &PyAny,
        source_size: u64,
        write_size: usize,
        write_return_read: bool,
        closefd: bool,
    ) -> Self {
        Self {
            cctx,
            writer: writer.into_py(py),
            source_size,
            write_size,
            write_return_read,
            closefd,
            entered: false,
            closing: false,
            closed: false,
            bytes_compressed: 0,
        }
    }
}

#[pymethods]
impl ZstdCompressionWriter {
    // TODO __enter__
    // TODO __exit__

    fn memory_size(&self) -> usize {
        self.cctx.memory_size()
    }

    fn fileno(&self) -> PyResult<()> {
        Err(PyNotImplementedError::new_err(()))
    }

    fn close(&self) -> PyResult<()> {
        Err(PyNotImplementedError::new_err(()))
    }

    #[getter]
    fn closed(&self) -> bool {
        self.closed
    }

    fn isatty(&self) -> bool {
        false
    }

    fn readable(&self) -> bool {
        false
    }

    #[args(size = "None")]
    fn readline(&self, py: Python, _size: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    #[args(size = "None")]
    fn readlines(&self, py: Python, _hint: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    #[args(pos, whence = "None")]
    fn seek(&self, pos: isize, whence: Option<&PyAny>) -> PyResult<()> {
        Err(PyNotImplementedError::new_err(()))
    }

    fn seekable(&self) -> bool {
        false
    }

    fn truncate(&self, py: Python, size: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn writable(&self) -> bool {
        true
    }

    fn writelines(&self, lines: &PyAny) -> PyResult<()> {
        Err(PyNotImplementedError::new_err(()))
    }

    #[args(size = "None")]
    fn read(&self, py: Python, size: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn readall(&self, py: Python) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn readinto(&self, py: Python, b: &PyAny) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn write(&self, data: &PyAny) -> PyResult<()> {
        Err(PyNotImplementedError::new_err(()))
    }

    #[args(flush_mode = "None")]
    fn flush_mode(&self, flush_mode: Option<&PyAny>) -> PyResult<()> {
        Err(PyNotImplementedError::new_err(()))
    }

    fn tell(&self) -> usize {
        self.bytes_compressed
    }
}
<commit_msg>rust: implement ZstdCompressionWriter<commit_after>// Copyright (c) 2020-present, Gregory Szorc
// All rights reserved.
//
// This software may be modified and distributed under the terms
// of the BSD license. See the LICENSE file for details.

use {
    crate::{compressor::CCtx, exceptions::ZstdError},
    pyo3::{
        buffer::PyBuffer,
        exceptions::{PyNotImplementedError, PyOSError, PyValueError},
        prelude::*,
        types::PyBytes,
    },
    std::sync::Arc,
};

const FLUSH_BLOCK: usize = 0;
const FLUSH_FRAME: usize = 1;

#[pyclass(module = "zstandard.backend_rust")]
pub struct ZstdCompressionWriter {
    cctx: Arc<CCtx<'static>>,
    writer: PyObject,
    source_size: u64,
    write_size: usize,
    write_return_read: bool,
    closefd: bool,
    entered: bool,
    closing: bool,
    closed: bool,
    bytes_compressed: usize,
    dest_buffer: Vec<u8>,
}

impl ZstdCompressionWriter {
    pub fn new(
        py: Python,
        cctx: Arc<CCtx<'static>>,
        writer: &PyAny,
        source_size: u64,
        write_size: usize,
        write_return_read: bool,
        closefd: bool,
    ) -> Self {
        Self {
            cctx,
            writer: writer.into_py(py),
            source_size,
            write_size,
            write_return_read,
            closefd,
            entered: false,
            closing: false,
            closed: false,
            bytes_compressed: 0,
            dest_buffer: Vec::with_capacity(write_size),
        }
    }
}

#[pymethods]
impl ZstdCompressionWriter {
    fn __enter__<'p>(mut slf: PyRefMut<'p, Self>, _py: Python<'p>) -> PyResult<PyRefMut<'p, Self>> {
        if slf.closed {
            Err(PyValueError::new_err("stream is closed"))
        } else if slf.entered {
            Err(ZstdError::new_err("cannot __enter__ multiple times"))
        } else {
            slf.entered = true;
            Ok(slf)
        }
    }

    fn __exit__<'p>(
        mut slf: PyRefMut<'p, Self>,
        py: Python<'p>,
        _exc_type: &PyAny,
        _exc_value: &PyAny,
        _exc_tb: &PyAny,
    ) -> PyResult<bool> {
        slf.entered = false;
        slf.close(py)?;

        // TODO clear out compressor context?

        Ok(false)
    }

    fn memory_size(&self) -> usize {
        self.cctx.memory_size()
    }

    fn fileno(&self, py: Python) -> PyResult<PyObject> {
        if let Ok(fileno) = self.writer.getattr(py, "fileno") {
            fileno.call0(py)
        } else {
            Err(PyOSError::new_err(
                "filenot not available on underlying writer",
            ))
        }
    }

    fn close(&mut self, py: Python) -> PyResult<()> {
        if self.closed {
            return Ok(());
        }

        self.closing = true;
        let res = self.flush(py, FLUSH_FRAME);
        self.closing = false;
        self.closed = true;

        res?;

        // Call close() on underlying stream as well.
        if let Ok(close) = self.writer.getattr(py, "close") {
            if self.closefd {
                close.call0(py)?;
            }
        }

        Ok(())
    }

    #[getter]
    fn closed(&self) -> bool {
        self.closed
    }

    fn isatty(&self) -> bool {
        false
    }

    fn readable(&self) -> bool {
        false
    }

    #[args(size = "None")]
    fn readline(&self, py: Python, size: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    #[args(size = "None")]
    fn readlines(&self, py: Python, hint: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    #[args(pos, whence = "None")]
    fn seek(&self, py: Python, pos: isize, whence: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn seekable(&self) -> bool {
        false
    }

    fn truncate(&self, py: Python, size: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn writable(&self) -> bool {
        true
    }

    fn writelines(&self, lines: &PyAny) -> PyResult<()> {
        Err(PyNotImplementedError::new_err(()))
    }

    #[args(size = "None")]
    fn read(&self, py: Python, size: Option<&PyAny>) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn readall(&self, py: Python) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn readinto(&self, py: Python, b: &PyAny) -> PyResult<()> {
        let io = py.import("io")?;
        let exc = io.getattr("UnsupportedOperation")?;

        Err(PyErr::from_instance(exc))
    }

    fn write(&mut self, py: Python, buffer: PyBuffer<u8>) -> PyResult<usize> {
        if self.closed {
            return Err(PyValueError::new_err("stream is closed"));
        }

        let mut total_write = 0;

        let mut in_buffer = zstd_sys::ZSTD_inBuffer {
            src: buffer.buf_ptr(),
            size: buffer.len_bytes(),
            pos: 0,
        };

        let mut out_buffer = zstd_sys::ZSTD_outBuffer {
            dst: self.dest_buffer.as_mut_ptr() as *mut _,
            size: self.dest_buffer.capacity(),
            pos: 0,
        };

        while in_buffer.pos < in_buffer.size {
            let zresult = unsafe {
                zstd_sys::ZSTD_compressStream2(
                    self.cctx.cctx(),
                    &mut out_buffer as *mut _,
                    &mut in_buffer as *mut _,
                    zstd_sys::ZSTD_EndDirective::ZSTD_e_continue,
                )
            };

            unsafe {
                self.dest_buffer.set_len(out_buffer.pos);
            }

            if unsafe { zstd_sys::ZSTD_isError(zresult) } != 0 {
                return Err(ZstdError::new_err(format!(
                    "zstd compress error: {}",
                    zstd_safe::get_error_name(zresult)
                )));
            }

            if out_buffer.pos > 0 {
                // TODO avoid buffer copy.
                let chunk = PyBytes::new(py, &self.dest_buffer);
                self.writer.call_method1(py, "write", (chunk,))?;

                total_write += out_buffer.pos;
                self.bytes_compressed += out_buffer.pos;
                out_buffer.pos = 0;
                unsafe {
                    self.dest_buffer.set_len(0);
                }
            }
        }

        if self.write_return_read {
            Ok(in_buffer.pos)
        } else {
            Ok(total_write)
        }
    }

    #[args(flush_mode = "FLUSH_BLOCK")]
    fn flush(&mut self, py: Python, flush_mode: usize) -> PyResult<usize> {
        let flush = match flush_mode {
            FLUSH_BLOCK => Ok(zstd_sys::ZSTD_EndDirective::ZSTD_e_flush),
            FLUSH_FRAME => Ok(zstd_sys::ZSTD_EndDirective::ZSTD_e_end),
            _ => Err(PyValueError::new_err(format!(
                "unknown flush_mode: {}",
                flush_mode
            ))),
        }?;

        if self.closed {
            return Err(PyValueError::new_err("stream is closed"));
        }

        let mut total_write = 0;

        let mut out_buffer = zstd_sys::ZSTD_outBuffer {
            dst: self.dest_buffer.as_mut_ptr() as *mut _,
            size: self.dest_buffer.capacity(),
            pos: 0,
        };

        let mut in_buffer = zstd_sys::ZSTD_inBuffer {
            src: std::ptr::null_mut(),
            size: 0,
            pos: 0,
        };

        loop {
            let zresult = unsafe {
                zstd_sys::ZSTD_compressStream2(
                    self.cctx.cctx(),
                    &mut out_buffer as *mut _,
                    &mut in_buffer as *mut _,
                    flush,
                )
            };

            unsafe {
                self.dest_buffer.set_len(out_buffer.pos);
            }

            if unsafe { zstd_sys::ZSTD_isError(zresult) } != 0 {
                return Err(ZstdError::new_err(format!(
                    "zstd compress error: {}",
                    zstd_safe::get_error_name(zresult)
                )));
            }

            if out_buffer.pos > 0 {
                // TODO avoid buffer copy.
                let chunk = PyBytes::new(py, &self.dest_buffer);
                self.writer.call_method1(py, "write", (chunk,))?;

                total_write += out_buffer.pos;
                self.bytes_compressed += out_buffer.pos;
                out_buffer.pos = 0;
                unsafe {
                    self.dest_buffer.set_len(0);
                }
            }

            if zresult == 0 {
                break;
            }
        }

        if let Ok(flush) = self.writer.getattr(py, "flush") {
            if !self.closing {
                flush.call0(py)?;
            }
        }

        Ok(total_write)
    }

    fn tell(&self) -> usize {
        self.bytes_compressed
    }
}
<|endoftext|>


<commit_before>// compile-flags: --test

#![feature(asm)]

#[test]
fn inlined_copy_args()
{
    #[inline(always)]
    fn inline_fn(mut v: u8) {
        v = 2;
        asm!("" : : "r" (v) : /*clobber*/ : "volatile");
    }
    let v = 1;
    inline_fn(v);
    assert_eq!(v, 1);
}
<commit_msg>tests - Support 1.54 by using `llvm_asm!`<commit_after>// compile-flags: --test

#![feature(asm)]

#[test]
fn inlined_copy_args()
{
    #[inline(always)]
    fn inline_fn(mut v: u8) {
        v = 2;
        llvm_asm!("" : : "r" (v) : /*clobber*/ : "volatile");
    }
    let v = 1;
    inline_fn(v);
    assert_eq!(v, 1);
}
<|endoftext|>


<commit_before>#![doc(html_root_url="https://docs.rs/phf_shared/0.7")]
#![cfg_attr(feature = "core", no_std)]

#[cfg(not(feature = "core"))]
extern crate std as core;

extern crate siphasher;

#[cfg(feature = "unicase")]
extern crate unicase;

use core::fmt;
use core::hash::{Hasher, Hash};
use siphasher::sip::SipHasher13;

#[inline]
pub fn displace(f1: u32, f2: u32, d1: u32, d2: u32) -> u32 {
    d2 + f1 * d1 + f2
}

#[inline]
pub fn split(hash: u64) -> (u32, u32, u32) {
    const BITS: u32 = 21;
    const MASK: u64 = (1 << BITS) - 1;

    ((hash & MASK) as u32,
     ((hash >> BITS) & MASK) as u32,
     ((hash >> (2 * BITS)) & MASK) as u32)
}

/// `key` is from `phf_generator::HashState::key`.
#[inline]
pub fn hash<T: ?Sized + PhfHash>(x: &T, key: u64) -> u64 {
    let mut hasher = SipHasher13::new_with_keys(0, key);
    x.phf_hash(&mut hasher);
    hasher.finish()
}

/// Return an index into `phf_generator::HashState::map`.
///
/// * `hash` is from `hash()` in this crate.
/// * `disps` is from `phf_generator::HashState::disps`.
/// * `len` is the length of `phf_generator::HashState::map`.
#[inline]
pub fn get_index(hash: u64, disps: &[(u32, u32)], len: usize) -> u32 {
    let (g, f1, f2) = split(hash);
    let (d1, d2) = disps[(g % (disps.len() as u32)) as usize];
    displace(f1, f2, d1, d2) % (len as u32)
}

/// A trait implemented by types which can be used in PHF data structures.
///
/// This differs from the standard library's `Hash` trait in that `PhfHash`'s
/// results must be architecture independent so that hashes will be consistent
/// between the host and target when cross compiling.
pub trait PhfHash {
    /// Feeds the value into the state given, updating the hasher as necessary.
    fn phf_hash<H: Hasher>(&self, state: &mut H);

    /// Feeds a slice of this type into the state provided.
    fn phf_hash_slice<H: Hasher>(data: &[Self], state: &mut H)
        where Self: Sized
    {
        for piece in data {
            piece.phf_hash(state);
        }
    }
}

/// Trait for printing types with `const` constructors, used by `phf_codegen` and `phf_macros`.
pub trait FmtConst {
    /// Print a `const` expression representing this value.
    fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result;
}

/// Create an impl of `FmtConst` delegating to `fmt::Debug` for types that can deal with it.
///
/// Ideally with specialization this could be just one default impl and then specialized where
/// it doesn't apply.
macro_rules! delegate_debug(
    ($ty:ty) => {
        impl FmtConst for $ty {
            fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{:?}", self)
            }
        }
    }
);

delegate_debug!(str);
delegate_debug!(char);
delegate_debug!(u8);
delegate_debug!(i8);
delegate_debug!(u16);
delegate_debug!(i16);
delegate_debug!(u32);
delegate_debug!(i32);
delegate_debug!(u64);
delegate_debug!(i64);
delegate_debug!(u128);
delegate_debug!(i128);
delegate_debug!(bool);

#[cfg(not(feature = "core"))]
impl PhfHash for String {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        (**self).phf_hash(state)
    }
}

#[cfg(not(feature = "core"))]
impl PhfHash for Vec<u8> {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        (**self).phf_hash(state)
    }
}

impl<'a, T: 'a + PhfHash + ?Sized> PhfHash for &'a T {
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        (*self).phf_hash(state)
    }
}

impl<'a, T: 'a + FmtConst + ?Sized> FmtConst for &'a T {
    fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
        (*self).fmt_const(f)
    }
}

impl PhfHash for str {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        self.as_bytes().phf_hash(state)
    }
}

impl PhfHash for [u8] {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        state.write(self);
    }
}

impl FmtConst for [u8] {
    #[inline]
    fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // slices need a leading reference
        write!(f, "&{:?}", self)
    }
}

#[cfg(feature = "unicase")]
impl<S> PhfHash for unicase::UniCase<S>
where unicase::UniCase<S>: Hash {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        self.hash(state)
    }
}

#[cfg(feature = "unicase")]
impl<S> FmtConst for unicase::UniCase<S> where S: AsRef<str> {
    fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.is_ascii() {
            f.write_str("UniCase::ascii(")?;
        } else {
            f.write_str("UniCase::unicode(")?;
        }

        self.as_ref().fmt_const(f)?;
        f.write_str(")")
    }
}

macro_rules! sip_impl(
    (le $t:ty) => (
        impl PhfHash for $t {
            #[inline]
            fn phf_hash<H: Hasher>(&self, state: &mut H) {
                self.to_le().hash(state);
            }
        }
    );
    ($t:ty) => (
        impl PhfHash for $t {
            #[inline]
            fn phf_hash<H: Hasher>(&self, state: &mut H) {
                self.hash(state);
            }
        }
    )
);

sip_impl!(u8);
sip_impl!(i8);
sip_impl!(le u16);
sip_impl!(le i16);
sip_impl!(le u32);
sip_impl!(le i32);
sip_impl!(le u64);
sip_impl!(le i64);
sip_impl!(le u128);
sip_impl!(le i128);
sip_impl!(bool);

impl PhfHash for char {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        (*self as u32).phf_hash(state)
    }
}

macro_rules! array_impl(
    ($t:ty, $n:expr) => (
        impl PhfHash for [$t; $n] {
            #[inline]
            fn phf_hash<H: Hasher>(&self, state: &mut H) {
                state.write(self);
            }
        }

        impl FmtConst for [$t; $n] {
            fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
                // delegate to the slice impl to minimize duplicated code
                self[..].fmt_const(f)
            }
        }
    )
);

array_impl!(u8, 1);
array_impl!(u8, 2);
array_impl!(u8, 3);
array_impl!(u8, 4);
array_impl!(u8, 5);
array_impl!(u8, 6);
array_impl!(u8, 7);
array_impl!(u8, 8);
array_impl!(u8, 9);
array_impl!(u8, 10);
array_impl!(u8, 11);
array_impl!(u8, 12);
array_impl!(u8, 13);
array_impl!(u8, 14);
array_impl!(u8, 15);
array_impl!(u8, 16);
array_impl!(u8, 17);
array_impl!(u8, 18);
array_impl!(u8, 19);
array_impl!(u8, 20);
array_impl!(u8, 21);
array_impl!(u8, 22);
array_impl!(u8, 23);
array_impl!(u8, 24);
array_impl!(u8, 25);
array_impl!(u8, 26);
array_impl!(u8, 27);
array_impl!(u8, 28);
array_impl!(u8, 29);
array_impl!(u8, 30);
array_impl!(u8, 31);
array_impl!(u8, 32);
<commit_msg>fix formatting for arrays after #156<commit_after>#![doc(html_root_url="https://docs.rs/phf_shared/0.7")]
#![cfg_attr(feature = "core", no_std)]

#[cfg(not(feature = "core"))]
extern crate std as core;

extern crate siphasher;

#[cfg(feature = "unicase")]
extern crate unicase;

use core::fmt;
use core::hash::{Hasher, Hash};
use siphasher::sip::SipHasher13;

#[inline]
pub fn displace(f1: u32, f2: u32, d1: u32, d2: u32) -> u32 {
    d2 + f1 * d1 + f2
}

#[inline]
pub fn split(hash: u64) -> (u32, u32, u32) {
    const BITS: u32 = 21;
    const MASK: u64 = (1 << BITS) - 1;

    ((hash & MASK) as u32,
     ((hash >> BITS) & MASK) as u32,
     ((hash >> (2 * BITS)) & MASK) as u32)
}

/// `key` is from `phf_generator::HashState::key`.
#[inline]
pub fn hash<T: ?Sized + PhfHash>(x: &T, key: u64) -> u64 {
    let mut hasher = SipHasher13::new_with_keys(0, key);
    x.phf_hash(&mut hasher);
    hasher.finish()
}

/// Return an index into `phf_generator::HashState::map`.
///
/// * `hash` is from `hash()` in this crate.
/// * `disps` is from `phf_generator::HashState::disps`.
/// * `len` is the length of `phf_generator::HashState::map`.
#[inline]
pub fn get_index(hash: u64, disps: &[(u32, u32)], len: usize) -> u32 {
    let (g, f1, f2) = split(hash);
    let (d1, d2) = disps[(g % (disps.len() as u32)) as usize];
    displace(f1, f2, d1, d2) % (len as u32)
}

/// A trait implemented by types which can be used in PHF data structures.
///
/// This differs from the standard library's `Hash` trait in that `PhfHash`'s
/// results must be architecture independent so that hashes will be consistent
/// between the host and target when cross compiling.
pub trait PhfHash {
    /// Feeds the value into the state given, updating the hasher as necessary.
    fn phf_hash<H: Hasher>(&self, state: &mut H);

    /// Feeds a slice of this type into the state provided.
    fn phf_hash_slice<H: Hasher>(data: &[Self], state: &mut H)
        where Self: Sized
    {
        for piece in data {
            piece.phf_hash(state);
        }
    }
}

/// Trait for printing types with `const` constructors, used by `phf_codegen` and `phf_macros`.
pub trait FmtConst {
    /// Print a `const` expression representing this value.
    fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result;
}

/// Create an impl of `FmtConst` delegating to `fmt::Debug` for types that can deal with it.
///
/// Ideally with specialization this could be just one default impl and then specialized where
/// it doesn't apply.
macro_rules! delegate_debug(
    ($ty:ty) => {
        impl FmtConst for $ty {
            fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{:?}", self)
            }
        }
    }
);

delegate_debug!(str);
delegate_debug!(char);
delegate_debug!(u8);
delegate_debug!(i8);
delegate_debug!(u16);
delegate_debug!(i16);
delegate_debug!(u32);
delegate_debug!(i32);
delegate_debug!(u64);
delegate_debug!(i64);
delegate_debug!(u128);
delegate_debug!(i128);
delegate_debug!(bool);

#[cfg(not(feature = "core"))]
impl PhfHash for String {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        (**self).phf_hash(state)
    }
}

#[cfg(not(feature = "core"))]
impl PhfHash for Vec<u8> {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        (**self).phf_hash(state)
    }
}

impl<'a, T: 'a + PhfHash + ?Sized> PhfHash for &'a T {
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        (*self).phf_hash(state)
    }
}

impl<'a, T: 'a + FmtConst + ?Sized> FmtConst for &'a T {
    fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
        (*self).fmt_const(f)
    }
}

impl PhfHash for str {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        self.as_bytes().phf_hash(state)
    }
}

impl PhfHash for [u8] {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        state.write(self);
    }
}

impl FmtConst for [u8] {
    #[inline]
    fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // slices need a leading reference
        write!(f, "&{:?}", self)
    }
}

#[cfg(feature = "unicase")]
impl<S> PhfHash for unicase::UniCase<S>
where unicase::UniCase<S>: Hash {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        self.hash(state)
    }
}

#[cfg(feature = "unicase")]
impl<S> FmtConst for unicase::UniCase<S> where S: AsRef<str> {
    fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.is_ascii() {
            f.write_str("UniCase::ascii(")?;
        } else {
            f.write_str("UniCase::unicode(")?;
        }

        self.as_ref().fmt_const(f)?;
        f.write_str(")")
    }
}

macro_rules! sip_impl(
    (le $t:ty) => (
        impl PhfHash for $t {
            #[inline]
            fn phf_hash<H: Hasher>(&self, state: &mut H) {
                self.to_le().hash(state);
            }
        }
    );
    ($t:ty) => (
        impl PhfHash for $t {
            #[inline]
            fn phf_hash<H: Hasher>(&self, state: &mut H) {
                self.hash(state);
            }
        }
    )
);

sip_impl!(u8);
sip_impl!(i8);
sip_impl!(le u16);
sip_impl!(le i16);
sip_impl!(le u32);
sip_impl!(le i32);
sip_impl!(le u64);
sip_impl!(le i64);
sip_impl!(le u128);
sip_impl!(le i128);
sip_impl!(bool);

impl PhfHash for char {
    #[inline]
    fn phf_hash<H: Hasher>(&self, state: &mut H) {
        (*self as u32).phf_hash(state)
    }
}

// minimize duplicated code since formatting drags in quite a bit
fn fmt_array(array: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{:?}", array)
}

macro_rules! array_impl(
    ($t:ty, $n:expr) => (
        impl PhfHash for [$t; $n] {
            #[inline]
            fn phf_hash<H: Hasher>(&self, state: &mut H) {
                state.write(self);
            }
        }

        impl FmtConst for [$t; $n] {
            fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt_array(self, f)
            }
        }
    )
);

array_impl!(u8, 1);
array_impl!(u8, 2);
array_impl!(u8, 3);
array_impl!(u8, 4);
array_impl!(u8, 5);
array_impl!(u8, 6);
array_impl!(u8, 7);
array_impl!(u8, 8);
array_impl!(u8, 9);
array_impl!(u8, 10);
array_impl!(u8, 11);
array_impl!(u8, 12);
array_impl!(u8, 13);
array_impl!(u8, 14);
array_impl!(u8, 15);
array_impl!(u8, 16);
array_impl!(u8, 17);
array_impl!(u8, 18);
array_impl!(u8, 19);
array_impl!(u8, 20);
array_impl!(u8, 21);
array_impl!(u8, 22);
array_impl!(u8, 23);
array_impl!(u8, 24);
array_impl!(u8, 25);
array_impl!(u8, 26);
array_impl!(u8, 27);
array_impl!(u8, 28);
array_impl!(u8, 29);
array_impl!(u8, 30);
array_impl!(u8, 31);
array_impl!(u8, 32);
<|endoftext|>


<commit_before>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! An utility for building an Oak application from a manifest file.
//!
//! Invoke with:
//!
//! ```shell
//! cargo run --manifest-path=sdk/rust/oak_app_build/Cargo.toml -- \
//!     --manifest-path=<OAK_APP_MANIFEST_FILE>
//! ```
//!
//! The compiled Oak application will then be stored under a `bin` subdirectory from the manifest
//! file location, named after the name field from the app manifest, and with an `.oak` extension.

use anyhow::{anyhow, Context};
use log::{debug, info};
use oak_abi::proto::oak::application::{
    node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration,
    WebAssemblyConfiguration,
};
use prost::Message;
use reqwest::Url;
use sha2::{Digest, Sha256};
use std::{
    collections::HashMap,
    fs,
    path::{Path, PathBuf},
};
use structopt::StructOpt;

/// Command line options for the Oak Application builder.
#[derive(StructOpt, Clone, Debug)]
#[structopt(about = "Oak Application Builder")]
struct Opt {
    #[structopt(long, help = "Input application manifest file in TOML format")]
    manifest_path: String,
}

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct Manifest {
    name: String,
    modules: HashMap<String, Module>,
    #[serde(default)]
    initial_node_configuration: InitialNodeConfig,
}

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
enum Module {
    #[serde(rename = "path")]
    Path(String),
    #[serde(rename = "external")]
    External(External),
}

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct External {
    url: String,
    sha256: String,
}

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct InitialNodeConfig {
    wasm_module_name: String,
    wasm_entrypoint_name: String,
}

impl Default for InitialNodeConfig {
    fn default() -> Self {
        Self {
            wasm_module_name: "app".to_string(),
            wasm_entrypoint_name: "oak_main".to_string(),
        }
    }
}

/// Directory used to save build artifacts.
///
/// Relative to the directory containing the app manifest file.
const OUTPUT_DIRECTORY: &str = "bin";

/// Directory used to save downloaded Wasm modules.
///
/// Relative to the directory containing the app manifest file.
const CACHE_DIRECTORY: &str = "bin/cache";

/// Get path for caching a downloaded file in [`CACHE_DIRECTORY`].
/// Cache file is named after `sha256_sum`.
fn get_module_cache_path(manifest_dir: &Path, module_sha256_sum: &str) -> PathBuf {
    manifest_dir.join(CACHE_DIRECTORY).join(&module_sha256_sum)
}

/// Get path for the output application file in [`OUTPUT_DIRECTORY`].
fn get_output_path(manifest_dir: &Path, app_name: &str) -> PathBuf {
    manifest_dir
        .join(OUTPUT_DIRECTORY)
        .join(format!("{}.oak", app_name))
}

/// Computes SHA256 sum from `data` and returns it as a HEX encoded string.
fn get_sha256(data: &[u8]) -> String {
    let mut hasher = Sha256::new();
    hasher.update(data);
    hex::encode(hasher.finalize().as_slice().to_vec())
}

/// Download file from `url`.
async fn download_file_from_url(url: &str) -> anyhow::Result<Vec<u8>> {
    let url: Url = url
        .parse()
        .with_context(|| format!("Couldn't parse URL {}", url))?;

    let response = reqwest::get(url.clone())
        .await
        .with_context(|| format!("Couldn't download file from {}", url))?;
    let data = response
        .bytes()
        .await
        .context("Couldn't retrieve file from HTTP response")?
        .to_vec();
    Ok(data)
}

/// Load Wasm module from file or URL if specified.
/// If the file was downloaded from URL, it is cached in [`CACHE_DIRECTORY`].
async fn load_module(manifest_dir: &Path, module: &Module) -> anyhow::Result<Vec<u8>> {
    match &module {
        Module::Path(path) => {
            fs::read(&path).with_context(|| format!("Couldn't read file {}", path))
        }
        Module::External(external) => {
            // Try to load module from cache, if failed, download it from URL.
            let cache_path = get_module_cache_path(&manifest_dir, &external.sha256);
            let data = match fs::read(&cache_path) {
                Ok(data) => {
                    info!("Loaded module from cache {:?}", cache_path.as_path());
                    data
                }
                Err(_) => {
                    info!(
                        "Couldn't load module from cache {:?}, downloading from URL {}",
                        cache_path.as_path(),
                        external.url,
                    );
                    let data = download_file_from_url(&external.url).await?;

                    // Save the downloaded Wasm module into the cache directory.
                    std::fs::create_dir_all(cache_path.parent().unwrap())
                        .context("Couldn't create cache directory")?;
                    fs::write(&cache_path, &data).with_context(|| {
                        format!("Couldn't write file {:?}", cache_path.as_path())
                    })?;
                    data
                }
            };

            // Check SHA256 sum of the Wasm module.
            let sha256_sum = get_sha256(&data);
            if sha256_sum == external.sha256 {
                Ok(data)
            } else {
                Err(anyhow!(
                    "Incorrect SHA256 sum: expected {}, received {}",
                    external.sha256,
                    sha256_sum
                ))
            }
        }
    }
}

/// Serializes an application configuration from `app_config` and writes it into `filename`.
pub fn write_config_to_file(
    app_config: &ApplicationConfiguration,
    filename: &Path,
) -> anyhow::Result<()> {
    let mut bytes = Vec::new();
    app_config
        .encode(&mut bytes)
        .context("Couldn't encode application configuration")?;
    fs::write(filename, &bytes)
        .with_context(|| format!("Couldn't write file {}", filename.display()))?;
    Ok(())
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    env_logger::init();

    let opt = Opt::from_args();
    debug!("Parsed opts: {:?}", opt);

    let manifest_path = Path::new(&opt.manifest_path);
    let manifest_dir = manifest_path
        .parent()
        .context("Couldn't find manifest directory")?;

    let manifest_file = fs::read_to_string(manifest_path).context("Couldn't read manifest file")?;
    let manifest: Manifest =
        toml::from_str(&manifest_file).context("Couldn't parse manifest file as TOML")?;
    debug!("Parsed manifest file: {:?}", manifest);

    // Load Wasm modules.
    let mut modules = HashMap::new();
    for (name, module) in manifest.modules.iter() {
        let loaded_module = load_module(&manifest_dir, &module)
            .await
            .context("Couldn't load module")?;
        modules.insert(name.to_string(), loaded_module);
    }

    // Create application configuration.
    let app_config = ApplicationConfiguration {
        wasm_modules: modules,
        initial_node_configuration: Some(NodeConfiguration {
            config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration {
                wasm_module_name: manifest.initial_node_configuration.wasm_module_name,
                wasm_entrypoint_name: manifest.initial_node_configuration.wasm_entrypoint_name,
            })),
        }),
    };

    let output_file = get_output_path(&manifest_dir, &manifest.name);

    // Serialize application.
    write_config_to_file(&app_config, &output_file).context("Couldn't write serialized config")?;

    Ok(())
}
<commit_msg>Fix Wasm module download from URL (#1689)<commit_after>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! An utility for building an Oak application from a manifest file.
//!
//! Invoke with:
//!
//! ```shell
//! cargo run --manifest-path=sdk/rust/oak_app_build/Cargo.toml -- \
//!     --manifest-path=<OAK_APP_MANIFEST_FILE>
//! ```
//!
//! The compiled Oak application will then be stored under a `bin` subdirectory from the manifest
//! file location, named after the name field from the app manifest, and with an `.oak` extension.

use anyhow::{anyhow, Context};
use log::{debug, info};
use oak_abi::proto::oak::application::{
    node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration,
    WebAssemblyConfiguration,
};
use prost::Message;
use reqwest::Url;
use sha2::{Digest, Sha256};
use std::{
    collections::HashMap,
    fs,
    path::{Path, PathBuf},
};
use structopt::StructOpt;

/// Command line options for the Oak Application builder.
#[derive(StructOpt, Clone, Debug)]
#[structopt(about = "Oak Application Builder")]
struct Opt {
    #[structopt(long, help = "Input application manifest file in TOML format")]
    manifest_path: String,
}

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct Manifest {
    name: String,
    modules: HashMap<String, Module>,
    #[serde(default)]
    initial_node_configuration: InitialNodeConfig,
}

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
enum Module {
    #[serde(rename = "path")]
    Path(String),
    #[serde(rename = "external")]
    External(External),
}

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct External {
    url: String,
    sha256: String,
}

#[derive(Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct InitialNodeConfig {
    wasm_module_name: String,
    wasm_entrypoint_name: String,
}

impl Default for InitialNodeConfig {
    fn default() -> Self {
        Self {
            wasm_module_name: "app".to_string(),
            wasm_entrypoint_name: "oak_main".to_string(),
        }
    }
}

/// Directory used to save build artifacts.
///
/// Relative to the directory containing the app manifest file.
const OUTPUT_DIRECTORY: &str = "bin";

/// Directory used to save downloaded Wasm modules.
///
/// Relative to the directory containing the app manifest file.
const CACHE_DIRECTORY: &str = "bin/cache";

/// Get path for caching a downloaded file in [`CACHE_DIRECTORY`].
/// Cache file is named after `sha256_sum`.
fn get_module_cache_path(manifest_dir: &Path, module_sha256_sum: &str) -> PathBuf {
    manifest_dir.join(CACHE_DIRECTORY).join(&module_sha256_sum)
}

/// Get path for the output application file in [`OUTPUT_DIRECTORY`].
fn get_output_path(manifest_dir: &Path, app_name: &str) -> PathBuf {
    manifest_dir
        .join(OUTPUT_DIRECTORY)
        .join(format!("{}.oak", app_name))
}

/// Computes SHA256 sum from `data` and returns it as a HEX encoded string.
fn get_sha256(data: &[u8]) -> String {
    let mut hasher = Sha256::new();
    hasher.update(data);
    hex::encode(hasher.finalize().as_slice().to_vec())
}

/// Download file from `url`.
async fn download_file_from_url(url: &str) -> anyhow::Result<Vec<u8>> {
    let url: Url = url
        .parse()
        .with_context(|| format!("Couldn't parse URL {}", url))?;
    let response = reqwest::get(url.clone())
        .await
        .with_context(|| format!("Couldn't send request to {}", url))?;

    // `reqwest` automatically handles redirects.
    // https://docs.rs/reqwest/0.10.8/reqwest/redirect/index.html
    if response.status().is_success() {
        let data = response
            .bytes()
            .await
            .context("Couldn't retrieve file from HTTP response")?
            .to_vec();
        Ok(data)
    } else {
        Err(anyhow!(
            "Couldn't download file from {}, status {:?}",
            url,
            response.status().canonical_reason()
        ))
    }
}

/// Load Wasm module from file or URL if specified.
/// If the file was downloaded from URL, it is cached in [`CACHE_DIRECTORY`].
async fn load_module(manifest_dir: &Path, module: &Module) -> anyhow::Result<Vec<u8>> {
    match &module {
        Module::Path(path) => {
            fs::read(&path).with_context(|| format!("Couldn't read file {}", path))
        }
        Module::External(external) => {
            // Try to load module from cache, if failed, download it from URL.
            let cache_path = get_module_cache_path(&manifest_dir, &external.sha256);
            let data = match fs::read(&cache_path) {
                Ok(data) => {
                    info!("Loaded module from cache {:?}", cache_path.as_path());
                    data
                }
                Err(_) => {
                    info!(
                        "Couldn't load module from cache {:?}, downloading from URL {}",
                        cache_path.as_path(),
                        external.url,
                    );
                    download_file_from_url(&external.url).await?
                }
            };

            // Check SHA256 sum of the Wasm module.
            let sha256_sum = get_sha256(&data);
            if sha256_sum == external.sha256 {
                // Save the downloaded Wasm module into the cache directory.
                std::fs::create_dir_all(cache_path.parent().unwrap())
                    .context("Couldn't create cache directory")?;
                fs::write(&cache_path, &data)
                    .with_context(|| format!("Couldn't write file {:?}", cache_path.as_path()))?;
                Ok(data)
            } else {
                Err(anyhow!(
                    "Incorrect SHA256 sum: expected {}, received {}",
                    external.sha256,
                    sha256_sum
                ))
            }
        }
    }
}

/// Serializes an application configuration from `app_config` and writes it into `filename`.
pub fn write_config_to_file(
    app_config: &ApplicationConfiguration,
    filename: &Path,
) -> anyhow::Result<()> {
    let mut bytes = Vec::new();
    app_config
        .encode(&mut bytes)
        .context("Couldn't encode application configuration")?;
    fs::write(filename, &bytes)
        .with_context(|| format!("Couldn't write file {}", filename.display()))?;
    Ok(())
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    env_logger::init();

    let opt = Opt::from_args();
    debug!("Parsed opts: {:?}", opt);

    let manifest_path = Path::new(&opt.manifest_path);
    let manifest_dir = manifest_path
        .parent()
        .context("Couldn't find manifest directory")?;

    let manifest_file = fs::read_to_string(manifest_path).context("Couldn't read manifest file")?;
    let manifest: Manifest =
        toml::from_str(&manifest_file).context("Couldn't parse manifest file as TOML")?;
    debug!("Parsed manifest file: {:?}", manifest);

    // Load Wasm modules.
    let mut modules = HashMap::new();
    for (name, module) in manifest.modules.iter() {
        let loaded_module = load_module(&manifest_dir, &module)
            .await
            .context("Couldn't load module")?;
        modules.insert(name.to_string(), loaded_module);
    }

    // Create application configuration.
    let app_config = ApplicationConfiguration {
        wasm_modules: modules,
        initial_node_configuration: Some(NodeConfiguration {
            config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration {
                wasm_module_name: manifest.initial_node_configuration.wasm_module_name,
                wasm_entrypoint_name: manifest.initial_node_configuration.wasm_entrypoint_name,
            })),
        }),
    };

    let output_file = get_output_path(&manifest_dir, &manifest.name);

    // Serialize application.
    write_config_to_file(&app_config, &output_file).context("Couldn't write serialized config")?;

    Ok(())
}
<|endoftext|>


<commit_before>mod completion_config;
mod completion_item;
mod completion_context;
mod presentation;
mod patterns;
#[cfg(test)]
mod test_utils;

mod complete_attribute;
mod complete_dot;
mod complete_record;
mod complete_pattern;
mod complete_fn_param;
mod complete_keyword;
mod complete_snippet;
mod complete_qualified_path;
mod complete_unqualified_path;
mod complete_postfix;
mod complete_macro_in_item_position;
mod complete_trait_impl;

use ra_ide_db::RootDatabase;

use crate::{
    completion::{
        completion_context::CompletionContext,
        completion_item::{CompletionKind, Completions},
    },
    FilePosition,
};

pub use crate::completion::{
    completion_config::CompletionConfig,
    completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
};

//FIXME: split the following feature into fine-grained features.

// Feature: Magic Completions
//
// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
// completions as well:
//
// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
// is placed at the appropriate position. Even though `if` is easy to type, you
// still want to complete it, to get ` { }` for free! `return` is inserted with a
// space or `;` depending on the return type of the function.
//
// When completing a function call, `()` are automatically inserted. If a function
// takes arguments, the cursor is positioned inside the parenthesis.
//
// There are postfix completions, which can be triggered by typing something like
// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
//
// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
// - `expr.match` -> `match expr {}`
// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
// - `expr.ref` -> `&expr`
// - `expr.refm` -> `&mut expr`
// - `expr.not` -> `!expr`
// - `expr.dbg` -> `dbg!(expr)`
//
// There also snippet completions:
//
// .Expressions
// - `pd` -> `eprintln!(" = {:?}", );")`
// - `ppd` -> `eprintln!(" = {:#?}", );`
//
// .Items
// - `tfn` -> `#[test] fn feature(){}`
// - `tmod` ->
// ```rust
// #[cfg(test)]
// mod tests {
//     use super::*;
//
//     #[test]
//     fn test_name() {}
// }
// ```

/// Main entry point for completion. We run completion as a two-phase process.
///
/// First, we look at the position and collect a so-called `CompletionContext.
/// This is a somewhat messy process, because, during completion, syntax tree is
/// incomplete and can look really weird.
///
/// Once the context is collected, we run a series of completion routines which
/// look at the context and produce completion items. One subtlety about this
/// phase is that completion engine should not filter by the substring which is
/// already present, it should give all possible variants for the identifier at
/// the caret. In other words, for
///
/// ```no-run
/// fn f() {
///     let foo = 92;
///     let _ = bar<|>
/// }
/// ```
///
/// `foo` *should* be present among the completion variants. Filtering by
/// identifier prefix/fuzzy match should be done higher in the stack, together
/// with ordering of completions (currently this is done by the client).
pub(crate) fn completions(
    db: &RootDatabase,
    config: &CompletionConfig,
    position: FilePosition,
) -> Option<Completions> {
    let ctx = CompletionContext::new(db, position, config)?;

    let mut acc = Completions::default();
    complete_attribute::complete_attribute(&mut acc, &ctx);
    complete_fn_param::complete_fn_param(&mut acc, &ctx);
    complete_keyword::complete_expr_keyword(&mut acc, &ctx);
    complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
    complete_snippet::complete_expr_snippet(&mut acc, &ctx);
    complete_snippet::complete_item_snippet(&mut acc, &ctx);
    complete_qualified_path::complete_qualified_path(&mut acc, &ctx);
    complete_unqualified_path::complete_unqualified_path(&mut acc, &ctx);
    complete_dot::complete_dot(&mut acc, &ctx);
    complete_record::complete_record(&mut acc, &ctx);
    complete_pattern::complete_pattern(&mut acc, &ctx);
    complete_postfix::complete_postfix(&mut acc, &ctx);
    complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
    complete_trait_impl::complete_trait_impl(&mut acc, &ctx);

    Some(acc)
}

#[cfg(test)]
mod tests {
    use crate::completion::completion_config::CompletionConfig;
    use crate::mock_analysis::analysis_and_position;

    struct DetailAndDocumentation<'a> {
        detail: &'a str,
        documentation: &'a str,
    }

    fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
        let (analysis, position) = analysis_and_position(ra_fixture);
        let config = CompletionConfig::default();
        let completions = analysis.completions(&config, position).unwrap().unwrap();
        for item in completions {
            if item.detail() == Some(expected.detail) {
                let opt = item.documentation();
                let doc = opt.as_ref().map(|it| it.as_str());
                assert_eq!(doc, Some(expected.documentation));
                return;
            }
        }
        panic!("completion detail not found: {}", expected.detail)
    }

    #[test]
    fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
        check_detail_and_documentation(
            r#"
            //- /lib.rs
            macro_rules! bar {
                () => {
                    struct Bar;
                    impl Bar {
                        #[doc = "Do the foo"]
                        fn foo(&self) {}
                    }
                }
            }

            bar!();

            fn foo() {
                let bar = Bar;
                bar.fo<|>;
            }
            "#,
            DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
        );
    }

    #[test]
    fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
        check_detail_and_documentation(
            r#"
            //- /lib.rs
            macro_rules! bar {
                () => {
                    struct Bar;
                    impl Bar {
                        /// Do the foo
                        fn foo(&self) {}
                    }
                }
            }

            bar!();

            fn foo() {
                let bar = Bar;
                bar.fo<|>;
            }
            "#,
            DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
        );
    }
}
<commit_msg>Fix a typo in completion docs<commit_after>mod completion_config;
mod completion_item;
mod completion_context;
mod presentation;
mod patterns;
#[cfg(test)]
mod test_utils;

mod complete_attribute;
mod complete_dot;
mod complete_record;
mod complete_pattern;
mod complete_fn_param;
mod complete_keyword;
mod complete_snippet;
mod complete_qualified_path;
mod complete_unqualified_path;
mod complete_postfix;
mod complete_macro_in_item_position;
mod complete_trait_impl;

use ra_ide_db::RootDatabase;

use crate::{
    completion::{
        completion_context::CompletionContext,
        completion_item::{CompletionKind, Completions},
    },
    FilePosition,
};

pub use crate::completion::{
    completion_config::CompletionConfig,
    completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
};

//FIXME: split the following feature into fine-grained features.

// Feature: Magic Completions
//
// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
// completions as well:
//
// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
// is placed at the appropriate position. Even though `if` is easy to type, you
// still want to complete it, to get ` { }` for free! `return` is inserted with a
// space or `;` depending on the return type of the function.
//
// When completing a function call, `()` are automatically inserted. If a function
// takes arguments, the cursor is positioned inside the parenthesis.
//
// There are postfix completions, which can be triggered by typing something like
// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
//
// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
// - `expr.match` -> `match expr {}`
// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
// - `expr.ref` -> `&expr`
// - `expr.refm` -> `&mut expr`
// - `expr.not` -> `!expr`
// - `expr.dbg` -> `dbg!(expr)`
//
// There also snippet completions:
//
// .Expressions
// - `pd` -> `eprintln!(" = {:?}", );`
// - `ppd` -> `eprintln!(" = {:#?}", );`
//
// .Items
// - `tfn` -> `#[test] fn feature(){}`
// - `tmod` ->
// ```rust
// #[cfg(test)]
// mod tests {
//     use super::*;
//
//     #[test]
//     fn test_name() {}
// }
// ```

/// Main entry point for completion. We run completion as a two-phase process.
///
/// First, we look at the position and collect a so-called `CompletionContext.
/// This is a somewhat messy process, because, during completion, syntax tree is
/// incomplete and can look really weird.
///
/// Once the context is collected, we run a series of completion routines which
/// look at the context and produce completion items. One subtlety about this
/// phase is that completion engine should not filter by the substring which is
/// already present, it should give all possible variants for the identifier at
/// the caret. In other words, for
///
/// ```no-run
/// fn f() {
///     let foo = 92;
///     let _ = bar<|>
/// }
/// ```
///
/// `foo` *should* be present among the completion variants. Filtering by
/// identifier prefix/fuzzy match should be done higher in the stack, together
/// with ordering of completions (currently this is done by the client).
pub(crate) fn completions(
    db: &RootDatabase,
    config: &CompletionConfig,
    position: FilePosition,
) -> Option<Completions> {
    let ctx = CompletionContext::new(db, position, config)?;

    let mut acc = Completions::default();
    complete_attribute::complete_attribute(&mut acc, &ctx);
    complete_fn_param::complete_fn_param(&mut acc, &ctx);
    complete_keyword::complete_expr_keyword(&mut acc, &ctx);
    complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
    complete_snippet::complete_expr_snippet(&mut acc, &ctx);
    complete_snippet::complete_item_snippet(&mut acc, &ctx);
    complete_qualified_path::complete_qualified_path(&mut acc, &ctx);
    complete_unqualified_path::complete_unqualified_path(&mut acc, &ctx);
    complete_dot::complete_dot(&mut acc, &ctx);
    complete_record::complete_record(&mut acc, &ctx);
    complete_pattern::complete_pattern(&mut acc, &ctx);
    complete_postfix::complete_postfix(&mut acc, &ctx);
    complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
    complete_trait_impl::complete_trait_impl(&mut acc, &ctx);

    Some(acc)
}

#[cfg(test)]
mod tests {
    use crate::completion::completion_config::CompletionConfig;
    use crate::mock_analysis::analysis_and_position;

    struct DetailAndDocumentation<'a> {
        detail: &'a str,
        documentation: &'a str,
    }

    fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
        let (analysis, position) = analysis_and_position(ra_fixture);
        let config = CompletionConfig::default();
        let completions = analysis.completions(&config, position).unwrap().unwrap();
        for item in completions {
            if item.detail() == Some(expected.detail) {
                let opt = item.documentation();
                let doc = opt.as_ref().map(|it| it.as_str());
                assert_eq!(doc, Some(expected.documentation));
                return;
            }
        }
        panic!("completion detail not found: {}", expected.detail)
    }

    #[test]
    fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
        check_detail_and_documentation(
            r#"
            //- /lib.rs
            macro_rules! bar {
                () => {
                    struct Bar;
                    impl Bar {
                        #[doc = "Do the foo"]
                        fn foo(&self) {}
                    }
                }
            }

            bar!();

            fn foo() {
                let bar = Bar;
                bar.fo<|>;
            }
            "#,
            DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
        );
    }

    #[test]
    fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
        check_detail_and_documentation(
            r#"
            //- /lib.rs
            macro_rules! bar {
                () => {
                    struct Bar;
                    impl Bar {
                        /// Do the foo
                        fn foo(&self) {}
                    }
                }
            }

            bar!();

            fn foo() {
                let bar = Bar;
                bar.fo<|>;
            }
            "#,
            DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
        );
    }
}
<|endoftext|>


<commit_before>//! `GET /_matrix/client/*/rooms/{roomId}/messages`

pub mod v3 {
    //! `/v3/` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmessages

    use js_int::{uint, UInt};
    use ruma_common::{
        api::ruma_api,
        events::{AnyRoomEvent, AnyStateEvent},
        serde::Raw,
        RoomId,
    };
    use serde::{Deserialize, Serialize};

    use crate::filter::{IncomingRoomEventFilter, RoomEventFilter};

    ruma_api! {
        metadata: {
            description: "Get message events for a room.",
            method: GET,
            name: "get_message_events",
            r0_path: "/_matrix/client/r0/rooms/:room_id/messages",
            stable_path: "/_matrix/client/v3/rooms/:room_id/messages",
            rate_limited: false,
            authentication: AccessToken,
            added: 1.0,
        }

        request: {
            /// The room to get events from.
            #[ruma_api(path)]
            pub room_id: &'a RoomId,

            /// The token to start returning events from.
            ///
            /// This token can be obtained from a `prev_batch` token returned for each room by the
            /// sync endpoint, or from a `start` or `end` token returned by a previous request to
            /// this endpoint.
            #[ruma_api(query)]
            pub from: &'a str,

            /// The token to stop returning events at.
            ///
            /// This token can be obtained from a `prev_batch` token returned for each room by the
            /// sync endpoint, or from a `start` or `end` token returned by a previous request to
            /// this endpoint.
            #[serde(skip_serializing_if = "Option::is_none")]
            #[ruma_api(query)]
            pub to: Option<&'a str>,

            /// The direction to return events from.
            #[ruma_api(query)]
            pub dir: Direction,

            /// The maximum number of events to return.
            ///
            /// Default: `10`.
            #[ruma_api(query)]
            #[serde(default = "default_limit", skip_serializing_if = "is_default_limit")]
            pub limit: UInt,

            /// A `RoomEventFilter` to filter returned events with.
            #[ruma_api(query)]
            #[serde(
                with = "ruma_common::serde::json_string",
                default,
                skip_serializing_if = "RoomEventFilter::is_empty"
            )]
            pub filter: RoomEventFilter<'a>,
        }

        #[derive(Default)]
        response: {
            /// The token the pagination starts from.
            pub start: String,

            /// The token the pagination ends at.
            #[serde(skip_serializing_if = "Option::is_none")]
            pub end: Option<String>,

            /// A list of room events.
            #[serde(default)]
            pub chunk: Vec<Raw<AnyRoomEvent>>,

            /// A list of state events relevant to showing the `chunk`.
            #[serde(default, skip_serializing_if = "Vec::is_empty")]
            pub state: Vec<Raw<AnyStateEvent>>,
        }

        error: crate::Error
    }

    impl<'a> Request<'a> {
        /// Creates a new `Request` with the given parameters.
        ///
        /// All other parameters will be defaulted.
        pub fn new(room_id: &'a RoomId, from: &'a str, dir: Direction) -> Self {
            Self {
                room_id,
                from,
                to: None,
                dir,
                limit: default_limit(),
                filter: RoomEventFilter::default(),
            }
        }

        /// Creates a new `Request` with the given room ID and from token, and `dir` set to
        /// `Backward`.
        pub fn backward(room_id: &'a RoomId, from: &'a str) -> Self {
            Self::new(room_id, from, Direction::Backward)
        }

        /// Creates a new `Request` with the given room ID and from token, and `dir` set to
        /// `Forward`.
        pub fn forward(room_id: &'a RoomId, from: &'a str) -> Self {
            Self::new(room_id, from, Direction::Forward)
        }
    }

    impl Response {
        /// Creates an empty `Response`.
        pub fn new() -> Self {
            Default::default()
        }
    }

    fn default_limit() -> UInt {
        uint!(10)
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    fn is_default_limit(val: &UInt) -> bool {
        *val == default_limit()
    }

    /// The direction to return events from.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    #[allow(clippy::exhaustive_enums)]
    pub enum Direction {
        /// Return events backwards in time from the requested `from` token.
        #[serde(rename = "b")]
        Backward,

        /// Return events forwards in time from the requested `from` token.
        #[serde(rename = "f")]
        Forward,
    }

    #[cfg(all(test, feature = "client"))]
    mod tests {
        use js_int::uint;
        use ruma_common::{
            api::{MatrixVersion, OutgoingRequest, SendAccessToken},
            room_id,
        };

        use super::{Direction, Request};
        use crate::filter::{LazyLoadOptions, RoomEventFilter};

        #[test]
        fn serialize_some_room_event_filter() {
            let room_id = room_id!("!roomid:example.org");
            let rooms = &[room_id.to_owned()];
            let filter = RoomEventFilter {
                lazy_load_options: LazyLoadOptions::Enabled { include_redundant_members: true },
                rooms: Some(rooms),
                not_rooms: &[
                    room_id!("!room:example.org").to_owned(),
                    room_id!("!room2:example.org").to_owned(),
                    room_id!("!room3:example.org").to_owned(),
                ],
                not_types: &["type".into()],
                ..Default::default()
            };
            let req = Request {
                room_id,
                from: "token",
                to: Some("token2"),
                dir: Direction::Backward,
                limit: uint!(0),
                filter,
            };

            let request: http::Request<Vec<u8>> = req
                .try_into_http_request(
                    "https://homeserver.tld",
                    SendAccessToken::IfRequired("auth_tok"),
                    &[MatrixVersion::V1_1],
                )
                .unwrap();
            assert_eq!(
            "from=token\
             &to=token2\
             &dir=b\
             &limit=0\
             &filter=%7B%22not_types%22%3A%5B%22type%22%5D%2C%22not_rooms%22%3A%5B%22%21room%3Aexample.org%22%2C%22%21room2%3Aexample.org%22%2C%22%21room3%3Aexample.org%22%5D%2C%22rooms%22%3A%5B%22%21roomid%3Aexample.org%22%5D%2C%22lazy_load_members%22%3Atrue%2C%22include_redundant_members%22%3Atrue%7D",
            request.uri().query().unwrap()
        );
        }

        #[test]
        fn serialize_default_room_event_filter() {
            let room_id = room_id!("!roomid:example.org");
            let req = Request {
                room_id,
                from: "token",
                to: Some("token2"),
                dir: Direction::Backward,
                limit: uint!(0),
                filter: RoomEventFilter::default(),
            };

            let request = req
                .try_into_http_request::<Vec<u8>>(
                    "https://homeserver.tld",
                    SendAccessToken::IfRequired("auth_tok"),
                    &[MatrixVersion::V1_1],
                )
                .unwrap();
            assert_eq!("from=token&to=token2&dir=b&limit=0", request.uri().query().unwrap(),);
        }
    }
}
<commit_msg>client-api: Make from param optional in get_message_events<commit_after>//! `GET /_matrix/client/*/rooms/{roomId}/messages`

pub mod v3 {
    //! `/v3/` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmessages

    use js_int::{uint, UInt};
    use ruma_common::{
        api::ruma_api,
        events::{AnyRoomEvent, AnyStateEvent},
        serde::Raw,
        RoomId,
    };
    use serde::{Deserialize, Serialize};

    use crate::filter::{IncomingRoomEventFilter, RoomEventFilter};

    ruma_api! {
        metadata: {
            description: "Get message events for a room.",
            method: GET,
            name: "get_message_events",
            r0_path: "/_matrix/client/r0/rooms/:room_id/messages",
            stable_path: "/_matrix/client/v3/rooms/:room_id/messages",
            rate_limited: false,
            authentication: AccessToken,
            added: 1.0,
        }

        request: {
            /// The room to get events from.
            #[ruma_api(path)]
            pub room_id: &'a RoomId,

            /// The token to start returning events from.
            ///
            /// This token can be obtained from a `prev_batch` token returned for each room by the
            /// sync endpoint, or from a `start` or `end` token returned by a previous request to
            /// this endpoint.
            ///
            /// If this is `None`, the server will return messages from the start or end of the
            /// history visible to the user, depending on the value of [`dir`][Self::dir].
            ///
            /// *Note: This field is marked required in v1.2 of the specification, but that is
            /// changing, most likely as part of v1.3. To frontload the breaking change, this field
            /// is optional already even though v1.2 is the latest version at the time of writing.
            /// The specification change can be found [here].*
            ///
            /// [here]: https://github.com/matrix-org/matrix-spec/pull/1002
            #[ruma_api(query)]
            pub from: Option<&'a str>,

            /// The token to stop returning events at.
            ///
            /// This token can be obtained from a `prev_batch` token returned for each room by the
            /// sync endpoint, or from a `start` or `end` token returned by a previous request to
            /// this endpoint.
            #[serde(skip_serializing_if = "Option::is_none")]
            #[ruma_api(query)]
            pub to: Option<&'a str>,

            /// The direction to return events from.
            #[ruma_api(query)]
            pub dir: Direction,

            /// The maximum number of events to return.
            ///
            /// Default: `10`.
            #[ruma_api(query)]
            #[serde(default = "default_limit", skip_serializing_if = "is_default_limit")]
            pub limit: UInt,

            /// A [`RoomEventFilter`] to filter returned events with.
            #[ruma_api(query)]
            #[serde(
                with = "ruma_common::serde::json_string",
                default,
                skip_serializing_if = "RoomEventFilter::is_empty"
            )]
            pub filter: RoomEventFilter<'a>,
        }

        #[derive(Default)]
        response: {
            /// The token the pagination starts from.
            pub start: String,

            /// The token the pagination ends at.
            #[serde(skip_serializing_if = "Option::is_none")]
            pub end: Option<String>,

            /// A list of room events.
            #[serde(default)]
            pub chunk: Vec<Raw<AnyRoomEvent>>,

            /// A list of state events relevant to showing the `chunk`.
            #[serde(default, skip_serializing_if = "Vec::is_empty")]
            pub state: Vec<Raw<AnyStateEvent>>,
        }

        error: crate::Error
    }

    impl<'a> Request<'a> {
        /// Creates a new `Request` with the given parameters.
        ///
        /// All other parameters will be defaulted.
        pub fn new(room_id: &'a RoomId, from: &'a str, dir: Direction) -> Self {
            Self {
                room_id,
                from: Some(from),
                to: None,
                dir,
                limit: default_limit(),
                filter: RoomEventFilter::default(),
            }
        }

        /// Creates a new `Request` with the given room ID and from token, and `dir` set to
        /// `Backward`.
        pub fn backward(room_id: &'a RoomId, from: &'a str) -> Self {
            Self::new(room_id, from, Direction::Backward)
        }

        /// Creates a new `Request` with the given room ID and from token, and `dir` set to
        /// `Forward`.
        pub fn forward(room_id: &'a RoomId, from: &'a str) -> Self {
            Self::new(room_id, from, Direction::Forward)
        }

        /// Creates a new `Request` to fetch messages from the very start of the available history
        /// for a given room.
        pub fn from_start(room_id: &'a RoomId) -> Self {
            Self {
                room_id,
                from: None,
                to: None,
                dir: Direction::Forward,
                limit: default_limit(),
                filter: RoomEventFilter::default(),
            }
        }

        /// Creates a new `Request` to fetch messages from the very end of the available history for
        /// a given room.
        pub fn from_end(room_id: &'a RoomId) -> Self {
            Self {
                room_id,
                from: None,
                to: None,
                dir: Direction::Backward,
                limit: default_limit(),
                filter: RoomEventFilter::default(),
            }
        }
    }

    impl Response {
        /// Creates an empty `Response`.
        pub fn new() -> Self {
            Default::default()
        }
    }

    fn default_limit() -> UInt {
        uint!(10)
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    fn is_default_limit(val: &UInt) -> bool {
        *val == default_limit()
    }

    /// The direction to return events from.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    #[allow(clippy::exhaustive_enums)]
    pub enum Direction {
        /// Return events backwards in time from the requested `from` token.
        #[serde(rename = "b")]
        Backward,

        /// Return events forwards in time from the requested `from` token.
        #[serde(rename = "f")]
        Forward,
    }

    #[cfg(all(test, feature = "client"))]
    mod tests {
        use js_int::uint;
        use ruma_common::{
            api::{MatrixVersion, OutgoingRequest, SendAccessToken},
            room_id,
        };

        use super::{Direction, Request};
        use crate::filter::{LazyLoadOptions, RoomEventFilter};

        #[test]
        fn serialize_some_room_event_filter() {
            let room_id = room_id!("!roomid:example.org");
            let rooms = &[room_id.to_owned()];
            let filter = RoomEventFilter {
                lazy_load_options: LazyLoadOptions::Enabled { include_redundant_members: true },
                rooms: Some(rooms),
                not_rooms: &[
                    room_id!("!room:example.org").to_owned(),
                    room_id!("!room2:example.org").to_owned(),
                    room_id!("!room3:example.org").to_owned(),
                ],
                not_types: &["type".into()],
                ..Default::default()
            };
            let req = Request {
                room_id,
                from: Some("token"),
                to: Some("token2"),
                dir: Direction::Backward,
                limit: uint!(0),
                filter,
            };

            let request: http::Request<Vec<u8>> = req
                .try_into_http_request(
                    "https://homeserver.tld",
                    SendAccessToken::IfRequired("auth_tok"),
                    &[MatrixVersion::V1_1],
                )
                .unwrap();
            assert_eq!(
            "from=token\
             &to=token2\
             &dir=b\
             &limit=0\
             &filter=%7B%22not_types%22%3A%5B%22type%22%5D%2C%22not_rooms%22%3A%5B%22%21room%3Aexample.org%22%2C%22%21room2%3Aexample.org%22%2C%22%21room3%3Aexample.org%22%5D%2C%22rooms%22%3A%5B%22%21roomid%3Aexample.org%22%5D%2C%22lazy_load_members%22%3Atrue%2C%22include_redundant_members%22%3Atrue%7D",
            request.uri().query().unwrap()
        );
        }

        #[test]
        fn serialize_default_room_event_filter() {
            let room_id = room_id!("!roomid:example.org");
            let req = Request {
                room_id,
                from: Some("token"),
                to: Some("token2"),
                dir: Direction::Backward,
                limit: uint!(0),
                filter: RoomEventFilter::default(),
            };

            let request = req
                .try_into_http_request::<Vec<u8>>(
                    "https://homeserver.tld",
                    SendAccessToken::IfRequired("auth_tok"),
                    &[MatrixVersion::V1_1],
                )
                .unwrap();
            assert_eq!("from=token&to=token2&dir=b&limit=0", request.uri().query().unwrap(),);
        }
    }
}
<|endoftext|>


<commit_before>//! Counts the number of codepoints of each UTF-8 length in files

use std::env::args_os;
use std::fs::File;
use std::io::{self, Read, stdin};
use std::borrow::Cow;
extern crate encode_unicode;
use encode_unicode::U8UtfExt;

#[derive(Default)]
struct Distribution {
    bytes: usize,
    utf8: [usize; 4],
}

fn read(file: &mut Read) -> (Distribution, Option<io::Error>) {
    let mut r = Distribution::default();
    let mut buf = [0u8; 4096];
    loop {
        let read = match file.read(&mut buf) {
            Ok(0) => return (r, None),
            Ok(n) => n,
            Err(e) => return (r, Some(e)),
        };
        r.bytes += read;
        for (o, &b) in buf[..read].iter().enumerate() {
            match b.extra_utf8_bytes() {
                Ok(i) => {
                    r.utf8[i] += 1;
                    if i == 3 {
                        let min = o.saturating_sub(20);
                        let max = if o+23 <= read {o+23} else {read};
                        println!("{}", String::from_utf8_lossy(&buf[min..max]));
                    }
                },
                Err(_) => {}
            }
        }
    }
}

fn display(name_pad: usize,  name: Cow<str>,
           r: Distribution,  err: Option<io::Error>) {
    let c = r.utf8;
    let characters = c[0]+c[1]+c[2]+c[3];
    let s = [c[0], c[1]*2, c[2]*3, c[3]*4];
    let p = [
        (s[0]*100) as f32 / r.bytes as f32,
        (s[1]*100) as f32 / r.bytes as f32,
        (s[2]*100) as f32 / r.bytes as f32,
        (s[3]*100) as f32 / r.bytes as f32,
    ];
    println!("{:>6$}: bytes: {:7}, UTF-8 distribution: [{:7}, {:6}, {:6}, {:6}]",
        name, r.bytes, s[0], s[1], s[2], s[3], name_pad
    );
    println!("{5:6$}  chars: {:7}, UTF-8 percentages:  [{:>6.2}%, {:>5.2}%, {:>5.2}%, {:>5.2}%]",
        characters, p[0], p[1], p[2], p[3], "", name_pad
    );
    if let Some(err) = err {
        println!("{1:2$}  {}", err, "", name_pad);
    }
}

fn main() {
    let name_length = args_os().skip(1)
        .map(|path| path.to_string_lossy().chars().count() )
        .max();
    for path in args_os().skip(1) {
        let name = path.to_string_lossy();
        let (r,err) = match File::open(&path) {
            Ok(mut file) => read(&mut file),
            Err(err) => {
                eprintln!("{}:\t{}", name, err);
                continue;
            }
        };
        display(name_length.unwrap(), name, r, err);
    }
    if name_length.is_none() {
        let stdin = stdin();
        let (r,err) = read(&mut stdin.lock());
        display(0, Cow::Borrowed("stdin"), r, err);
    }
}
<commit_msg>Add license header to examples/ program<commit_after>/* Copyright 2018 The encode_unicode Developers
 *
 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
 * http://opensource.org/licenses/MIT>, at your option. This file may not be
 * copied, modified, or distributed except according to those terms.
 */

//! Counts the number of codepoints of each UTF-8 length in files

use std::env::args_os;
use std::fs::File;
use std::io::{self, Read, stdin};
use std::borrow::Cow;
extern crate encode_unicode;
use encode_unicode::U8UtfExt;

#[derive(Default)]
struct Distribution {
    bytes: usize,
    utf8: [usize; 4],
}

fn read(file: &mut Read) -> (Distribution, Option<io::Error>) {
    let mut r = Distribution::default();
    let mut buf = [0u8; 4096];
    loop {
        let read = match file.read(&mut buf) {
            Ok(0) => return (r, None),
            Ok(n) => n,
            Err(e) => return (r, Some(e)),
        };
        r.bytes += read;
        for (o, &b) in buf[..read].iter().enumerate() {
            match b.extra_utf8_bytes() {
                Ok(i) => {
                    r.utf8[i] += 1;
                    if i == 3 {
                        let min = o.saturating_sub(20);
                        let max = if o+23 <= read {o+23} else {read};
                        println!("{}", String::from_utf8_lossy(&buf[min..max]));
                    }
                },
                Err(_) => {}
            }
        }
    }
}

fn display(name_pad: usize,  name: Cow<str>,
           r: Distribution,  err: Option<io::Error>) {
    let c = r.utf8;
    let characters = c[0]+c[1]+c[2]+c[3];
    let s = [c[0], c[1]*2, c[2]*3, c[3]*4];
    let p = [
        (s[0]*100) as f32 / r.bytes as f32,
        (s[1]*100) as f32 / r.bytes as f32,
        (s[2]*100) as f32 / r.bytes as f32,
        (s[3]*100) as f32 / r.bytes as f32,
    ];
    println!("{:>6$}: bytes: {:7}, UTF-8 distribution: [{:7}, {:6}, {:6}, {:6}]",
        name, r.bytes, s[0], s[1], s[2], s[3], name_pad
    );
    println!("{5:6$}  chars: {:7}, UTF-8 percentages:  [{:>6.2}%, {:>5.2}%, {:>5.2}%, {:>5.2}%]",
        characters, p[0], p[1], p[2], p[3], "", name_pad
    );
    if let Some(err) = err {
        println!("{1:2$}  {}", err, "", name_pad);
    }
}

fn main() {
    let name_length = args_os().skip(1)
        .map(|path| path.to_string_lossy().chars().count() )
        .max();
    for path in args_os().skip(1) {
        let name = path.to_string_lossy();
        let (r,err) = match File::open(&path) {
            Ok(mut file) => read(&mut file),
            Err(err) => {
                eprintln!("{}:\t{}", name, err);
                continue;
            }
        };
        display(name_length.unwrap(), name, r, err);
    }
    if name_length.is_none() {
        let stdin = stdin();
        let (r,err) = read(&mut stdin.lock());
        display(0, Cow::Borrowed("stdin"), r, err);
    }
}
<|endoftext|>