<commit_before>extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate lazy_static;
extern crate nalgebra;
extern crate rand;

use nalgebra::ToHomogeneous;

use cube;
use cube::ComputedCube;
use math::{IntoDomain, RealSpace};

pub type Point3 = nalgebra::Point3<RealSpace>;
pub type Vector3 = nalgebra::Vector3<RealSpace>;
pub type Vector4 = nalgebra::Vector4<RealSpace>;
pub type Matrix4 = nalgebra::Matrix4<RealSpace>;
pub type Index = u32;

#[cfg_attr(rustfmt, rustfmt_skip)]
const UNIT_CUBE_INDECES: [Index; 36] = [
    0, 1, 2, 2, 3, 0,
    4, 5, 6, 6, 7, 4,
    6, 5, 2, 2, 1, 6,
    0, 3, 4, 4, 7, 0,
    5, 4, 3, 3, 2, 5,
    1, 0, 7, 7, 6, 1,
];
lazy_static! {
    static ref UNIT_CUBE_POINTS: [Point3; 8] = [
        // Bottom.
        Point3::new(0.0, 0.0, 1.0), // 0
        Point3::new(1.0, 0.0, 1.0), // 1
        Point3::new(1.0, 1.0, 1.0), // 2
        Point3::new(0.0, 1.0, 1.0), // 3
        // Top.
        Point3::new(0.0, 1.0, 0.0), // 4
        Point3::new(1.0, 1.0, 0.0), // 5
        Point3::new(1.0, 0.0, 0.0), // 6
        Point3::new(0.0, 0.0, 0.0), // 7
    ];
}

gfx_pipeline!{
    pipeline {
        vertex_buffer: gfx::VertexBuffer<RawVertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_transform",
        output: gfx::RenderTarget<gfx::format::Rgba8> = "f_target0",
    }
}

gfx_vertex_struct!{
    RawVertex {
        position: [f32; 3] = "a_position",
        color: [f32; 4] = "a_color",
    }
}

trait GeometricEdge {
    fn front_unit_transform(&self) -> f32;
    fn back_unit_transform(&self) -> f32;
}

impl GeometricEdge for cube::Edge {
    fn front_unit_transform(&self) -> f32 {
        ((self.front() - cube::MIN_EDGE) as f32) / ((cube::MAX_EDGE - cube::MIN_EDGE) as f32)
    }

    fn back_unit_transform(&self) -> f32 {
        let range = cube::MAX_EDGE - cube::MIN_EDGE;
        -((range - (self.back() - cube::MIN_EDGE)) as f32) / (range as f32)
    }
}

pub trait GeometricCube: cube::ComputedCube {
    fn points(&self) -> Vec<Point3>;
}

impl<T: cube::ComputedCube> GeometricCube for T {
    fn points(&self) -> Vec<Point3> {
        // TODO: Compute the points of this cube.
        unimplemented!()
    }
}

impl From<Vertex> for RawVertex {
    fn from(vertex: Vertex) -> Self {
        RawVertex {
            position: *vertex.position.as_ref(),
            color: *vertex.color.as_ref(),
        }
    }
}

struct Vertex {
    pub position: Point3,
    pub color: Vector4,
}

impl Vertex {
    pub fn new(position: Point3, color: Vector4) -> Self {
        Vertex {
            position: position,
            color: color,
        }
    }
}

pub fn vertex_buffer_from_cube<R, F>(cube: &cube::Traversal,
                                     factory: &mut F)
                                     -> (gfx::handle::Buffer<R, RawVertex>, gfx::Slice<R>)
    where R: gfx::Resources,
          F: gfx::traits::FactoryExt<R>
{
    let mut points = Vec::new();
    let mut indeces = Vec::new();
    for (i, cube) in cube.iter().filter(|cube| cube.is_leaf()).enumerate() {
        let width = cube.partition().width();
        let origin: Vector3 = cube.partition().origin().to_vector().into_domain();
        let color = Vector4::new(rand::random::<f32>(),
                                 rand::random::<f32>(),
                                 rand::random::<f32>(),
                                 1.0);
        points.extend(UNIT_CUBE_POINTS.iter()
            .map(|point| (point * cube::exp(width) as RealSpace) + origin)
            .map(|point| RawVertex::from(Vertex::new(point, color))));
        indeces.extend(UNIT_CUBE_INDECES.iter()
            .map(|j| ((UNIT_CUBE_POINTS.len() * i) as Index + *j)));
    }
    factory.create_vertex_buffer_with_slice(points.as_slice(), indeces.as_slice())
}

pub fn projection_from_window(window: &glutin::Window) -> Matrix4 {
    let aspect = {
        let (width, height) = window.get_inner_size_pixels().unwrap();
        width as f32 / height as f32
    };
    nalgebra::PerspectiveMatrix3::new(aspect, 1.0, -1.0, 1.0).to_matrix()
}

pub fn look_at_cube<C>(cube: &C, from: &Point3) -> Matrix4
    where C: cube::ComputedCube
{
    nalgebra::Isometry3::look_at_rh(from,
                                    &cube.partition().midpoint().into_domain(),
                                    &Vector3::new(0.0, 0.0, 1.0))
        .to_homogeneous()
}

#[cfg(test)]
mod tests {
    use super::*;
}
<commit_msg>Fix comments on unit cube points.<commit_after>extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate lazy_static;
extern crate nalgebra;
extern crate rand;

use nalgebra::ToHomogeneous;

use cube;
use cube::ComputedCube;
use math::{IntoDomain, RealSpace};

pub type Point3 = nalgebra::Point3<RealSpace>;
pub type Vector3 = nalgebra::Vector3<RealSpace>;
pub type Vector4 = nalgebra::Vector4<RealSpace>;
pub type Matrix4 = nalgebra::Matrix4<RealSpace>;
pub type Index = u32;

#[cfg_attr(rustfmt, rustfmt_skip)]
const UNIT_CUBE_INDECES: [Index; 36] = [
    0, 1, 2, 2, 3, 0,
    4, 5, 6, 6, 7, 4,
    6, 5, 2, 2, 1, 6,
    0, 3, 4, 4, 7, 0,
    5, 4, 3, 3, 2, 5,
    1, 0, 7, 7, 6, 1,
];
lazy_static! {
    static ref UNIT_CUBE_POINTS: [Point3; 8] = [
        // Back.
        Point3::new(0.0, 0.0, 1.0), // 0
        Point3::new(1.0, 0.0, 1.0), // 1
        Point3::new(1.0, 1.0, 1.0), // 2
        Point3::new(0.0, 1.0, 1.0), // 3
        // Front.
        Point3::new(0.0, 1.0, 0.0), // 4
        Point3::new(1.0, 1.0, 0.0), // 5
        Point3::new(1.0, 0.0, 0.0), // 6
        Point3::new(0.0, 0.0, 0.0), // 7
    ];
}

gfx_pipeline!{
    pipeline {
        vertex_buffer: gfx::VertexBuffer<RawVertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_transform",
        output: gfx::RenderTarget<gfx::format::Rgba8> = "f_target0",
    }
}

gfx_vertex_struct!{
    RawVertex {
        position: [f32; 3] = "a_position",
        color: [f32; 4] = "a_color",
    }
}

trait GeometricEdge {
    fn front_unit_transform(&self) -> f32;
    fn back_unit_transform(&self) -> f32;
}

impl GeometricEdge for cube::Edge {
    fn front_unit_transform(&self) -> f32 {
        ((self.front() - cube::MIN_EDGE) as f32) / ((cube::MAX_EDGE - cube::MIN_EDGE) as f32)
    }

    fn back_unit_transform(&self) -> f32 {
        let range = cube::MAX_EDGE - cube::MIN_EDGE;
        -((range - (self.back() - cube::MIN_EDGE)) as f32) / (range as f32)
    }
}

pub trait GeometricCube: cube::ComputedCube {
    fn points(&self) -> Vec<Point3>;
}

impl<T: cube::ComputedCube> GeometricCube for T {
    fn points(&self) -> Vec<Point3> {
        // TODO: Compute the points of this cube.
        unimplemented!()
    }
}

impl From<Vertex> for RawVertex {
    fn from(vertex: Vertex) -> Self {
        RawVertex {
            position: *vertex.position.as_ref(),
            color: *vertex.color.as_ref(),
        }
    }
}

struct Vertex {
    pub position: Point3,
    pub color: Vector4,
}

impl Vertex {
    pub fn new(position: Point3, color: Vector4) -> Self {
        Vertex {
            position: position,
            color: color,
        }
    }
}

pub fn vertex_buffer_from_cube<R, F>(cube: &cube::Traversal,
                                     factory: &mut F)
                                     -> (gfx::handle::Buffer<R, RawVertex>, gfx::Slice<R>)
    where R: gfx::Resources,
          F: gfx::traits::FactoryExt<R>
{
    let mut points = Vec::new();
    let mut indeces = Vec::new();
    for (i, cube) in cube.iter().filter(|cube| cube.is_leaf()).enumerate() {
        let width = cube.partition().width();
        let origin: Vector3 = cube.partition().origin().to_vector().into_domain();
        let color = Vector4::new(rand::random::<f32>(),
                                 rand::random::<f32>(),
                                 rand::random::<f32>(),
                                 1.0);
        points.extend(UNIT_CUBE_POINTS.iter()
            .map(|point| (point * cube::exp(width) as RealSpace) + origin)
            .map(|point| RawVertex::from(Vertex::new(point, color))));
        indeces.extend(UNIT_CUBE_INDECES.iter()
            .map(|j| ((UNIT_CUBE_POINTS.len() * i) as Index + *j)));
    }
    factory.create_vertex_buffer_with_slice(points.as_slice(), indeces.as_slice())
}

pub fn projection_from_window(window: &glutin::Window) -> Matrix4 {
    let aspect = {
        let (width, height) = window.get_inner_size_pixels().unwrap();
        width as f32 / height as f32
    };
    nalgebra::PerspectiveMatrix3::new(aspect, 1.0, -1.0, 1.0).to_matrix()
}

pub fn look_at_cube<C>(cube: &C, from: &Point3) -> Matrix4
    where C: cube::ComputedCube
{
    nalgebra::Isometry3::look_at_rh(from,
                                    &cube.partition().midpoint().into_domain(),
                                    &Vector3::new(0.0, 0.0, 1.0))
        .to_homogeneous()
}

#[cfg(test)]
mod tests {
    use super::*;
}
<|endoftext|>


<commit_before>//! Render README files to HTML.

use ammonia::{Builder, UrlRelative, UrlRelativeEvaluate};
use htmlescape::encode_minimal;
use std::borrow::Cow;
use std::path::Path;
use swirl::errors::PerformError;
use url::Url;

use crate::background_jobs::Environment;
use crate::models::Version;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
struct MarkdownRenderer<'a> {
    html_sanitizer: Builder<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    ///
    /// Per `readme_to_html`, `base_url` is the base URL prepended to any
    /// relative links in the input document.  See that function for more detail.
    fn new(base_url: Option<&'a str>) -> MarkdownRenderer<'a> {
        let tag_attributes = hashmap(&[
            ("a", hashset(&["id", "target"])),
            ("input", hashset(&["checked", "disabled", "type"])),
        ]);
        let allowed_classes = hashmap(&[(
            "code",
            hashset(&[
                "language-bash",
                "language-clike",
                "language-glsl",
                "language-go",
                "language-ini",
                "language-javascript",
                "language-json",
                "language-markup",
                "language-protobuf",
                "language-ruby",
                "language-rust",
                "language-scss",
                "language-sql",
                "yaml",
            ]),
        )]);
        let sanitize_url = UrlRelative::Custom(Box::new(SanitizeUrl::new(base_url)));

        let mut html_sanitizer = Builder::default();
        html_sanitizer
            .add_tags(&["input"])
            .link_rel(Some("nofollow noopener noreferrer"))
            .add_tag_attributes("a", &["id", "target"])
            .add_tag_attributes("input", &["checked", "disabled", "type"])
            .allowed_classes(allowed_classes)
            .url_relative(sanitize_url)
            .id_prefix(Some("user-content-"));
        MarkdownRenderer { html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    fn to_html(&self, text: &str) -> String {
        let options = comrak::ComrakOptions {
            unsafe_: true, // The output will be sanitized with `ammonia`
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ext_header_ids: Some("user-content-".to_string()),
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        self.html_sanitizer.clean(&rendered).to_string()
    }
}

/// Add trailing slash and remove `.git` suffix of base URL.
fn canon_base_url(mut base_url: String) -> String {
    if !base_url.ends_with('/') {
        base_url.push('/');
    }
    if base_url.ends_with(".git/") {
        let offset = base_url.len() - 5;
        base_url.drain(offset..offset + 4);
    }
    base_url
}

/// Sanitize relative URLs in README files.
struct SanitizeUrl {
    base_url: Option<String>,
}

impl SanitizeUrl {
    fn new(base_url: Option<&str>) -> Self {
        let base_url = base_url
            .and_then(|base_url| Url::parse(base_url).ok())
            .and_then(|url| match url.host_str() {
                Some("github.com") | Some("gitlab.com") | Some("bitbucket.org") => {
                    Some(canon_base_url(url.into_string()))
                }
                _ => None,
            });
        Self { base_url }
    }
}

/// Determine whether the given URL has a media file externsion.
fn is_media_url(url: &str) -> bool {
    Path::new(url)
        .extension()
        .and_then(std::ffi::OsStr::to_str)
        .map_or(false, |e| match e {
            "png" | "svg" | "jpg" | "jpeg" | "gif" | "mp4" | "webm" | "ogg" => true,
            _ => false,
        })
}

impl UrlRelativeEvaluate for SanitizeUrl {
    fn evaluate<'a>(&self, url: &'a str) -> Option<Cow<'a, str>> {
        if url.starts_with('#') {
            // Always allow fragment URLs.
            return Some(Cow::Borrowed(url));
        }
        self.base_url.as_ref().map(|base_url| {
            let mut new_url = base_url.clone();
            // Assumes GitHub’s URL scheme. GitHub renders text and markdown
            // better in the "blob" view, but images need to be served raw.
            new_url += if is_media_url(url) {
                "raw/master"
            } else {
                "blob/master"
            };
            if !url.starts_with('/') {
                new_url.push('/');
            }
            new_url += url;
            Cow::Owned(new_url)
        })
    }
}

/// Renders Markdown text to sanitized HTML with a given `base_url`.
/// See `readme_to_html` for the interpretation of `base_url`.
fn markdown_to_html(text: &str, base_url: Option<&str>) -> String {
    let renderer = MarkdownRenderer::new(base_url);
    renderer.to_html(text)
}

/// Any readme with a filename ending in one of these extensions will be rendered as Markdown.
/// Note we also render a readme as Markdown if _no_ extension is on the filename.
static MARKDOWN_EXTENSIONS: [&str; 7] = [
    ".md",
    ".markdown",
    ".mdown",
    ".mdwn",
    ".mkd",
    ".mkdn",
    ".mkdown",
];

/// Renders a readme to sanitized HTML.  An appropriate rendering method is chosen depending
/// on the extension of the supplied `filename`.
///
/// The returned text will not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// The `base_url` parameter will be used as the base for any relative links found in the
/// Markdown, as long as its host part is github.com, gitlab.com, or bitbucket.org.  The
/// supplied URL will be used as a directory base whether or not the relative link is
/// prefixed with '/'.  If `None` is passed, relative links will be omitted.
///
/// # Examples
///
/// ```
/// use render::render_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = readme_to_html(text, "README.md", None)?;
/// ```
pub fn readme_to_html(text: &str, filename: &str, base_url: Option<&str>) -> String {
    let filename = filename.to_lowercase();

    if !filename.contains('.') || MARKDOWN_EXTENSIONS.iter().any(|e| filename.ends_with(e)) {
        return markdown_to_html(text, base_url);
    }

    encode_minimal(text).replace("\n", "
\n")
}

#[swirl::background_job]
pub fn render_and_upload_readme(
    env: &Environment,
    version_id: i32,
    text: String,
    file_name: String,
    base_url: Option<String>,
) -> Result<(), PerformError> {
    use crate::schema::*;
    use crate::util::errors::std_error_no_send;
    use diesel::prelude::*;

    let rendered = readme_to_html(&text, &file_name, base_url.as_ref().map(String::as_str));
    let conn = env.connection()?;

    conn.transaction(|| {
        Version::record_readme_rendering(version_id, &conn)?;
        let (crate_name, vers) = versions::table
            .find(version_id)
            .inner_join(crates::table)
            .select((crates::name, versions::num))
            .first::<(String, String)>(&*conn)?;
        env.uploader
            .upload_readme(env.http_client(), &crate_name, &vers, rendered)
            .map_err(std_error_no_send)?;
        Ok(())
    })
}

/// Helper function to build a new `HashSet` from the items slice.
fn hashset<T>(items: &[T]) -> std::collections::HashSet<T>
where
    T: Clone + Eq + std::hash::Hash,
{
    items.iter().cloned().collect()
}

/// Helper function to build a new `HashMap` from a slice of key-value pairs.
fn hashmap<K, V>(items: &[(K, V)]) -> std::collections::HashMap<K, V>
where
    K: Clone + Eq + std::hash::Hash,
    V: Clone,
{
    items.iter().cloned().collect()
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text =
            r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
wb’
\n");
    }

    #[test]
    fn code_block_with_syntax_highlighting() {
        let code_block = r#"```rust \
                            println!("Hello World"); \
                           ```"#;
        let result = markdown_to_html(code_block, None);
        assert!(result.contains(""));
    }

    #[test]
    fn text_with_forbidden_class_attribute() {
        let text = "
Hello World!
";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
Hello World!
\n");
    }

    #[test]
    fn relative_links() {
        let absolute = "[hi](/hi)";
        let relative = "[there](there)";
        let image = "![alt](img.png)";

        for host in &["github.com", "gitlab.com", "bitbucket.org"] {
            for (&extra_slash, &dot_git) in [true, false].iter().zip(&[true, false]) {
                let url = format!(
                    "https://{}/rust-lang/test{}{}",
                    host,
                    if dot_git { ".git" } else { "" },
                    if extra_slash { "/" } else { "" },
                );

                let result = markdown_to_html(absolute, Some(&url));
                assert_eq!(
                    result,
                    format!(
                        "
hi
\n",
                        host
                    )
                );

                let result = markdown_to_html(relative, Some(&url));
                assert_eq!(
                    result,
                    format!(
                        "
there
\n",
                        host
                    )
                );

                let result = markdown_to_html(image, Some(&url));
                assert_eq!(
                    result,
                    format!(
                 "

\n",
                        host
                    )
                );
            }
        }

        let result = markdown_to_html(absolute, Some("https://google.com/"));
        assert_eq!(
            result,
            "
hi
\n"
        );
    }

    #[test]
    fn absolute_links_dont_get_resolved() {
        let readme_text =
            "[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap)";
        let repository = "https://github.com/kbknapp/clap-rs/";
        let result = markdown_to_html(readme_text, Some(repository));

        assert_eq!(
            result,
            "

\n"
        );
    }

    #[test]
    fn readme_to_html_renders_markdown() {
        for f in &["README", "readme.md", "README.MARKDOWN", "whatever.mkd"] {
            assert_eq!(
                readme_to_html("*lobster*", f, None),
                "
lobster
\n"
            );
        }
    }

    #[test]
    fn readme_to_html_renders_other_things() {
        for f in &["readme.exe", "readem.org", "blah.adoc"] {
            assert_eq!(
                readme_to_html("\n\nis my friend\n", f, None),
                "<script>lobster</script>
\n
\nis my friend
\n"
            );
        }
    }

    #[test]
    fn header_has_tags() {
        let text = "# My crate\n\nHello, world!\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
My crate
\n
Hello, world!
\n"
        );
    }

    #[test]
    fn manual_anchor_is_sanitized() {
        let text =
            "
My crate
\n
Hello, world!
\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
My crate
\n
Hello, world!
\n"
        );
    }

    #[test]
    fn tables_with_rowspan_and_colspan() {
        let text = "

Target

\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "

Target

\n"
        );
    }
}
<commit_msg>Remove useless attribution<commit_after>//! Render README files to HTML.

use ammonia::{Builder, UrlRelative, UrlRelativeEvaluate};
use htmlescape::encode_minimal;
use std::borrow::Cow;
use std::path::Path;
use swirl::errors::PerformError;
use url::Url;

use crate::background_jobs::Environment;
use crate::models::Version;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
struct MarkdownRenderer<'a> {
    html_sanitizer: Builder<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    ///
    /// Per `readme_to_html`, `base_url` is the base URL prepended to any
    /// relative links in the input document.  See that function for more detail.
    fn new(base_url: Option<&'a str>) -> MarkdownRenderer<'a> {
        let allowed_classes = hashmap(&[(
            "code",
            hashset(&[
                "language-bash",
                "language-clike",
                "language-glsl",
                "language-go",
                "language-ini",
                "language-javascript",
                "language-json",
                "language-markup",
                "language-protobuf",
                "language-ruby",
                "language-rust",
                "language-scss",
                "language-sql",
                "yaml",
            ]),
        )]);
        let sanitize_url = UrlRelative::Custom(Box::new(SanitizeUrl::new(base_url)));

        let mut html_sanitizer = Builder::default();
        html_sanitizer
            .add_tags(&["input"])
            .link_rel(Some("nofollow noopener noreferrer"))
            .add_tag_attributes("a", &["id", "target"])
            .add_tag_attributes("input", &["checked", "disabled", "type"])
            .allowed_classes(allowed_classes)
            .url_relative(sanitize_url)
            .id_prefix(Some("user-content-"));
        MarkdownRenderer { html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    fn to_html(&self, text: &str) -> String {
        let options = comrak::ComrakOptions {
            unsafe_: true, // The output will be sanitized with `ammonia`
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ext_header_ids: Some("user-content-".to_string()),
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        self.html_sanitizer.clean(&rendered).to_string()
    }
}

/// Add trailing slash and remove `.git` suffix of base URL.
fn canon_base_url(mut base_url: String) -> String {
    if !base_url.ends_with('/') {
        base_url.push('/');
    }
    if base_url.ends_with(".git/") {
        let offset = base_url.len() - 5;
        base_url.drain(offset..offset + 4);
    }
    base_url
}

/// Sanitize relative URLs in README files.
struct SanitizeUrl {
    base_url: Option<String>,
}

impl SanitizeUrl {
    fn new(base_url: Option<&str>) -> Self {
        let base_url = base_url
            .and_then(|base_url| Url::parse(base_url).ok())
            .and_then(|url| match url.host_str() {
                Some("github.com") | Some("gitlab.com") | Some("bitbucket.org") => {
                    Some(canon_base_url(url.into_string()))
                }
                _ => None,
            });
        Self { base_url }
    }
}

/// Determine whether the given URL has a media file externsion.
fn is_media_url(url: &str) -> bool {
    Path::new(url)
        .extension()
        .and_then(std::ffi::OsStr::to_str)
        .map_or(false, |e| match e {
            "png" | "svg" | "jpg" | "jpeg" | "gif" | "mp4" | "webm" | "ogg" => true,
            _ => false,
        })
}

impl UrlRelativeEvaluate for SanitizeUrl {
    fn evaluate<'a>(&self, url: &'a str) -> Option<Cow<'a, str>> {
        if url.starts_with('#') {
            // Always allow fragment URLs.
            return Some(Cow::Borrowed(url));
        }
        self.base_url.as_ref().map(|base_url| {
            let mut new_url = base_url.clone();
            // Assumes GitHub’s URL scheme. GitHub renders text and markdown
            // better in the "blob" view, but images need to be served raw.
            new_url += if is_media_url(url) {
                "raw/master"
            } else {
                "blob/master"
            };
            if !url.starts_with('/') {
                new_url.push('/');
            }
            new_url += url;
            Cow::Owned(new_url)
        })
    }
}

/// Renders Markdown text to sanitized HTML with a given `base_url`.
/// See `readme_to_html` for the interpretation of `base_url`.
fn markdown_to_html(text: &str, base_url: Option<&str>) -> String {
    let renderer = MarkdownRenderer::new(base_url);
    renderer.to_html(text)
}

/// Any readme with a filename ending in one of these extensions will be rendered as Markdown.
/// Note we also render a readme as Markdown if _no_ extension is on the filename.
static MARKDOWN_EXTENSIONS: [&str; 7] = [
    ".md",
    ".markdown",
    ".mdown",
    ".mdwn",
    ".mkd",
    ".mkdn",
    ".mkdown",
];

/// Renders a readme to sanitized HTML.  An appropriate rendering method is chosen depending
/// on the extension of the supplied `filename`.
///
/// The returned text will not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// The `base_url` parameter will be used as the base for any relative links found in the
/// Markdown, as long as its host part is github.com, gitlab.com, or bitbucket.org.  The
/// supplied URL will be used as a directory base whether or not the relative link is
/// prefixed with '/'.  If `None` is passed, relative links will be omitted.
///
/// # Examples
///
/// ```
/// use render::render_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = readme_to_html(text, "README.md", None)?;
/// ```
pub fn readme_to_html(text: &str, filename: &str, base_url: Option<&str>) -> String {
    let filename = filename.to_lowercase();

    if !filename.contains('.') || MARKDOWN_EXTENSIONS.iter().any(|e| filename.ends_with(e)) {
        return markdown_to_html(text, base_url);
    }

    encode_minimal(text).replace("\n", "
\n")
}

#[swirl::background_job]
pub fn render_and_upload_readme(
    env: &Environment,
    version_id: i32,
    text: String,
    file_name: String,
    base_url: Option<String>,
) -> Result<(), PerformError> {
    use crate::schema::*;
    use crate::util::errors::std_error_no_send;
    use diesel::prelude::*;

    let rendered = readme_to_html(&text, &file_name, base_url.as_ref().map(String::as_str));
    let conn = env.connection()?;

    conn.transaction(|| {
        Version::record_readme_rendering(version_id, &conn)?;
        let (crate_name, vers) = versions::table
            .find(version_id)
            .inner_join(crates::table)
            .select((crates::name, versions::num))
            .first::<(String, String)>(&*conn)?;
        env.uploader
            .upload_readme(env.http_client(), &crate_name, &vers, rendered)
            .map_err(std_error_no_send)?;
        Ok(())
    })
}

/// Helper function to build a new `HashSet` from the items slice.
fn hashset<T>(items: &[T]) -> std::collections::HashSet<T>
where
    T: Clone + Eq + std::hash::Hash,
{
    items.iter().cloned().collect()
}

/// Helper function to build a new `HashMap` from a slice of key-value pairs.
fn hashmap<K, V>(items: &[(K, V)]) -> std::collections::HashMap<K, V>
where
    K: Clone + Eq + std::hash::Hash,
    V: Clone,
{
    items.iter().cloned().collect()
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text =
            r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
wb’
\n");
    }

    #[test]
    fn code_block_with_syntax_highlighting() {
        let code_block = r#"```rust \
                            println!("Hello World"); \
                           ```"#;
        let result = markdown_to_html(code_block, None);
        assert!(result.contains(""));
    }

    #[test]
    fn text_with_forbidden_class_attribute() {
        let text = "
Hello World!
";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
Hello World!
\n");
    }

    #[test]
    fn relative_links() {
        let absolute = "[hi](/hi)";
        let relative = "[there](there)";
        let image = "![alt](img.png)";

        for host in &["github.com", "gitlab.com", "bitbucket.org"] {
            for (&extra_slash, &dot_git) in [true, false].iter().zip(&[true, false]) {
                let url = format!(
                    "https://{}/rust-lang/test{}{}",
                    host,
                    if dot_git { ".git" } else { "" },
                    if extra_slash { "/" } else { "" },
                );

                let result = markdown_to_html(absolute, Some(&url));
                assert_eq!(
                    result,
                    format!(
                        "
hi
\n",
                        host
                    )
                );

                let result = markdown_to_html(relative, Some(&url));
                assert_eq!(
                    result,
                    format!(
                        "
there
\n",
                        host
                    )
                );

                let result = markdown_to_html(image, Some(&url));
                assert_eq!(
                    result,
                    format!(
                 "

\n",
                        host
                    )
                );
            }
        }

        let result = markdown_to_html(absolute, Some("https://google.com/"));
        assert_eq!(
            result,
            "
hi
\n"
        );
    }

    #[test]
    fn absolute_links_dont_get_resolved() {
        let readme_text =
            "[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap)";
        let repository = "https://github.com/kbknapp/clap-rs/";
        let result = markdown_to_html(readme_text, Some(repository));

        assert_eq!(
            result,
            "

\n"
        );
    }

    #[test]
    fn readme_to_html_renders_markdown() {
        for f in &["README", "readme.md", "README.MARKDOWN", "whatever.mkd"] {
            assert_eq!(
                readme_to_html("*lobster*", f, None),
                "
lobster
\n"
            );
        }
    }

    #[test]
    fn readme_to_html_renders_other_things() {
        for f in &["readme.exe", "readem.org", "blah.adoc"] {
            assert_eq!(
                readme_to_html("\n\nis my friend\n", f, None),
                "<script>lobster</script>
\n
\nis my friend
\n"
            );
        }
    }

    #[test]
    fn header_has_tags() {
        let text = "# My crate\n\nHello, world!\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
My crate
\n
Hello, world!
\n"
        );
    }

    #[test]
    fn manual_anchor_is_sanitized() {
        let text =
            "
My crate
\n
Hello, world!
\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
My crate
\n
Hello, world!
\n"
        );
    }

    #[test]
    fn tables_with_rowspan_and_colspan() {
        let text = "

Target

\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "

Target

\n"
        );
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::error::Error;
use std::fmt;
use std::io;
use std::result::Result;

use controls::Control;
use protocol::LdapResultExt;

use lber::structure::StructureTag;
use lber::structures::Tag;

/// Common components of an LDAP operation result.
///
/// This structure faithfully replicates the components dictated by the standard,
/// and is distinctly C-like with its reliance on numeric codes for the indication
/// of outcome. It would be tempting to hide it behind an automatic `Result`-like
/// interface, but there are scenarios where this would preclude intentional
/// incorporation of error conditions into query design. Instead, the struct
/// implements helper methods, [`success()`](#method.success) and [`non_error()`]
/// (#method.non_error), which may be used for ergonomic error handling when
/// simple condition checking suffices.
#[derive(Clone, Debug)]
pub struct LdapResult {
    /// Result code.
    ///
    /// Generally, the value of zero indicates successful completion, but there's
    /// a number of other non-error codes arising as a result of various operations.
    /// See [Section A.1 of RFC 4511](https://tools.ietf.org/html/rfc4511#appendix-A.1).
    pub rc: u32,
    /// Matched component DN, where applicable.
    pub matched: String,
    /// Additional diagnostic text.
    pub text: String,
    /// Referrals.
    ///
    /// In the current implementation, all referrals received during a Search
    /// operation will be accumulated in this vector.
    pub refs: Vec<HashSet<String>>,
    /// Response controls.
    ///
    /// Missing and empty controls are both represented by an empty vector.
    pub ctrls: Vec<Control>,
}

#[doc(hidden)]
impl From<Tag> for LdapResult {
    fn from(t: Tag) -> LdapResult {
        <LdapResultExt as From<Tag>>::from(t).0
    }
}

impl Error for LdapResult {
    fn description(&self) -> &'static str {
	match self.rc {
	    0 => "success",
	    1 => "operationsError",
	    2 => "protocolError",
	    3 => "timeLimitExceeded",
	    4 => "sizeLimitExceeded",
	    5 => "compareFalse",
	    6 => "compareTrue",
	    7 => "authMethodNotSupported",
	    8 => "strongerAuthRequired",
	    10 => "referral",
	    11 => "adminLimitExceeded",
	    12 => "unavailableCriticalExtension",
	    13 => "confidentialityRequired",
	    14 => "saslBindInProgress",
	    16 => "noSuchAttribute",
	    17 => "undefinedAttributeType",
	    18 => "inappropriateMatching",
	    19 => "constraintViolation",
	    20 => "attributeOrValueExists",
	    21 => "invalidAttributeSyntax",
	    32 => "noSuchObject",
	    33 => "aliasProblem",
	    34 => "invalidDNSyntax",
	    36 => "aliasDereferencingProblem",
	    48 => "inappropriateAuthentication",
	    49 => "invalidCredentials",
	    50 => "insufficientAccessRights",
	    51 => "busy",
	    52 => "unavailable",
	    53 => "unwillingToPerform",
	    54 => "loopDetect",
	    64 => "namingViolation",
	    65 => "objectClassViolation",
	    66 => "notAllowedOnNonLeaf",
	    67 => "notAllowedOnRDN",
	    68 => "entryAlreadyExists",
	    69 => "objectClassModsProhibited",
	    71 => "affectsMultipleDSAs",
	    80 => "other",
	    88 => "abandoned",
	    _ => "unknown",
	}
    }
}

impl fmt::Display for LdapResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
	write!(f,
	    "rc={} ({}), dn: \"{}\", text: \"{}\"",
	    self.rc,
	    self.description(),
	    self.matched,
            self.text
        )
    }
}

impl LdapResult {
    /// If the result code is zero, return the instance itself wrapped
    /// in `Ok()`, otherwise wrap the instance in an `io::Error`.
    pub fn success(self) -> Result<Self, io::Error> {
        if self.rc == 0 {
            Ok(self)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self))
        }
    }

    /// If the result code is 0 or 10 (referral), return the instance
    /// itself wrapped in `Ok()`, otherwise wrap the instance in an
    /// io::Error`.
    pub fn non_error(self) -> Result<Self, io::Error> {
        if self.rc == 0 || self.rc == 10 {
            Ok(self)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self))
        }
    }
}

/// Wrapper for results of a Search operation which returns all entries at once.
///
/// The wrapper exists so that methods [`success()`](#method.success) and
/// [`non_error()`](#method.non_error) can be called on an instance. Those methods
/// destructure the wrapper and return its components as elements of an anonymous
/// tuple.
#[derive(Clone, Debug)]
pub struct SearchResult(pub Vec<StructureTag>, pub LdapResult);

impl SearchResult {
    /// If the result code is zero, return an anonymous tuple of component structs
    /// wrapped in `Ok()`, otherwise wrap the `LdapResult` part in an `io::Error`.
    pub fn success(self) -> Result<(Vec<StructureTag>, LdapResult), io::Error> {
        if self.1.rc == 0 {
            Ok((self.0, self.1))
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self.1))
        }
    }

    /// If the result code is 0 or 10 (referral), return an anonymous tuple of component
    /// structs wrapped in `Ok()`, otherwise wrap the `LdapResult` part in an `io::Error`.
    pub fn non_error(self) -> Result<(Vec<StructureTag>, LdapResult), io::Error> {
        if self.1.rc == 0 || self.1.rc == 10 {
            Ok((self.0, self.1))
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self.1))
        }
    }
}

#[derive(Clone, Debug)]
pub struct CompareResult(pub LdapResult);

impl CompareResult {
    /// If the result code is 5 (compareFalse) or 6 (compareTrue), return the corresponding
    /// boolean value wrapped in `Ok()`, otherwise wrap the `LdapResult` part in an `io::Error`.
    pub fn equal(self) -> Result<bool, io::Error> {
        match self.0.rc {
            5 => Ok(false),
            6 => Ok(true),
            _ => Err(io::Error::new(io::ErrorKind::Other, self.0))
        }
    }

    /// If the result code is 5 (compareFalse), 6 (compareTrue),  or 10 (referral), return
    /// the inner `LdapResult`, otherwise rewrap `LdapResult` in an `io::Error`.
    pub fn non_error(self) -> Result<LdapResult, io::Error> {
        if self.0.rc == 5 || self.0.rc == 6 || self.0.rc == 10 {
            Ok(self.0)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self.0))
        }
    }
}
<commit_msg>Update docs<commit_after>//! Operation result structures and helpers.
//!
//! Most LDAP operations return a [`LdapResult`](#struct.LdapResult). This module
//! contains its definition, as well as that of a number of wrapper structs and
//! helper methods, which adapt the LDAP result and error handling to be a closer
//! match to Rust conventions.

use std::collections::HashSet;
use std::error::Error;
use std::fmt;
use std::io;
use std::result::Result;

use controls::Control;
use protocol::LdapResultExt;

use lber::structure::StructureTag;
use lber::structures::Tag;

/// Common components of an LDAP operation result.
///
/// This structure faithfully replicates the components dictated by the standard,
/// and is distinctly C-like with its reliance on numeric codes for the indication
/// of outcome. It would be tempting to hide it behind an automatic `Result`-like
/// interface, but there are scenarios where this would preclude intentional
/// incorporation of error conditions into query design. Instead, the struct
/// implements helper methods, [`success()`](#method.success) and [`non_error()`]
/// (#method.non_error), which may be used for ergonomic error handling when
/// simple condition checking suffices.
#[derive(Clone, Debug)]
pub struct LdapResult {
    /// Result code.
    ///
    /// Generally, the value of zero indicates successful completion, but there's
    /// a number of other non-error codes arising as a result of various operations.
    /// See [Section A.1 of RFC 4511](https://tools.ietf.org/html/rfc4511#appendix-A.1).
    pub rc: u32,
    /// Matched component DN, where applicable.
    pub matched: String,
    /// Additional diagnostic text.
    pub text: String,
    /// Referrals.
    ///
    /// In the current implementation, all referrals received during a Search
    /// operation will be accumulated in this vector.
    pub refs: Vec<HashSet<String>>,
    /// Response controls.
    ///
    /// Missing and empty controls are both represented by an empty vector.
    pub ctrls: Vec<Control>,
}

#[doc(hidden)]
impl From<Tag> for LdapResult {
    fn from(t: Tag) -> LdapResult {
        <LdapResultExt as From<Tag>>::from(t).0
    }
}

impl Error for LdapResult {
    fn description(&self) -> &'static str {
	match self.rc {
	    0 => "success",
	    1 => "operationsError",
	    2 => "protocolError",
	    3 => "timeLimitExceeded",
	    4 => "sizeLimitExceeded",
	    5 => "compareFalse",
	    6 => "compareTrue",
	    7 => "authMethodNotSupported",
	    8 => "strongerAuthRequired",
	    10 => "referral",
	    11 => "adminLimitExceeded",
	    12 => "unavailableCriticalExtension",
	    13 => "confidentialityRequired",
	    14 => "saslBindInProgress",
	    16 => "noSuchAttribute",
	    17 => "undefinedAttributeType",
	    18 => "inappropriateMatching",
	    19 => "constraintViolation",
	    20 => "attributeOrValueExists",
	    21 => "invalidAttributeSyntax",
	    32 => "noSuchObject",
	    33 => "aliasProblem",
	    34 => "invalidDNSyntax",
	    36 => "aliasDereferencingProblem",
	    48 => "inappropriateAuthentication",
	    49 => "invalidCredentials",
	    50 => "insufficientAccessRights",
	    51 => "busy",
	    52 => "unavailable",
	    53 => "unwillingToPerform",
	    54 => "loopDetect",
	    64 => "namingViolation",
	    65 => "objectClassViolation",
	    66 => "notAllowedOnNonLeaf",
	    67 => "notAllowedOnRDN",
	    68 => "entryAlreadyExists",
	    69 => "objectClassModsProhibited",
	    71 => "affectsMultipleDSAs",
	    80 => "other",
	    88 => "abandoned",
	    _ => "unknown",
	}
    }
}

impl fmt::Display for LdapResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
	write!(f,
	    "rc={} ({}), dn: \"{}\", text: \"{}\"",
	    self.rc,
	    self.description(),
	    self.matched,
            self.text
        )
    }
}

impl LdapResult {
    /// If the result code is zero, return the instance itself wrapped
    /// in `Ok()`, otherwise wrap the instance in an `io::Error`.
    pub fn success(self) -> Result<Self, io::Error> {
        if self.rc == 0 {
            Ok(self)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self))
        }
    }

    /// If the result code is 0 or 10 (referral), return the instance
    /// itself wrapped in `Ok()`, otherwise wrap the instance in an
    /// io::Error`.
    pub fn non_error(self) -> Result<Self, io::Error> {
        if self.rc == 0 || self.rc == 10 {
            Ok(self)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self))
        }
    }
}

/// Wrapper for results of a Search operation which returns all entries at once.
///
/// The wrapper exists so that methods [`success()`](#method.success) and
/// [`non_error()`](#method.non_error) can be called on an instance. Those methods
/// destructure the wrapper and return its components as elements of an anonymous
/// tuple.
#[derive(Clone, Debug)]
pub struct SearchResult(pub Vec<StructureTag>, pub LdapResult);

impl SearchResult {
    /// If the result code is zero, return an anonymous tuple of component structs
    /// wrapped in `Ok()`, otherwise wrap the `LdapResult` part in an `io::Error`.
    pub fn success(self) -> Result<(Vec<StructureTag>, LdapResult), io::Error> {
        if self.1.rc == 0 {
            Ok((self.0, self.1))
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self.1))
        }
    }

    /// If the result code is 0 or 10 (referral), return an anonymous tuple of component
    /// structs wrapped in `Ok()`, otherwise wrap the `LdapResult` part in an `io::Error`.
    pub fn non_error(self) -> Result<(Vec<StructureTag>, LdapResult), io::Error> {
        if self.1.rc == 0 || self.1.rc == 10 {
            Ok((self.0, self.1))
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self.1))
        }
    }
}

/// Wraper for the result of a Compare operation.
///
/// Compare uniquely has two non-zero return codes to indicate the outcome of a successful
/// comparison, while other return codes indicate errors, as usual (except 10 for referral).
/// The [`equal()`](#method.equal) method optimizes for the expected case of ignoring
/// referrals; [`non_error()`](#method.non_error) can be used when that's not possible.
#[derive(Clone, Debug)]
pub struct CompareResult(pub LdapResult);

impl CompareResult {
    /// If the result code is 5 (compareFalse) or 6 (compareTrue), return the corresponding
    /// boolean value wrapped in `Ok()`, otherwise wrap the `LdapResult` part in an `io::Error`.
    pub fn equal(self) -> Result<bool, io::Error> {
        match self.0.rc {
            5 => Ok(false),
            6 => Ok(true),
            _ => Err(io::Error::new(io::ErrorKind::Other, self.0))
        }
    }

    /// If the result code is 5 (compareFalse), 6 (compareTrue),  or 10 (referral), return
    /// the inner `LdapResult`, otherwise rewrap `LdapResult` in an `io::Error`.
    pub fn non_error(self) -> Result<LdapResult, io::Error> {
        if self.0.rc == 5 || self.0.rc == 6 || self.0.rc == 10 {
            Ok(self.0)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self.0))
        }
    }
}
<|endoftext|>


<commit_before>extern crate dbus;
use self::dbus::{Connection, ConnectionItem, Message, NameFlag, ReleaseNameReply};

use super::error::DBusError;
use super::interface::DBusInterface;
use super::object::DBusObject;
use super::target::{DBusTarget, extract_target};

use std::collections::btree_map::{BTreeMap, Entry};
use std::error::Error;

pub struct DBusServer<'a> {
    conn: &'a Connection,
    name: String,

    objects: BTreeMap<String, DBusObject<'a>>,
    signals: BTreeMap<DBusTarget, Vec<fn (&Connection, &DBusTarget) -> ()>>
}

impl<'a> DBusServer<'a> {
    pub fn new(conn: &'a Connection, name: &str) -> Result<DBusServer<'a>, dbus::Error> {
        try!(conn.register_name(name, NameFlag::DoNotQueue as u32));

        Ok(DBusServer {
            conn: conn,
            name: name.to_string(),

            objects: BTreeMap::new(),
            signals: BTreeMap::new(),
        })
    }

    pub fn add_object(&mut self, path: &str, ifaces: BTreeMap<String, Box<DBusInterface>>) -> Result<&DBusObject<'a>, Box<Error>> {
        match self.objects.entry(path.to_string()) {
            Entry::Vacant(v)    => Ok(v.insert(try!(DBusObject::new(self.conn, ifaces, path)))),
            Entry::Occupied(_)  => Err(Box::new(DBusError::PathAlreadyRegistered(path.to_string()))),
        }
    }

    pub fn remove_object(&mut self, path: &str) -> Result<(), DBusError> {
        match self.objects.remove(path) {
            Some(_) => Ok(()),
            None    => Err(DBusError::NoSuchPath(path.to_string())),
        }
    }

    pub fn connect(&mut self, signal: DBusTarget, callback: fn (&Connection, &DBusTarget) -> ()) -> () {
        match self.signals.entry(signal) {
            Entry::Vacant(v)    => { v.insert(vec![callback]); },
            Entry::Occupied(o)  => o.into_mut().push(callback),
        };
    }

    pub fn run(&mut self) -> () {
        self.conn.iter(100).fold((), |_, item| {
            match item {
                ConnectionItem::MethodCall(m)   => self._call_method(m),
                ConnectionItem::Signal(s)       => self._match_signal(s),
                ConnectionItem::Nothing         => (),
            }
        });
    }

    fn _call_method(&mut self, m: Message) -> () {
        let conn = self.conn;

        self._find_interface(&m).map(|(ref mut iface, ref method)| {
            iface.call_method(&method, conn, &m).map(|result| {
                conn.send(result)
            })
        });
    }

    fn _find_interface(&mut self, m: &Message) -> Option<(&mut DBusInterface, String)> {
        let ref mut objects = self.objects;

        extract_target(&m).and_then(move |method| {
            objects.get_mut(&method.1).and_then(|dbus_object| {
                dbus_object.get_interface_mut(&method.0).map(|dbus_interface| {
                    (dbus_interface, method.2.clone())
                })
            })
        })
    }

    fn _match_signal(&self, m: Message) -> () {
        // TODO: Implement.
    }
}

impl<'a> Drop for DBusServer<'a> {
    fn drop(&mut self) {
        let res = self.conn.release_name(&self.name);
        match res {
            Ok(reply) =>
                match reply {
                    ReleaseNameReply::Released    => (),
                    ReleaseNameReply::NonExistent => panic!("internal error: non-existent name {}?!", self.name),
                    ReleaseNameReply::NotOwner    => panic!("internal error: not the owner of {}?!", self.name),
                },
            Err(err) => println!("failed to release {}: {:?}: {:?}", self.name, err.name(), err.message()),
        }
    }
}
<commit_msg>server: implement signal handling<commit_after>extern crate dbus;
use self::dbus::{Connection, ConnectionItem, Message, NameFlag, ReleaseNameReply};

use super::error::DBusError;
use super::interface::DBusInterface;
use super::object::DBusObject;
use super::target::{DBusTarget, extract_target};

use std::collections::btree_map::{BTreeMap, Entry};
use std::error::Error;

pub struct DBusServer<'a> {
    conn: &'a Connection,
    name: String,

    objects: BTreeMap<String, DBusObject<'a>>,
    signals: BTreeMap<DBusTarget, Vec<fn (&Connection, &DBusTarget) -> ()>>
}

impl<'a> DBusServer<'a> {
    pub fn new(conn: &'a Connection, name: &str) -> Result<DBusServer<'a>, dbus::Error> {
        try!(conn.register_name(name, NameFlag::DoNotQueue as u32));

        Ok(DBusServer {
            conn: conn,
            name: name.to_string(),

            objects: BTreeMap::new(),
            signals: BTreeMap::new(),
        })
    }

    pub fn add_object(&mut self, path: &str, ifaces: BTreeMap<String, Box<DBusInterface>>) -> Result<&DBusObject<'a>, Box<Error>> {
        match self.objects.entry(path.to_string()) {
            Entry::Vacant(v)    => Ok(v.insert(try!(DBusObject::new(self.conn, ifaces, path)))),
            Entry::Occupied(_)  => Err(Box::new(DBusError::PathAlreadyRegistered(path.to_string()))),
        }
    }

    pub fn remove_object(&mut self, path: &str) -> Result<(), DBusError> {
        match self.objects.remove(path) {
            Some(_) => Ok(()),
            None    => Err(DBusError::NoSuchPath(path.to_string())),
        }
    }

    pub fn connect(&mut self, signal: DBusTarget, callback: fn (&Connection, &DBusTarget) -> ()) -> () {
        match self.signals.entry(signal) {
            Entry::Vacant(v)    => { v.insert(vec![callback]); },
            Entry::Occupied(o)  => o.into_mut().push(callback),
        };
    }

    pub fn run(&mut self) -> () {
        self.conn.iter(100).fold((), |_, item| {
            match item {
                ConnectionItem::MethodCall(m)   => self._call_method(m),
                ConnectionItem::Signal(s)       => self._match_signal(s),
                ConnectionItem::Nothing         => (),
            }
        });
    }

    fn _call_method(&mut self, m: Message) -> () {
        let conn = self.conn;

        self._find_interface(&m).map(|(ref mut iface, ref method)| {
            iface.call_method(&method, conn, &m).map(|result| {
                conn.send(result)
            })
        });
    }

    fn _find_interface(&mut self, m: &Message) -> Option<(&mut DBusInterface, String)> {
        let ref mut objects = self.objects;

        extract_target(&m).and_then(move |method| {
            objects.get_mut(&method.1).and_then(|dbus_object| {
                dbus_object.get_interface_mut(&method.0).map(|dbus_interface| {
                    (dbus_interface, method.2.clone())
                })
            })
        })
    }

    fn _match_signal(&self, m: Message) -> () {
        let ref signals = self.signals;
        let conn = self.conn;

        extract_target(&m).and_then(|signal| {
            signals.get(&signal).map(|fs| {
                fs.iter().fold((conn, signal), |(conn, signal), f| {
                    f(&conn, &signal);
                    (conn, signal)
                })
            })
        });
    }
}

impl<'a> Drop for DBusServer<'a> {
    fn drop(&mut self) {
        let res = self.conn.release_name(&self.name);
        match res {
            Ok(reply) =>
                match reply {
                    ReleaseNameReply::Released    => (),
                    ReleaseNameReply::NonExistent => panic!("internal error: non-existent name {}?!", self.name),
                    ReleaseNameReply::NotOwner    => panic!("internal error: not the owner of {}?!", self.name),
                },
            Err(err) => println!("failed to release {}: {:?}: {:?}", self.name, err.name(), err.message()),
        }
    }
}
<|endoftext|>


<commit_before>//! `Server` listens for HTTP requests.
//!
//!```rust
//!let server = Server {
//!	router: ~Router::from_vec(routes),
//!	port: 8080
//!};
//!
//!server.run();
//!```

use router::Router;
use http::server::{Request, ResponseWriter, Config};
use http::server::request::{AbsoluteUri, AbsolutePath, Star};
use http::headers::content_type::MediaType;
use std::io::net::ip::{SocketAddr, Ipv4Addr, Port};
use extra::time;

use HTTP = http::server::Server;

#[deriving(Clone)]
pub struct Server {
	///A routing tree with response handlers
	router: ~Router,

	///The port where the server will listen for requests
	port: Port
}

impl Server {
	///Start the server and run forever.
	///This will only return if the initial connection fails.
	pub fn run(&self) {
		self.clone().serve_forever();
	}
}

impl HTTP for Server {
	fn get_config(&self) -> Config {
		Config {
			bind_address: SocketAddr {
				ip: Ipv4Addr(127, 0, 0, 1),
				port: self.port
			}
		}
	}

	fn handle_request(&self, request: &Request, writer: &mut ResponseWriter) {
		let response = match request.request_uri {
			Star => self.router.route(&"*"),
			AbsoluteUri(ref url) => self.router.route(url.path),
			AbsolutePath(ref path) => self.router.route(path.to_owned()),
			_ => None
		};

		let content = match response {
			Some(text) => text,
			None => ~"Error"
		}.as_bytes().to_owned();

		writer.headers.date = Some(time::now_utc());
		writer.headers.content_length = Some(content.len());
		writer.headers.content_type = Some(MediaType {
			type_: ~"text",
			subtype: ~"plain",
			parameters: ~[(~"charset", ~"UTF-8")]
		});
		writer.headers.server = Some(~"rustful");
		match writer.write(content) {
			Err(e) => println!("error when writing response: {}", e),
			_ => {}
		}
	}
}<commit_msg>Changed to non-specific binding address<commit_after>//! `Server` listens for HTTP requests.
//!
//!```rust
//!let server = Server {
//!	router: ~Router::from_vec(routes),
//!	port: 8080
//!};
//!
//!server.run();
//!```

use router::Router;
use http::server::{Request, ResponseWriter, Config};
use http::server::request::{AbsoluteUri, AbsolutePath, Star};
use http::headers::content_type::MediaType;
use std::io::net::ip::{SocketAddr, Ipv4Addr, Port};
use extra::time;

use HTTP = http::server::Server;

#[deriving(Clone)]
pub struct Server {
	///A routing tree with response handlers
	router: ~Router,

	///The port where the server will listen for requests
	port: Port
}

impl Server {
	///Start the server and run forever.
	///This will only return if the initial connection fails.
	pub fn run(&self) {
		self.clone().serve_forever();
	}
}

impl HTTP for Server {
	fn get_config(&self) -> Config {
		Config {
			bind_address: SocketAddr {
				ip: Ipv4Addr(0, 0, 0, 0),
				port: self.port
			}
		}
	}

	fn handle_request(&self, request: &Request, writer: &mut ResponseWriter) {
		let response = match request.request_uri {
			Star => self.router.route(&"*"),
			AbsoluteUri(ref url) => self.router.route(url.path),
			AbsolutePath(ref path) => self.router.route(path.to_owned()),
			_ => None
		};

		let content = match response {
			Some(text) => text,
			None => ~"Error"
		}.as_bytes().to_owned();

		writer.headers.date = Some(time::now_utc());
		writer.headers.content_length = Some(content.len());
		writer.headers.content_type = Some(MediaType {
			type_: ~"text",
			subtype: ~"plain",
			parameters: ~[(~"charset", ~"UTF-8")]
		});
		writer.headers.server = Some(~"rustful");
		match writer.write(content) {
			Err(e) => println!("error when writing response: {}", e),
			_ => {}
		}
	}
}<|endoftext|>


<commit_before>mod thread_guard;

use std::fmt;
use std::io;
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use std::thread;
use std::time::{Duration, Instant};
use std::ops::Deref;

use serial::prelude::*;
use serial;
use self::thread_guard::*;
use super::Packet;

pub enum ConnectionEvent {
    Packet(Packet),
    InvalidPacket(Packet),
    ReadError(io::Error),
    WriteError(io::Error),
    Heartbeat
}

impl fmt::Display for ConnectionEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionEvent::Packet(ref p) => write!(f, "{}", p),
            &ConnectionEvent::InvalidPacket(ref p) => write!(f, "Invalid! {}", p),
            &ConnectionEvent::ReadError(ref err) => write!(f, "READ ERROR! [{:?}]", err),
            &ConnectionEvent::WriteError(ref err) => write!(f, "WRITE ERROR! [{:?}]", err),
            &ConnectionEvent::Heartbeat => write!(f, "Tick Tock")
        }
    }
}

pub struct Connection {
    pub tty_path: String,
    event_receiver: Receiver<ConnectionEvent>,
    packet_sender: Sender<Packet>,
    kill_thread_signal: Arc<Mutex<bool>>,
    reader_thread_handle: Option<ThreadHandle<()>>,
}

impl Connection {
    pub fn new(tty_path: &str, heartbeat_milliseconds: Option<u64>) -> io::Result<Connection> {
        let (event_sender, event_receiver) = channel::<ConnectionEvent>();
        let (packet_sender, packet_receiver) = channel::<Packet>();

        let kill_signal = Arc::new(Mutex::new(false));

        let reader_thread = build_connection_read_thread(tty_path.to_string(), event_sender, packet_receiver, heartbeat_milliseconds, kill_signal.clone());

        Ok(Connection {
            tty_path: tty_path.to_string(),
            event_receiver: event_receiver,
            packet_sender: packet_sender,
            kill_thread_signal: kill_signal,
            reader_thread_handle: Some(reader_thread)
        })
    }

    pub fn send(&mut self, p: Packet) -> io::Result<()> {
        match self.is_ok() {
            true => {
                self.packet_sender.send(p).unwrap();
                Ok(())
            },
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped"))
        }
    }

    pub fn wait(&self) -> io::Result<ConnectionEvent> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                Ok(self.event_receiver.recv().unwrap())
            }
        }
    }

    pub fn get_events(&self) -> io::Result<Vec<ConnectionEvent>> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                let mut data: Vec<ConnectionEvent> = vec!();

                loop {
                    match self.event_receiver.try_recv() {
                        Ok(evt) => { data.push(evt) },
                        Err(TryRecvError::Empty) => { break; },
                        Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
                    }
                }

                Ok(data)
            }
        }
    }

    pub fn is_ok(&self) -> bool {
        let mut ret = true;

        match &self.reader_thread_handle {
            &Some(ref h) => {
                ret = ret && match h.status.lock().unwrap().deref() {
                    &ThreadStatus::Ok => true,
                    _ => false
                }
            },
            _ => {}
        }
        ret
    }
}

impl Drop for Connection {
    fn drop(&mut self) {

        {
            *self.kill_thread_signal.lock().unwrap() = true;
        }

        match self.reader_thread_handle.take() {
            Some(th) => {
                th.handle.join().unwrap();
            },
            None => {}
        }
    }
}

fn build_connection_read_thread(tty_path: String, event_sender: Sender<ConnectionEvent>, packet_receiver: Receiver<Packet>, heartbeat: Option<u64>, kill_signal: Arc<Mutex<bool>>) -> ThreadHandle<()> {
    guard_thread("reader_thread", move || {
        let mut serial = match serial::open(&tty_path) {
            Err(e) => {panic!(format!("Unable to create serial port: {}", e)) },
            Ok(s) => s
        };

        match serial.set_timeout(Duration::from_millis(1000)) {
            Err(e) => { panic!(format!("Unable to set serial timeout: {}", e)) },
            Ok(_) => {}
        };

        let mut packet_buffer: Vec<u8> = Vec::new();
        let mut read_buffer: Vec<u8> = Vec::new();
        let mut last_read = Instant::now();
        let mut last_heartbeat = Instant::now();

        while !*kill_signal.lock().unwrap() {

            if packet_buffer.len() > 0 && last_read.elapsed().as_secs() > 2 {
                let evt = ConnectionEvent::ReadError(io::Error::new(io::ErrorKind::InvalidData, format!("Invalid packet data: {:?}", packet_buffer.clone())));
                event_sender.send(evt).unwrap();
                packet_buffer.clear();
            };

            match packet_receiver.try_recv() {
                Ok(p) => {
                    match serial.write_all(&p.data) {
                        Ok(_) => {},
                        Err(e) => {
                            let evt = ConnectionEvent::WriteError(e);
                            event_sender.send(evt).unwrap();
                        }
                    }
                },
                Err(TryRecvError::Empty) => {  },
                Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
            }

            match serial.read(&mut read_buffer) {
                Err(e) => {
                    let evt = ConnectionEvent::ReadError(e);
                    event_sender.send(evt).unwrap();
                },
                Ok(bytes) if bytes > 0 => {
                    last_read = Instant::now();
                    for x in 0 .. bytes {
                        packet_buffer.push(read_buffer[x]);
                    }
                },
                Ok(_) => {} // do nothing for 0 bytes read
            };

            while packet_buffer.len() >= 128 {
                let data: Vec<u8> = packet_buffer.drain(0..128).collect();
                let p = Packet::from_bytes(&data);

                if p.is_checksum_valid() {
                    let evt = ConnectionEvent::Packet(p);
                    event_sender.send(evt).unwrap();
                } else {
                    let evt = ConnectionEvent::InvalidPacket(p);
                    event_sender.send(evt).unwrap();
                }
            }

            match heartbeat {
                Some(ms) => {
                    if last_heartbeat.elapsed() >= Duration::from_millis(ms) {
                        let evt = ConnectionEvent::Heartbeat;
                        event_sender.send(evt).unwrap();
                        last_heartbeat = Instant::now();
                    }
                },
                None => {}
            }

            thread::sleep(Duration::from_millis(250));
        }
        ()
    })
}
<commit_msg>debug<commit_after>mod thread_guard;

use std::fmt;
use std::io;
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use std::thread;
use std::time::{Duration, Instant};
use std::ops::Deref;

use serial::prelude::*;
use serial;
use self::thread_guard::*;
use super::Packet;

pub enum ConnectionEvent {
    Packet(Packet),
    InvalidPacket(Packet),
    ReadError(io::Error),
    WriteError(io::Error),
    Heartbeat
}

impl fmt::Display for ConnectionEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionEvent::Packet(ref p) => write!(f, "{}", p),
            &ConnectionEvent::InvalidPacket(ref p) => write!(f, "Invalid! {}", p),
            &ConnectionEvent::ReadError(ref err) => write!(f, "READ ERROR! [{:?}]", err),
            &ConnectionEvent::WriteError(ref err) => write!(f, "WRITE ERROR! [{:?}]", err),
            &ConnectionEvent::Heartbeat => write!(f, "Tick Tock")
        }
    }
}

pub struct Connection {
    pub tty_path: String,
    event_receiver: Receiver<ConnectionEvent>,
    packet_sender: Sender<Packet>,
    kill_thread_signal: Arc<Mutex<bool>>,
    reader_thread_handle: Option<ThreadHandle<()>>,
}

impl Connection {
    pub fn new(tty_path: &str, heartbeat_milliseconds: Option<u64>) -> io::Result<Connection> {
        let (event_sender, event_receiver) = channel::<ConnectionEvent>();
        let (packet_sender, packet_receiver) = channel::<Packet>();

        let kill_signal = Arc::new(Mutex::new(false));

        let reader_thread = build_connection_read_thread(tty_path.to_string(), event_sender, packet_receiver, heartbeat_milliseconds, kill_signal.clone());

        Ok(Connection {
            tty_path: tty_path.to_string(),
            event_receiver: event_receiver,
            packet_sender: packet_sender,
            kill_thread_signal: kill_signal,
            reader_thread_handle: Some(reader_thread)
        })
    }

    pub fn send(&mut self, p: Packet) -> io::Result<()> {
        match self.is_ok() {
            true => {
                self.packet_sender.send(p).unwrap();
                Ok(())
            },
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped"))
        }
    }

    pub fn wait(&self) -> io::Result<ConnectionEvent> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                Ok(self.event_receiver.recv().unwrap())
            }
        }
    }

    pub fn get_events(&self) -> io::Result<Vec<ConnectionEvent>> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                let mut data: Vec<ConnectionEvent> = vec!();

                loop {
                    match self.event_receiver.try_recv() {
                        Ok(evt) => { data.push(evt) },
                        Err(TryRecvError::Empty) => { break; },
                        Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
                    }
                }

                Ok(data)
            }
        }
    }

    pub fn is_ok(&self) -> bool {
        let mut ret = true;

        match &self.reader_thread_handle {
            &Some(ref h) => {
                ret = ret && match h.status.lock().unwrap().deref() {
                    &ThreadStatus::Ok => true,
                    _ => false
                }
            },
            _ => {}
        }
        ret
    }
}

impl Drop for Connection {
    fn drop(&mut self) {

        {
            *self.kill_thread_signal.lock().unwrap() = true;
        }

        match self.reader_thread_handle.take() {
            Some(th) => {
                th.handle.join().unwrap();
            },
            None => {}
        }
    }
}

fn build_connection_read_thread(tty_path: String, event_sender: Sender<ConnectionEvent>, packet_receiver: Receiver<Packet>, heartbeat: Option<u64>, kill_signal: Arc<Mutex<bool>>) -> ThreadHandle<()> {
    guard_thread("reader_thread", move || {
        let mut serial = match serial::open(&tty_path) {
            Err(e) => {panic!(format!("Unable to create serial port: {}", e)) },
            Ok(s) => s
        };

        match serial.set_timeout(Duration::from_millis(2000)) {
            Err(e) => { panic!(format!("Unable to set serial timeout: {}", e)) },
            Ok(_) => {}
        };

        let mut packet_buffer: Vec<u8> = Vec::new();
        let mut read_buffer: Vec<u8> = Vec::new();
        let mut last_read = Instant::now();
        let mut last_heartbeat = Instant::now();

        while !*kill_signal.lock().unwrap() {

            if packet_buffer.len() > 0 && last_read.elapsed().as_secs() > 2 {
                let evt = ConnectionEvent::ReadError(io::Error::new(io::ErrorKind::InvalidData, format!("Invalid packet data: {:?}", packet_buffer.clone())));
                event_sender.send(evt).unwrap();
                packet_buffer.clear();
            };

            match packet_receiver.try_recv() {
                Ok(p) => {
                    println!("going to write...");
                    match serial.write_all(&p.data) {
                        Ok(_) => {},
                        Err(e) => {
                            let evt = ConnectionEvent::WriteError(e);
                            event_sender.send(evt).unwrap();
                        }
                    }
                },
                Err(TryRecvError::Empty) => {  },
                Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
            }

            match serial.read(&mut read_buffer) {
                Err(e) => {
                    let evt = ConnectionEvent::ReadError(e);
                    event_sender.send(evt).unwrap();
                },
                Ok(bytes) if bytes > 0 => {
                    last_read = Instant::now();
                    for x in 0 .. bytes {
                        packet_buffer.push(read_buffer[x]);
                    }
                },
                Ok(_) => {} // do nothing for 0 bytes read
            };

            while packet_buffer.len() >= 128 {
                let data: Vec<u8> = packet_buffer.drain(0..128).collect();
                let p = Packet::from_bytes(&data);

                if p.is_checksum_valid() {
                    let evt = ConnectionEvent::Packet(p);
                    event_sender.send(evt).unwrap();
                } else {
                    let evt = ConnectionEvent::InvalidPacket(p);
                    event_sender.send(evt).unwrap();
                }
            }

            match heartbeat {
                Some(ms) => {
                    if last_heartbeat.elapsed() >= Duration::from_millis(ms) {
                        let evt = ConnectionEvent::Heartbeat;
                        event_sender.send(evt).unwrap();
                        last_heartbeat = Instant::now();
                    }
                },
                None => {}
            }

            thread::sleep(Duration::from_millis(250));
        }
        ()
    })
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;

use crate::error::*;
use crate::path;
use crate::value::{Value, ValueKind};

/// Describes a generic _source_ of configuration properties.
pub trait Source: Debug {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>;

    /// Collect all configuration properties available from this source and return
    /// a HashMap.
    fn collect(&self) -> Result<HashMap<String, Value>>;

    fn collect_to(&self, cache: &mut Value) -> Result<()> {
        self.collect()?
            .iter()
            .for_each(|(key, val)| set_value(cache, key, val));

        Ok(())
    }
}

fn set_value(cache: &mut Value, key: &String, value: &Value) {
    match path::Expression::from_str(key) {
        // Set using the path
        Ok(expr) => expr.set(cache, value.clone()),

        // Set diretly anyway
        _ => path::Expression::Identifier(key.clone()).set(cache, value.clone()),
    }
}

impl Clone for Box<dyn Source + Send + Sync> {
    fn clone(&self) -> Box<dyn Source + Send + Sync> {
        self.clone_into_box()
    }
}

impl Source for Vec<Box<dyn Source + Send + Sync>> {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl Source for [Box<dyn Source + Send + Sync>] {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new(self.to_owned())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl<T> Source for Vec<T>
where
    T: Source + Sync + Send,
    T: Clone,
    T: 'static,
{
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}
<commit_msg>Add doc to fn<commit_after>use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;

use crate::error::*;
use crate::path;
use crate::value::{Value, ValueKind};

/// Describes a generic _source_ of configuration properties.
pub trait Source: Debug {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>;

    /// Collect all configuration properties available from this source and return
    /// a HashMap.
    fn collect(&self) -> Result<HashMap<String, Value>>;

    /// Collects all configuration properties to a provided cache.
    fn collect_to(&self, cache: &mut Value) -> Result<()> {
        self.collect()?
            .iter()
            .for_each(|(key, val)| set_value(cache, key, val));

        Ok(())
    }
}

fn set_value(cache: &mut Value, key: &String, value: &Value) {
    match path::Expression::from_str(key) {
        // Set using the path
        Ok(expr) => expr.set(cache, value.clone()),

        // Set diretly anyway
        _ => path::Expression::Identifier(key.clone()).set(cache, value.clone()),
    }
}

impl Clone for Box<dyn Source + Send + Sync> {
    fn clone(&self) -> Box<dyn Source + Send + Sync> {
        self.clone_into_box()
    }
}

impl Source for Vec<Box<dyn Source + Send + Sync>> {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl Source for [Box<dyn Source + Send + Sync>] {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new(self.to_owned())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}

impl<T> Source for Vec<T>
where
    T: Source + Sync + Send,
    T: Clone,
    T: 'static,
{
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut cache: Value = HashMap::<String, Value>::new().into();

        for source in self {
            source.collect_to(&mut cache)?;
        }

        if let ValueKind::Table(table) = cache.kind {
            Ok(table)
        } else {
            unreachable!();
        }
    }
}
<|endoftext|>