<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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.

use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{Command, ExitStatus, Stdio};
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use hab_core::channel::{BLDR_CHANNEL_ENVVAR, STABLE_CHANNEL};
use hab_core::env;
use hab_core::fs;
use hab_core::url::BLDR_URL_ENVVAR;
use hab_core::AUTH_TOKEN_ENVVAR;

use error::Result;
use runner::log_pipe::LogPipe;
use runner::{NONINTERACTIVE_ENVVAR, RUNNER_DEBUG_ENVVAR};
use runner::workspace::Workspace;

pub static STUDIO_UID: AtomicUsize = ATOMIC_USIZE_INIT;
pub static STUDIO_GID: AtomicUsize = ATOMIC_USIZE_INIT;
pub const STUDIO_USER: &'static str = "krangschnak";
pub const STUDIO_GROUP: &'static str = "krangschnak";

lazy_static! {
    /// Absolute path to the Studio program
    static ref STUDIO_PROGRAM: PathBuf = fs::resolve_cmd_in_pkg(
        "hab-studio",
        include_str!(concat!(env!("OUT_DIR"), "/STUDIO_PKG_IDENT")),
    );

    pub static ref STUDIO_HOME: Mutex<PathBuf> = {
        Mutex::new(PathBuf::new())
    };
}

pub struct Studio<'a> {
    workspace: &'a Workspace,
    bldr_url: &'a str,
    auth_token: &'a str,
}

impl<'a> Studio<'a> {
    /// Creates a new Studio runner for a given `Workspace` and Builder URL.
    pub fn new(workspace: &'a Workspace, bldr_url: &'a str, auth_token: &'a str) -> Self {
        Studio {
            workspace,
            bldr_url,
            auth_token,
        }
    }

    /// Spawns a Studio build command, pipes output streams to the given `LogPipe` and returns the
    /// process' `ExitStatus`.
    ///
    /// # Errors
    ///
    /// * If the child process can't be spawned
    /// * If the calling thread can't wait on the child process
    /// * If the `LogPipe` fails to pipe output
    pub fn build(&self, log_pipe: &mut LogPipe) -> Result<ExitStatus> {
        let channel = if self.workspace.job.has_channel() {
            self.workspace.job.get_channel()
        } else {
            STABLE_CHANNEL
        };

        let mut cmd = self.studio_command();
        cmd.arg("-k"); // Origin key
        cmd.arg(self.workspace.job.origin());
        cmd.arg("build");
        cmd.arg(build_path(self.workspace.job.get_project().get_plan_path()));
        debug!("building studio build command, cmd={:?}", &cmd);
        debug!(
            "setting studio build command env, {}={}",
            BLDR_CHANNEL_ENVVAR,
            &channel
        );
        cmd.env(BLDR_CHANNEL_ENVVAR, channel);
        debug!(
            "setting studio build command env, {}={}",
            BLDR_URL_ENVVAR,
            self.bldr_url
        );
        cmd.env(BLDR_URL_ENVVAR, self.bldr_url);
        cmd.env(AUTH_TOKEN_ENVVAR, self.auth_token);

        debug!("spawning studio build command");
        let mut child = cmd.spawn()?;
        log_pipe.pipe(&mut child)?;
        let exit_status = child.wait()?;
        debug!("completed studio build command, status={:?}", exit_status);
        Ok(exit_status)
    }

    /// Spawns a Studio rm command and returns the process' `ExitStatus`.
    ///
    /// # Errors
    ///
    /// * If the child process can't be spawned
    /// * If the calling thread can't wait on the child process
    pub fn rm(&self) -> Result<ExitStatus> {
        let mut cmd = self.studio_command();
        cmd.arg("rm");
        debug!("building studio rm command, cmd={:?}", &cmd);

        debug!("spawning studio rm command");
        let mut child = cmd.spawn()?;
        let exit_status = child.wait()?;
        debug!("completed studio rm command, status={:?}", exit_status);
        Ok(exit_status)
    }

    /// Builds and returns a `Command` for spawning a Habitat Studio process.
    fn studio_command(&self) -> Command {
        let mut cmd = Command::new("airlock");
        cmd.uid(studio_uid());
        cmd.gid(studio_gid());
        cmd.env_clear();
        if let Some(val) = env::var_os(RUNNER_DEBUG_ENVVAR) {
            cmd.env("DEBUG", val);
        }
        cmd.env(NONINTERACTIVE_ENVVAR, "true"); // Disables progress bars
        cmd.env("TERM", "xterm-256color"); // Emits ANSI color codes
        cmd.arg("run");
        cmd.arg(&*STUDIO_PROGRAM);
        cmd.arg("-s"); // Source path
        cmd.arg(self.workspace.src());
        cmd.arg("-r"); // Studio root
        cmd.arg(self.workspace.studio());
        cmd.stdout(Stdio::piped());
        cmd.stderr(Stdio::piped());
        cmd
    }
}

pub fn studio_gid() -> u32 {
    STUDIO_GID.load(Ordering::Relaxed) as u32
}

pub fn studio_uid() -> u32 {
    STUDIO_UID.load(Ordering::Relaxed) as u32
}

pub fn set_studio_gid(gid: u32) {
    STUDIO_GID.store(gid as usize, Ordering::Relaxed);
}

pub fn set_studio_uid(uid: u32) {
    STUDIO_UID.store(uid as usize, Ordering::Relaxed);
}

/// Returns a path argument suitable to pass to a Studio build command.
fn build_path(plan_path: &str) -> String {
    let mut parts: Vec<_> = plan_path.split("/").collect();
    if parts.last().map_or("", |p| *p) == "plan.sh" {
        parts.pop();
    }
    if parts.last().map_or("", |p| *p) == "habitat" {
        parts.pop();
    }

    if parts.is_empty() {
        String::from(".")
    } else {
        parts.join("/")
    }
}

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

    #[test]
    fn build_path_with_plan_sh() {
        assert_eq!(".", build_path("plan.sh"));
    }

    #[test]
    fn build_path_with_habitat_plan_sh() {
        assert_eq!(".", build_path("habitat/plan.sh"));
    }

    #[test]
    fn build_path_with_subdir_plan_sh() {
        assert_eq!("haaay", build_path("haaay/plan.sh"));
    }

    #[test]
    fn build_path_with_subdir_habitat_plan_sh() {
        assert_eq!(
            "components/yep",
            build_path("components/yep/habitat/plan.sh")
        );
    }
}
<commit_msg>Keep environment when executing Studio from within worker<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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.

use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{Command, ExitStatus, Stdio};
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use hab_core::channel::{BLDR_CHANNEL_ENVVAR, STABLE_CHANNEL};
use hab_core::env;
use hab_core::fs;
use hab_core::url::BLDR_URL_ENVVAR;
use hab_core::AUTH_TOKEN_ENVVAR;

use error::Result;
use runner::log_pipe::LogPipe;
use runner::{NONINTERACTIVE_ENVVAR, RUNNER_DEBUG_ENVVAR};
use runner::workspace::Workspace;

pub static STUDIO_UID: AtomicUsize = ATOMIC_USIZE_INIT;
pub static STUDIO_GID: AtomicUsize = ATOMIC_USIZE_INIT;
pub const STUDIO_USER: &'static str = "krangschnak";
pub const STUDIO_GROUP: &'static str = "krangschnak";

lazy_static! {
    /// Absolute path to the Studio program
    static ref STUDIO_PROGRAM: PathBuf = fs::resolve_cmd_in_pkg(
        "hab-studio",
        include_str!(concat!(env!("OUT_DIR"), "/STUDIO_PKG_IDENT")),
    );

    pub static ref STUDIO_HOME: Mutex<PathBuf> = {
        Mutex::new(PathBuf::new())
    };
}

pub struct Studio<'a> {
    workspace: &'a Workspace,
    bldr_url: &'a str,
    auth_token: &'a str,
}

impl<'a> Studio<'a> {
    /// Creates a new Studio runner for a given `Workspace` and Builder URL.
    pub fn new(workspace: &'a Workspace, bldr_url: &'a str, auth_token: &'a str) -> Self {
        Studio {
            workspace,
            bldr_url,
            auth_token,
        }
    }

    /// Spawns a Studio build command, pipes output streams to the given `LogPipe` and returns the
    /// process' `ExitStatus`.
    ///
    /// # Errors
    ///
    /// * If the child process can't be spawned
    /// * If the calling thread can't wait on the child process
    /// * If the `LogPipe` fails to pipe output
    pub fn build(&self, log_pipe: &mut LogPipe) -> Result<ExitStatus> {
        let channel = if self.workspace.job.has_channel() {
            self.workspace.job.get_channel()
        } else {
            STABLE_CHANNEL
        };

        let mut cmd = self.studio_command();
        cmd.arg("-k"); // Origin key
        cmd.arg(self.workspace.job.origin());
        cmd.arg("build");
        cmd.arg(build_path(self.workspace.job.get_project().get_plan_path()));
        debug!("building studio build command, cmd={:?}", &cmd);
        debug!(
            "setting studio build command env, {}={}",
            BLDR_CHANNEL_ENVVAR,
            &channel
        );
        cmd.env(BLDR_CHANNEL_ENVVAR, channel);
        debug!(
            "setting studio build command env, {}={}",
            BLDR_URL_ENVVAR,
            self.bldr_url
        );
        cmd.env(BLDR_URL_ENVVAR, self.bldr_url);
        cmd.env(AUTH_TOKEN_ENVVAR, self.auth_token);

        debug!("spawning studio build command");
        let mut child = cmd.spawn()?;
        log_pipe.pipe(&mut child)?;
        let exit_status = child.wait()?;
        debug!("completed studio build command, status={:?}", exit_status);
        Ok(exit_status)
    }

    /// Spawns a Studio rm command and returns the process' `ExitStatus`.
    ///
    /// # Errors
    ///
    /// * If the child process can't be spawned
    /// * If the calling thread can't wait on the child process
    pub fn rm(&self) -> Result<ExitStatus> {
        let mut cmd = self.studio_command();
        cmd.arg("rm");
        debug!("building studio rm command, cmd={:?}", &cmd);

        debug!("spawning studio rm command");
        let mut child = cmd.spawn()?;
        let exit_status = child.wait()?;
        debug!("completed studio rm command, status={:?}", exit_status);
        Ok(exit_status)
    }

    /// Builds and returns a `Command` for spawning a Habitat Studio process.
    fn studio_command(&self) -> Command {
        let mut cmd = Command::new("airlock");
        cmd.uid(studio_uid());
        cmd.gid(studio_gid());
        if let Some(val) = env::var_os(RUNNER_DEBUG_ENVVAR) {
            cmd.env("DEBUG", val);
        }
        cmd.env(NONINTERACTIVE_ENVVAR, "true"); // Disables progress bars
        cmd.env("TERM", "xterm-256color"); // Emits ANSI color codes
        cmd.arg("run");
        cmd.arg(&*STUDIO_PROGRAM);
        cmd.arg("-s"); // Source path
        cmd.arg(self.workspace.src());
        cmd.arg("-r"); // Studio root
        cmd.arg(self.workspace.studio());
        cmd.stdout(Stdio::piped());
        cmd.stderr(Stdio::piped());
        cmd
    }
}

pub fn studio_gid() -> u32 {
    STUDIO_GID.load(Ordering::Relaxed) as u32
}

pub fn studio_uid() -> u32 {
    STUDIO_UID.load(Ordering::Relaxed) as u32
}

pub fn set_studio_gid(gid: u32) {
    STUDIO_GID.store(gid as usize, Ordering::Relaxed);
}

pub fn set_studio_uid(uid: u32) {
    STUDIO_UID.store(uid as usize, Ordering::Relaxed);
}

/// Returns a path argument suitable to pass to a Studio build command.
fn build_path(plan_path: &str) -> String {
    let mut parts: Vec<_> = plan_path.split("/").collect();
    if parts.last().map_or("", |p| *p) == "plan.sh" {
        parts.pop();
    }
    if parts.last().map_or("", |p| *p) == "habitat" {
        parts.pop();
    }

    if parts.is_empty() {
        String::from(".")
    } else {
        parts.join("/")
    }
}

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

    #[test]
    fn build_path_with_plan_sh() {
        assert_eq!(".", build_path("plan.sh"));
    }

    #[test]
    fn build_path_with_habitat_plan_sh() {
        assert_eq!(".", build_path("habitat/plan.sh"));
    }

    #[test]
    fn build_path_with_subdir_plan_sh() {
        assert_eq!("haaay", build_path("haaay/plan.sh"));
    }

    #[test]
    fn build_path_with_subdir_habitat_plan_sh() {
        assert_eq!(
            "components/yep",
            build_path("components/yep/habitat/plan.sh")
        );
    }
}
<|endoftext|>


<commit_before>use serde_json::{Value, from_str};
use serde_json::error::Result as R;
use serde_json::Serializer;
use serde::ser::Serialize;
use serde::ser::Serializer as Ser;

use std::collections::HashMap;
use std::io::stdout;
use std::error::Error;

use super::super::parser::{FileHeaderParser, ParserError};
use super::super::file::{FileHeaderSpec, FileHeaderData};

pub struct JsonHeaderParser {
    spec: Option<FileHeaderSpec>,
}

impl JsonHeaderParser {

    pub fn new(spec: Option<FileHeaderSpec>) -> JsonHeaderParser {
        JsonHeaderParser {
            spec: spec
        }
    }

}

impl FileHeaderParser for JsonHeaderParser {

    fn read(&self, string: Option<String>)
        -> Result<FileHeaderData, ParserError>
    {
        if string.is_some() {
            let s = string.unwrap();
            debug!("Deserializing: {}", s);
            let fromstr : R<Value> = from_str(&s[..]);
            if let Ok(ref content) = fromstr {
                return Ok(visit_json(&content))
            }
            let oe = fromstr.err().unwrap();
            let s = format!("JSON parser error: {}", oe.description());
            let e = ParserError::short(&s[..], s.clone(), 0);
            Err(e)
        } else {
            Ok(FileHeaderData::Null)
        }
    }

    fn write(&self, data: &FileHeaderData) -> Result<String, ParserError> {
        let mut s = Vec::<u8>::new();
        {
            let mut ser = Serializer::pretty(&mut s);
            data.serialize(&mut ser);
        }

        String::from_utf8(s).or(
            Err(ParserError::short("Cannot parse utf8 bytes",
                                   String::from("<not printable>"),
                                   0)))
    }

}

// TODO: This function must be able to return a parser error
fn visit_json(v: &Value) -> FileHeaderData {
    match v {
        &Value::Null             => FileHeaderData::Null,
        &Value::Bool(b)          => FileHeaderData::Bool(b),
        &Value::I64(i)           => FileHeaderData::Integer(i),
        &Value::U64(u)           => FileHeaderData::UInteger(u),
        &Value::F64(f)           => FileHeaderData::Float(f),
        &Value::String(ref s)        => FileHeaderData::Text(s.clone()),
        &Value::Array(ref vec)       => {
            FileHeaderData::Array {
                values: Box::new(vec.clone().into_iter().map(|i| visit_json(&i)).collect())
            }
        },
        &Value::Object(ref btree)    => {
            let btree = btree.clone();
            FileHeaderData::Map{
                keys: btree.into_iter().map(|(k, v)|
                    FileHeaderData::Key {
                        name: k,
                        value: Box::new(visit_json(&v)),
                    }
                ).collect()
            }
        }
    }
}

impl Serialize for FileHeaderData {

    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Ser
    {
        match self {
            &FileHeaderData::Null               => {
                let o : Option<bool> = None;
                o.serialize(ser)
            },
            &FileHeaderData::Bool(ref b)            => b.serialize(ser),
            &FileHeaderData::Integer(ref i)         => i.serialize(ser),
            &FileHeaderData::UInteger(ref u)        => u.serialize(ser),
            &FileHeaderData::Float(ref f)           => f.serialize(ser),
            &FileHeaderData::Text(ref s)            => (&s[..]).serialize(ser),
            &FileHeaderData::Array{values: ref vs}  => vs.serialize(ser),
            &FileHeaderData::Map{keys: ref ks}      => {
                let mut hm = HashMap::new();

                for key in ks {
                    if let &FileHeaderData::Key{name: ref n, value: ref v} = key {
                        hm.insert(n, v);
                    } else {
                        panic!("Not a key: {:?}", key);
                    }
                }

                hm.serialize(ser)
            },
            &FileHeaderData::Key{name: ref n, value: ref v} => unreachable!(),

        }
    }

}

#[cfg(test)]
mod test {

    use super::JsonHeaderParser;
    use storage::parser::{FileHeaderParser, ParserError};
    use storage::file::FileHeaderData as FHD;
    use storage::file::FileHeaderSpec as FHS;

    #[test]
    fn test_deserialization() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser = JsonHeaderParser::new(Some(spec));
        let parsed = parser.read(Some(text));
        assert!(parsed.is_ok(), "Parsed is not ok: {:?}", parsed);

        match parsed.ok() {
            Some(FHD::Map{keys: keys}) => {
                for k in keys {
                    match k {
                        FHD::Key{name: name, value: box value} => {
                            assert!(name == "a" || name == "b", "Key unknown");
                            match &value {
                                &FHD::UInteger(u) => assert_eq!(u, 1),
                                &FHD::Integer(i) => assert_eq!(i, -2),
                                _ => assert!(false, "Integers are not here"),
                            }
                        },
                        _ => assert!(false, "Key is not a Key"),
                    }
                }
            },

            _ => assert!(false, "Parsed is not a map"),
        }
    }

    #[test]
    fn test_deserialization_without_spec() {
        let text    = String::from("{\"a\": [1], \"b\": {\"c\": -2}}");
        let parser  = JsonHeaderParser::new(None);
        let parsed  = parser.read(Some(text));

        assert!(parsed.is_ok(), "Parsed is not ok: {:?}", parsed);

        match parsed.ok() {
            Some(FHD::Map{keys: keys}) => {
                for k in keys {
                    match_key(&k);
                }
            },

            _ => assert!(false, "Parsed is not a map"),
        }
    }

    fn match_key(k: &FHD) {
        use std::ops::Deref;

        match k {
            &FHD::Key{name: ref name, value: ref value} => {
                assert!(name == "a" || name == "b", "Key unknown");
                match value.deref() {
                    &FHD::Array{values: ref vs} => {
                        for value in vs.iter() {
                            match value {
                                &FHD::UInteger(u) => assert_eq!(u, 1),
                                _ => assert!(false, "UInt is not an UInt"),
                            }
                        }
                    }

                    &FHD::Map{keys: ref ks} => {
                        for key in ks.iter() {
                            match key {
                                &FHD::Key{name: ref name, value: ref value} => {
                                    match value.deref() {
                                        &FHD::Integer(i) => {
                                            assert_eq!(i, -2);
                                            assert_eq!(name, "c");
                                        },
                                        _ => assert!(false, "Int is not an Int"),
                                    };
                                },
                                _ => assert!(false, "Key is not a Key"),
                            }
                        }
                    }
                    _ => assert!(false, "Integers are not here"),
                }
            },
            _ => assert!(false, "Key in main Map is not a Key"),
        }
    }

}
<commit_msg>Add test: serialize-deserialize-compare<commit_after>use serde_json::{Value, from_str};
use serde_json::error::Result as R;
use serde_json::Serializer;
use serde::ser::Serialize;
use serde::ser::Serializer as Ser;

use std::collections::HashMap;
use std::io::stdout;
use std::error::Error;

use super::super::parser::{FileHeaderParser, ParserError};
use super::super::file::{FileHeaderSpec, FileHeaderData};

pub struct JsonHeaderParser {
    spec: Option<FileHeaderSpec>,
}

impl JsonHeaderParser {

    pub fn new(spec: Option<FileHeaderSpec>) -> JsonHeaderParser {
        JsonHeaderParser {
            spec: spec
        }
    }

}

impl FileHeaderParser for JsonHeaderParser {

    fn read(&self, string: Option<String>)
        -> Result<FileHeaderData, ParserError>
    {
        if string.is_some() {
            let s = string.unwrap();
            debug!("Deserializing: {}", s);
            let fromstr : R<Value> = from_str(&s[..]);
            if let Ok(ref content) = fromstr {
                return Ok(visit_json(&content))
            }
            let oe = fromstr.err().unwrap();
            let s = format!("JSON parser error: {}", oe.description());
            let e = ParserError::short(&s[..], s.clone(), 0);
            Err(e)
        } else {
            Ok(FileHeaderData::Null)
        }
    }

    fn write(&self, data: &FileHeaderData) -> Result<String, ParserError> {
        let mut s = Vec::<u8>::new();
        {
            let mut ser = Serializer::pretty(&mut s);
            data.serialize(&mut ser);
        }

        String::from_utf8(s).or(
            Err(ParserError::short("Cannot parse utf8 bytes",
                                   String::from("<not printable>"),
                                   0)))
    }

}

// TODO: This function must be able to return a parser error
fn visit_json(v: &Value) -> FileHeaderData {
    match v {
        &Value::Null             => FileHeaderData::Null,
        &Value::Bool(b)          => FileHeaderData::Bool(b),
        &Value::I64(i)           => FileHeaderData::Integer(i),
        &Value::U64(u)           => FileHeaderData::UInteger(u),
        &Value::F64(f)           => FileHeaderData::Float(f),
        &Value::String(ref s)        => FileHeaderData::Text(s.clone()),
        &Value::Array(ref vec)       => {
            FileHeaderData::Array {
                values: Box::new(vec.clone().into_iter().map(|i| visit_json(&i)).collect())
            }
        },
        &Value::Object(ref btree)    => {
            let btree = btree.clone();
            FileHeaderData::Map{
                keys: btree.into_iter().map(|(k, v)|
                    FileHeaderData::Key {
                        name: k,
                        value: Box::new(visit_json(&v)),
                    }
                ).collect()
            }
        }
    }
}

impl Serialize for FileHeaderData {

    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Ser
    {
        match self {
            &FileHeaderData::Null               => {
                let o : Option<bool> = None;
                o.serialize(ser)
            },
            &FileHeaderData::Bool(ref b)            => b.serialize(ser),
            &FileHeaderData::Integer(ref i)         => i.serialize(ser),
            &FileHeaderData::UInteger(ref u)        => u.serialize(ser),
            &FileHeaderData::Float(ref f)           => f.serialize(ser),
            &FileHeaderData::Text(ref s)            => (&s[..]).serialize(ser),
            &FileHeaderData::Array{values: ref vs}  => vs.serialize(ser),
            &FileHeaderData::Map{keys: ref ks}      => {
                let mut hm = HashMap::new();

                for key in ks {
                    if let &FileHeaderData::Key{name: ref n, value: ref v} = key {
                        hm.insert(n, v);
                    } else {
                        panic!("Not a key: {:?}", key);
                    }
                }

                hm.serialize(ser)
            },
            &FileHeaderData::Key{name: ref n, value: ref v} => unreachable!(),

        }
    }

}

#[cfg(test)]
mod test {

    use super::JsonHeaderParser;
    use storage::parser::{FileHeaderParser, ParserError};
    use storage::file::FileHeaderData as FHD;
    use storage::file::FileHeaderSpec as FHS;

    #[test]
    fn test_deserialization() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser = JsonHeaderParser::new(Some(spec));
        let parsed = parser.read(Some(text));
        assert!(parsed.is_ok(), "Parsed is not ok: {:?}", parsed);

        match parsed.ok() {
            Some(FHD::Map{keys: keys}) => {
                for k in keys {
                    match k {
                        FHD::Key{name: name, value: box value} => {
                            assert!(name == "a" || name == "b", "Key unknown");
                            match &value {
                                &FHD::UInteger(u) => assert_eq!(u, 1),
                                &FHD::Integer(i) => assert_eq!(i, -2),
                                _ => assert!(false, "Integers are not here"),
                            }
                        },
                        _ => assert!(false, "Key is not a Key"),
                    }
                }
            },

            _ => assert!(false, "Parsed is not a map"),
        }
    }

    #[test]
    fn test_deserialization_without_spec() {
        let text    = String::from("{\"a\": [1], \"b\": {\"c\": -2}}");
        let parser  = JsonHeaderParser::new(None);
        let parsed  = parser.read(Some(text));

        assert!(parsed.is_ok(), "Parsed is not ok: {:?}", parsed);

        match parsed.ok() {
            Some(FHD::Map{keys: keys}) => {
                for k in keys {
                    match_key(&k);
                }
            },

            _ => assert!(false, "Parsed is not a map"),
        }
    }

    fn match_key(k: &FHD) {
        use std::ops::Deref;

        match k {
            &FHD::Key{name: ref name, value: ref value} => {
                assert!(name == "a" || name == "b", "Key unknown");
                match value.deref() {
                    &FHD::Array{values: ref vs} => {
                        for value in vs.iter() {
                            match value {
                                &FHD::UInteger(u) => assert_eq!(u, 1),
                                _ => assert!(false, "UInt is not an UInt"),
                            }
                        }
                    }

                    &FHD::Map{keys: ref ks} => {
                        for key in ks.iter() {
                            match key {
                                &FHD::Key{name: ref name, value: ref value} => {
                                    match value.deref() {
                                        &FHD::Integer(i) => {
                                            assert_eq!(i, -2);
                                            assert_eq!(name, "c");
                                        },
                                        _ => assert!(false, "Int is not an Int"),
                                    };
                                },
                                _ => assert!(false, "Key is not a Key"),
                            }
                        }
                    }
                    _ => assert!(false, "Integers are not here"),
                }
            },
            _ => assert!(false, "Key in main Map is not a Key"),
        }
    }

    #[test]
    fn test_desser() {
        use serde_json::error::Result as R;
        use serde_json::{Value, from_str};

        let text    = String::from("{\"a\": [1], \"b\": {\"c\": -2}}");
        let parser  = JsonHeaderParser::new(None);

        let des = parser.read(Some(text.clone()));
        assert!(des.is_ok(), "Deserializing failed");

        let ser = parser.write(&des.unwrap());
        assert!(ser.is_ok(), "Parser error when serializing deserialized text");

        let json_text : R<Value> = from_str(&text[..]);
        let json_ser  : R<Value> = from_str(&ser.unwrap()[..]);

        assert!(json_text.is_ok(), "Could not use serde to serialize text for comparison");
        assert!(json_ser.is_ok(),  "Could not use serde to serialize serialized-deserialized text for comparison");
        assert_eq!(json_text.unwrap(), json_ser.unwrap());
    }

}
<|endoftext|>


<commit_before>//! Provide syntax highlighting via [Syntect].
//!
//! [Syntect]: https://docs.rs/syntect/1.0.0/syntect/

// Standard library
use std::default::Default;
use std::collections::HashMap;
use std::str;

// Third party
use quick_xml::{XmlReader, XmlWriter, Element, Event};
use syntect::html::highlighted_snippet_for_string;
use syntect::highlighting::Theme;
use syntect::parsing::{SyntaxDefinition, SyntaxSet};

/// A `Language` is a `String` representing a code highlighting language.
type Language = String;

/// Define XML parsing states of interest for highlighting.
///
/// We use these in conjunction with `ParseEvent`s to drive a state machine
/// which tracks whether a stream of `quick_xml::Event`s indicates the presence
/// of a code block to be highlighted.
#[derive(Clone, Debug, PartialEq)]
enum ParseState {
    /// Not in a code block at all.
    NotInBlock,
    /// Possibly the start of a highlight block, with the language as payload.
    MaybeStartBlock(Language),
    /// The start of the code block has been confirmed; highlight next text node.
    WillStartCodeBlock(Language),
    /// In a code block; use the content for highlighting.
    InCodeBlock(Language),
}

/// Define XML parsing events of interest for highlighting.
///
/// We don't care at all about any other items for this specific scenario; we
/// use these in conjunction with the `ParseState` to drive the state  machine
/// described there.
#[derive(Debug)]
enum ParseEvent {
    // Starting a `<pre>`; optionally specifying a language.
    StartPre(Option<Language>),
    // Starting a `<code>`.
    StartCode,
    // Ending a `<code>`.
    EndCode,
    Text,
    Whitespace,
    Other,
}

impl ParseState {
    /// Get the next `ParseState` given current `ParseState` and a `ParseEvent`.
    fn next(self, event: ParseEvent) -> ParseState {
        use self::ParseState::*;
        use self::ParseEvent::*;

        match (self, event) {
            (NotInBlock, StartPre(Some(language))) => MaybeStartBlock(language),
            (MaybeStartBlock(language), Whitespace) => MaybeStartBlock(language),
            (MaybeStartBlock(language), StartCode) => WillStartCodeBlock(language),
            (WillStartCodeBlock(language), Text) |
            (WillStartCodeBlock(language), Whitespace) => InCodeBlock(language),
            (InCodeBlock(_), EndCode) => NotInBlock,
            (_, _) => NotInBlock,
        }
    }
}

impl Default for ParseState {
    fn default() -> ParseState {
        ParseState::NotInBlock
    }
}

impl<'e> From<&'e Event> for ParseEvent {
    fn from(event: &'e Event) -> ParseEvent {
        // TODO @1.15: remove `'static`.
        const PRE: &'static [u8] = b"pre";
        const CODE: &'static [u8] = b"code";
        const CLASS: &'static [u8] = b"class";
        const WHITE_SPACE: &'static [u8] = b"";

        match *event {
            Event::Start(ref element) => {
                match element.name() {
                    PRE => {
                        let maybe_class_attr = element.attributes()
                            .map(|attr| attr.unwrap())
                            .filter(|&(attr, _value)| attr == CLASS)
                            .next();

                        if let Some((_attr, value)) = maybe_class_attr {
                            match str::from_utf8(value) {
                                Ok(lang) => ParseEvent::StartPre(Some(lang.to_string())),
                                Err(_) => ParseEvent::StartPre(None),
                            }
                        } else {
                            ParseEvent::StartPre(None)
                        }
                    }
                    CODE => ParseEvent::StartCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::End(ref element) => {
                match element.name() {
                    CODE => ParseEvent::EndCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::Text(ref element) => {
                match element.name() {
                    WHITE_SPACE => ParseEvent::Whitespace,
                    _ => ParseEvent::Text,
                }
            }
            _ => ParseEvent::Other,
        }
    }
}

/// Highlight all code blocks in a block of HTML.
///
/// Assumes that the blocks to be highlighted are in the following basic format:
///
/// ```html
/// <pre class="rust">
///   <code>
///     pub fn syntax_highlight(html_string: String) -> String {
///         // implementation details
///     }
///   </code>
/// </pre>
/// ```
///
/// The `class` attribute value from the `pre` tag defines the language used to
/// highlight the code.
///
/// Note that any `html_string` will do; if it cannot be parsed as XML, it will
/// simply be returned unchanged; and if there are no code blocks to highlight,
/// it will also be returned unchanged.
pub fn syntax_highlight(html_string: String, theme: &Theme) -> String {
    let ss = SyntaxSet::load_defaults_nonewlines();
    let mut syntax_definitions = HashMap::<Language, &SyntaxDefinition>::new();

    let mut writer = XmlWriter::new(Vec::<u8>::new());
    let mut state = ParseState::default();

    for event in XmlReader::from(html_string.as_str()) {
        let event = match event {
            Ok(event) => event,
            Err(_) => continue
        };

        let parse_event = ParseEvent::from(&event);
        state = ParseState::next(state, parse_event);

        if let ParseState::InCodeBlock(ref language) = state {
            if let Ok(unescaped_content) = event.element().unescaped_content() {
                if let Ok(content_to_highlight) = str::from_utf8(&unescaped_content) {
                    if let Some(valid_syntax) = ss.find_syntax_by_token(&language) {
                        let syntax_definition =
                            syntax_definitions.entry(language.clone()).or_insert(valid_syntax);

                        let highlighted = highlighted_snippet_for_string(content_to_highlight,
                                                                         syntax_definition,
                                                                         &theme);
                        let text = Element::new(highlighted);
                        assert!(writer.write(Event::Text(text)).is_ok());
                        continue;
                    }
                }
            }
        }

        // Syntax highlighting did not succeed, so just write the original event.
        assert!(writer.write(event).is_ok());
    }

    String::from_utf8(writer.into_inner()).unwrap_or(html_string)
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_parse_state() {
        use super::ParseState;
        use super::ParseEvent;

        let lang = "rust";

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::MaybeStartBlock(lang.to_string()));

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::StartCode),
                   ParseState::WillStartCodeBlock(lang.to_string()));

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string())
                       .next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::WillStartCodeBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::InCodeBlock(lang.to_string()));
    }
}
<commit_msg>Small formatting tweaks to `syntax_highlighting`.<commit_after>//! Provide syntax highlighting via [Syntect].
//!
//! [Syntect]: https://docs.rs/syntect/1.0.0/syntect/

// Standard library
use std::default::Default;
use std::collections::HashMap;
use std::str;

// Third party
use quick_xml::{XmlReader, XmlWriter, Element, Event};
use syntect::html::highlighted_snippet_for_string;
use syntect::highlighting::Theme;
use syntect::parsing::{SyntaxDefinition, SyntaxSet};

/// A `Language` is a `String` representing a code highlighting language.
type Language = String;

/// Define XML parsing states of interest for highlighting.
///
/// We use these in conjunction with `ParseEvent`s to drive a state machine
/// which tracks whether a stream of `quick_xml::Event`s indicates the presence
/// of a code block to be highlighted.
#[derive(Clone, Debug, PartialEq)]
enum ParseState {
    /// Not in a code block at all.
    NotInBlock,
    /// Possibly the start of a highlight block, with the language as payload.
    MaybeStartBlock(Language),
    /// The start of the code block has been confirmed; highlight next text node.
    WillStartCodeBlock(Language),
    /// In a code block; use the content for highlighting.
    InCodeBlock(Language),
}

/// Define XML parsing events of interest for highlighting.
///
/// We don't care at all about any other items for this specific scenario; we
/// use these in conjunction with the `ParseState` to drive the state  machine
/// described there.
#[derive(Debug)]
enum ParseEvent {
    // Starting a `<pre>`; optionally specifying a language.
    StartPre(Option<Language>),
    // Starting a `<code>`.
    StartCode,
    // Ending a `<code>`.
    EndCode,
    Text,
    Whitespace,
    Other,
}

impl ParseState {
    /// Get the next `ParseState` given current `ParseState` and a `ParseEvent`.
    fn next(self, event: ParseEvent) -> ParseState {
        use self::ParseState::*;
        use self::ParseEvent::*;

        match (self, event) {
            (NotInBlock, StartPre(Some(language))) => MaybeStartBlock(language),
            (MaybeStartBlock(language), Whitespace) => MaybeStartBlock(language),
            (MaybeStartBlock(language), StartCode) => WillStartCodeBlock(language),
            (WillStartCodeBlock(language), Text) |
            (WillStartCodeBlock(language), Whitespace) => InCodeBlock(language),
            (InCodeBlock(_), EndCode) => NotInBlock,
            (_, _) => NotInBlock,
        }
    }
}

impl Default for ParseState {
    fn default() -> ParseState {
        ParseState::NotInBlock
    }
}

impl<'e> From<&'e Event> for ParseEvent {
    fn from(event: &'e Event) -> ParseEvent {
        // TODO @1.15: remove `'static`.
        const PRE: &'static [u8] = b"pre";
        const CODE: &'static [u8] = b"code";
        const CLASS: &'static [u8] = b"class";
        const WHITE_SPACE: &'static [u8] = b"";

        match *event {
            Event::Start(ref element) => {
                match element.name() {
                    PRE => {
                        let maybe_class_attr = element.attributes()
                            .map(|attr| attr.unwrap())
                            .filter(|&(attr, _value)| attr == CLASS)
                            .next();

                        if let Some((_attr, value)) = maybe_class_attr {
                            match str::from_utf8(value) {
                                Ok(lang) => ParseEvent::StartPre(Some(lang.into())),
                                Err(_) => ParseEvent::StartPre(None),
                            }
                        } else {
                            ParseEvent::StartPre(None)
                        }
                    }
                    CODE => ParseEvent::StartCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::End(ref element) => {
                match element.name() {
                    CODE => ParseEvent::EndCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::Text(ref element) => {
                match element.name() {
                    WHITE_SPACE => ParseEvent::Whitespace,
                    _ => ParseEvent::Text,
                }
            }
            _ => ParseEvent::Other,
        }
    }
}

/// Highlight all code blocks in a block of HTML.
///
/// Assumes that the blocks to be highlighted are in the following basic format:
///
/// ```html
/// <pre class="rust">
///   <code>
///     pub fn syntax_highlight(html_string: String) -> String {
///         // implementation details
///     }
///   </code>
/// </pre>
/// ```
///
/// The `class` attribute value from the `pre` tag defines the language used to
/// highlight the code.
///
/// Note that any `html_string` will do; if it cannot be parsed as XML, it will
/// simply be returned unchanged; and if there are no code blocks to highlight,
/// it will also be returned unchanged.
pub fn syntax_highlight(html_string: String, theme: &Theme) -> String {
    let ss = SyntaxSet::load_defaults_nonewlines();
    let mut syntax_definitions = HashMap::<Language, &SyntaxDefinition>::new();

    let mut writer = XmlWriter::new(Vec::<u8>::new());
    let mut state = ParseState::default();

    for event in XmlReader::from(html_string.as_str()) {
        let event = match event {
            Ok(event) => event,
            Err(_) => continue,
        };

        let parse_event = ParseEvent::from(&event);
        state = ParseState::next(state, parse_event);

        if let ParseState::InCodeBlock(ref language) = state {
            if let Ok(unescaped_content) = event.element().unescaped_content() {
                if let Ok(content_to_highlight) = str::from_utf8(&unescaped_content) {
                    if let Some(valid_syntax) = ss.find_syntax_by_token(&language) {
                        let syntax_definition = syntax_definitions.entry(language.clone())
                            .or_insert(valid_syntax);

                        let highlighted = highlighted_snippet_for_string(content_to_highlight,
                                                                         syntax_definition,
                                                                         &theme);
                        let text = Element::new(highlighted);
                        assert!(writer.write(Event::Text(text)).is_ok());
                        continue;
                    }
                }
            }
        }

        // Syntax highlighting did not succeed, so just write the original event.
        assert!(writer.write(event).is_ok());
    }

    String::from_utf8(writer.into_inner()).unwrap_or(html_string)
}

#[cfg(test)]
mod tests {

    #[test]
    fn parse_state() {
        use super::ParseState;
        use super::ParseEvent;

        let lang = "rust";

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartPre(Some(lang.into()))),
                   ParseState::MaybeStartBlock(lang.into()));

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.into()).next(ParseEvent::StartCode),
                   ParseState::WillStartCodeBlock(lang.into()));

        assert_eq!(ParseState::MaybeStartBlock(lang.into()).next(ParseEvent::Text),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.into()).next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.into())
                       .next(ParseEvent::StartPre(Some(lang.into()))),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.into()).next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::WillStartCodeBlock(lang.into()).next(ParseEvent::Text),
                   ParseState::InCodeBlock(lang.into()));
    }
}
<|endoftext|>


<commit_before>// See comments in `u8::to_ascii_uppercase` in `src/libcore/num/mod.rs`.
fn branchless_to_ascii_upper_case(byte: u8) -> u8 {
    byte &
    !(
        (
            byte.wrapping_add(0x1f) &
            !byte.wrapping_add(0x05) &
            0x80
        ) >> 2
    )
}

macro_rules! benches {
    ($( fn $name: ident($arg: ident: &mut [u8]) $body: block )+ @iter $( $is_: ident, )+) => {
        benches! {@
            $( fn $name($arg: &mut [u8]) $body )+
            $( fn $is_(bytes: &mut [u8]) { bytes.iter().all(u8::$is_) } )+
        }
    };

    (@$( fn $name: ident($arg: ident: &mut [u8]) $body: block )+) => {
        benches!(mod short SHORT $($name $arg $body)+);
        benches!(mod medium MEDIUM $($name $arg $body)+);
        benches!(mod long LONG $($name $arg $body)+);
    };

    (mod $mod_name: ident $input: ident $($name: ident $arg: ident $body: block)+) => {
        mod $mod_name {
            use super::*;

            $(
                #[bench]
                fn $name(bencher: &mut Bencher) {
                    bencher.bytes = $input.len() as u64;
                    bencher.iter(|| {
                        let mut vec = $input.as_bytes().to_vec();
                        {
                            let $arg = &mut vec[..];
                            black_box($body);
                        }
                        vec
                    })
                }
            )+
        }
    }
}

use test::black_box;
use test::Bencher;

benches! {
    fn case00_alloc_only(_bytes: &mut [u8]) {}

    fn case01_black_box_read_each_byte(bytes: &mut [u8]) {
        for byte in bytes {
            black_box(*byte);
        }
    }

    fn case02_lookup(bytes: &mut [u8]) {
        for byte in bytes {
            *byte = ASCII_UPPERCASE_MAP[*byte as usize]
        }
    }

    fn case03_branch_and_subtract(bytes: &mut [u8]) {
        for byte in bytes {
            *byte = if b'a' <= *byte && *byte <= b'z' {
                *byte - b'a' + b'A'
            } else {
                *byte
            }
        }
    }

    fn case04_branch_and_mask(bytes: &mut [u8]) {
        for byte in bytes {
            *byte = if b'a' <= *byte && *byte <= b'z' {
                *byte & !0x20
            } else {
                *byte
            }
        }
    }

    fn case05_branchless(bytes: &mut [u8]) {
        for byte in bytes {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
    }

    fn case06_libcore(bytes: &mut [u8]) {
        bytes.make_ascii_uppercase()
    }

    fn case07_fake_simd_u32(bytes: &mut [u8]) {
        let (before, aligned, after) = unsafe {
            bytes.align_to_mut::<u32>()
        };
        for byte in before {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
        for word in aligned {
            // FIXME: this is incorrect for some byte values:
            // addition within a byte can carry/overflow into the next byte.
            // Test case: b"\xFFz  "
            *word &= !(
                (
                    word.wrapping_add(0x1f1f1f1f) &
                    !word.wrapping_add(0x05050505) &
                    0x80808080
                ) >> 2
            )
        }
        for byte in after {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
    }

    fn case08_fake_simd_u64(bytes: &mut [u8]) {
        let (before, aligned, after) = unsafe {
            bytes.align_to_mut::<u64>()
        };
        for byte in before {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
        for word in aligned {
            // FIXME: like above, this is incorrect for some byte values.
            *word &= !(
                (
                    word.wrapping_add(0x1f1f1f1f_1f1f1f1f) &
                    !word.wrapping_add(0x05050505_05050505) &
                    0x80808080_80808080
                ) >> 2
            )
        }
        for byte in after {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
    }

    @iter

    is_ascii,
    is_ascii_alphabetic,
    is_ascii_uppercase,
    is_ascii_lowercase,
    is_ascii_alphanumeric,
    is_ascii_digit,
    is_ascii_hexdigit,
    is_ascii_punctuation,
    is_ascii_graphic,
    is_ascii_whitespace,
    is_ascii_control,
}

macro_rules! repeat {
    ($s: expr) => { concat!($s, $s, $s, $s, $s, $s, $s, $s, $s, $s) }
}

const SHORT: &'static str = "Alice's";
const MEDIUM: &'static str = "Alice's Adventures in Wonderland";
const LONG: &'static str = repeat!(r#"
    La Guida di Bragia, a Ballad Opera for the Marionette Theatre (around 1850)
    Alice's Adventures in Wonderland (1865)
    Phantasmagoria and Other Poems (1869)
    Through the Looking-Glass, and What Alice Found There
        (includes "Jabberwocky" and "The Walrus and the Carpenter") (1871)
    The Hunting of the Snark (1876)
    Rhyme? And Reason? (1883) – shares some contents with the 1869 collection,
        including the long poem "Phantasmagoria"
    A Tangled Tale (1885)
    Sylvie and Bruno (1889)
    Sylvie and Bruno Concluded (1893)
    Pillow Problems (1893)
    What the Tortoise Said to Achilles (1895)
    Three Sunsets and Other Poems (1898)
    The Manlet (1903)[106]
"#);

const ASCII_UPPERCASE_MAP: [u8; 256] = [
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'',
    b'(', b')', b'*', b'+', b',', b'-', b'.', b'/',
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
    b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
    b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G',
    b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
    b'X', b'Y', b'Z', b'[', b'\\', b']', b'^', b'_',
    b'`',

          b'A', b'B', b'C', b'D', b'E', b'F', b'G',
    b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
    b'X', b'Y', b'Z',

                      b'{', b'|', b'}', b'~', 0x7f,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];

<commit_msg>Benchmark more possibles impls of [u8]::make_ascii_uppercase<commit_after>// See comments in `u8::to_ascii_uppercase` in `src/libcore/num/mod.rs`.
fn branchless_to_ascii_upper_case(byte: u8) -> u8 {
    byte &
    !(
        (
            byte.wrapping_add(0x1f) &
            !byte.wrapping_add(0x05) &
            0x80
        ) >> 2
    )
}

macro_rules! benches {
    ($( fn $name: ident($arg: ident: &mut [u8]) $body: block )+ @iter $( $is_: ident, )+) => {
        benches! {@
            $( fn $name($arg: &mut [u8]) $body )+
            $( fn $is_(bytes: &mut [u8]) { bytes.iter().all(u8::$is_) } )+
        }
    };

    (@$( fn $name: ident($arg: ident: &mut [u8]) $body: block )+) => {
        benches!(mod short SHORT $($name $arg $body)+);
        benches!(mod medium MEDIUM $($name $arg $body)+);
        benches!(mod long LONG $($name $arg $body)+);
    };

    (mod $mod_name: ident $input: ident $($name: ident $arg: ident $body: block)+) => {
        mod $mod_name {
            use super::*;

            $(
                #[bench]
                fn $name(bencher: &mut Bencher) {
                    bencher.bytes = $input.len() as u64;
                    bencher.iter(|| {
                        let mut vec = $input.as_bytes().to_vec();
                        {
                            let $arg = &mut vec[..];
                            black_box($body);
                        }
                        vec
                    })
                }
            )+
        }
    }
}

use test::black_box;
use test::Bencher;

benches! {
    fn case00_alloc_only(_bytes: &mut [u8]) {}

    fn case01_black_box_read_each_byte(bytes: &mut [u8]) {
        for byte in bytes {
            black_box(*byte);
        }
    }

    fn case02_lookup_table(bytes: &mut [u8]) {
        for byte in bytes {
            *byte = ASCII_UPPERCASE_MAP[*byte as usize]
        }
    }

    fn case03_branch_and_subtract(bytes: &mut [u8]) {
        for byte in bytes {
            *byte = if b'a' <= *byte && *byte <= b'z' {
                *byte - b'a' + b'A'
            } else {
                *byte
            }
        }
    }

    fn case04_branch_and_mask(bytes: &mut [u8]) {
        for byte in bytes {
            *byte = if b'a' <= *byte && *byte <= b'z' {
                *byte & !0x20
            } else {
                *byte
            }
        }
    }

    fn case05_branchless(bytes: &mut [u8]) {
        for byte in bytes {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
    }

    fn case06_libcore(bytes: &mut [u8]) {
        bytes.make_ascii_uppercase()
    }

    fn case07_fake_simd_u32(bytes: &mut [u8]) {
        let (before, aligned, after) = unsafe {
            bytes.align_to_mut::<u32>()
        };
        for byte in before {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
        for word in aligned {
            // FIXME: this is incorrect for some byte values:
            // addition within a byte can carry/overflow into the next byte.
            // Test case: b"\xFFz  "
            *word &= !(
                (
                    word.wrapping_add(0x1f1f1f1f) &
                    !word.wrapping_add(0x05050505) &
                    0x80808080
                ) >> 2
            )
        }
        for byte in after {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
    }

    fn case08_fake_simd_u64(bytes: &mut [u8]) {
        let (before, aligned, after) = unsafe {
            bytes.align_to_mut::<u64>()
        };
        for byte in before {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
        for word in aligned {
            // FIXME: like above, this is incorrect for some byte values.
            *word &= !(
                (
                    word.wrapping_add(0x1f1f1f1f_1f1f1f1f) &
                    !word.wrapping_add(0x05050505_05050505) &
                    0x80808080_80808080
                ) >> 2
            )
        }
        for byte in after {
            *byte = branchless_to_ascii_upper_case(*byte)
        }
    }

    fn case09_mask_mult_bool_branchy_lookup_table(bytes: &mut [u8]) {
        fn is_ascii_lowercase(b: u8) -> bool {
            if b >= 0x80 { return false }
            match ASCII_CHARACTER_CLASS[b as usize] {
                L | Lx => true,
                _ => false,
            }
        }
        for byte in bytes {
            *byte &= !(0x20 * (is_ascii_lowercase(*byte) as u8))
        }
    }

    fn case10_mask_mult_bool_lookup_table(bytes: &mut [u8]) {
        fn is_ascii_lowercase(b: u8) -> bool {
            match ASCII_CHARACTER_CLASS[b as usize] {
                L | Lx => true,
                _ => false
            }
        }
        for byte in bytes {
            *byte &= !(0x20 * (is_ascii_lowercase(*byte) as u8))
        }
    }

    fn case11_mask_mult_bool_match_range(bytes: &mut [u8]) {
        fn is_ascii_lowercase(b: u8) -> bool {
            match b {
                b'a'...b'z' => true,
                _ => false
            }
        }
        for byte in bytes {
            *byte &= !(0x20 * (is_ascii_lowercase(*byte) as u8))
        }
    }

    fn case12_mask_shifted_bool_match_range(bytes: &mut [u8]) {
        fn is_ascii_lowercase(b: u8) -> bool {
            match b {
                b'a'...b'z' => true,
                _ => false
            }
        }
        for byte in bytes {
            *byte &= !((is_ascii_lowercase(*byte) as u8) << 5)
        }
    }

    @iter

    is_ascii,
    is_ascii_alphabetic,
    is_ascii_uppercase,
    is_ascii_lowercase,
    is_ascii_alphanumeric,
    is_ascii_digit,
    is_ascii_hexdigit,
    is_ascii_punctuation,
    is_ascii_graphic,
    is_ascii_whitespace,
    is_ascii_control,
}

macro_rules! repeat {
    ($s: expr) => { concat!($s, $s, $s, $s, $s, $s, $s, $s, $s, $s) }
}

const SHORT: &'static str = "Alice's";
const MEDIUM: &'static str = "Alice's Adventures in Wonderland";
const LONG: &'static str = repeat!(r#"
    La Guida di Bragia, a Ballad Opera for the Marionette Theatre (around 1850)
    Alice's Adventures in Wonderland (1865)
    Phantasmagoria and Other Poems (1869)
    Through the Looking-Glass, and What Alice Found There
        (includes "Jabberwocky" and "The Walrus and the Carpenter") (1871)
    The Hunting of the Snark (1876)
    Rhyme? And Reason? (1883) – shares some contents with the 1869 collection,
        including the long poem "Phantasmagoria"
    A Tangled Tale (1885)
    Sylvie and Bruno (1889)
    Sylvie and Bruno Concluded (1893)
    Pillow Problems (1893)
    What the Tortoise Said to Achilles (1895)
    Three Sunsets and Other Poems (1898)
    The Manlet (1903)[106]
"#);

const ASCII_UPPERCASE_MAP: [u8; 256] = [
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'',
    b'(', b')', b'*', b'+', b',', b'-', b'.', b'/',
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
    b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
    b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G',
    b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
    b'X', b'Y', b'Z', b'[', b'\\', b']', b'^', b'_',
    b'`',

          b'A', b'B', b'C', b'D', b'E', b'F', b'G',
    b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
    b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
    b'X', b'Y', b'Z',

                      b'{', b'|', b'}', b'~', 0x7f,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];

enum AsciiCharacterClass {
    C,  // control
    Cw, // control whitespace
    W,  // whitespace
    D,  // digit
    L,  // lowercase
    Lx, // lowercase hex digit
    U,  // uppercase
    Ux, // uppercase hex digit
    P,  // punctuation
    N,  // Non-ASCII
}
use self::AsciiCharacterClass::*;

static ASCII_CHARACTER_CLASS: [AsciiCharacterClass; 256] = [
//  _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 _a _b _c _d _e _f
    C, C, C, C, C, C, C, C, C, Cw,Cw,C, Cw,Cw,C, C, // 0_
    C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // 1_
    W, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, // 2_
    D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, P, // 3_
    P, Ux,Ux,Ux,Ux,Ux,Ux,U, U, U, U, U, U, U, U, U, // 4_
    U, U, U, U, U, U, U, U, U, U, U, P, P, P, P, P, // 5_
    P, Lx,Lx,Lx,Lx,Lx,Lx,L, L, L, L, L, L, L, L, L, // 6_
    L, L, L, L, L, L, L, L, L, L, L, P, P, P, P, C, // 7_
    N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
    N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
    N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
    N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
    N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
    N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
    N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
    N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
];
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use rand::RngCore;
use std::{
    fs, io,
    path::{Path, PathBuf},
};

/// A simple wrapper for creating a temporary directory that is automatically deleted when it's
/// dropped. Used in lieu of tempfile due to the large number of dependencies.
#[derive(Debug, PartialEq)]
pub struct TempPath {
    path_buf: PathBuf,
    persist: bool,
}

impl Drop for TempPath {
    fn drop(&mut self) {
        if !self.persist {
            fs::remove_dir_all(&self.path_buf)
                .or_else(|_| fs::remove_file(&self.path_buf))
                .unwrap_or(());
        }
    }
}

impl TempPath {
    /// Create new uninitialized temporary path, i.e. a file or directory isn't created
    /// automatically
    pub fn new() -> Self {
        // Create a random path in the system temp directory
        let mut temppath = std::env::temp_dir();
        let mut rng = rand::thread_rng();
        let mut bytes = [0_u8; 16];
        rng.fill_bytes(&mut bytes);
        temppath.push(hex::encode(&bytes));

        TempPath {
            path_buf: temppath,
            persist: false,
        }
    }

    /// Return the underlying path to this temporary directory.
    pub fn path(&self) -> &Path {
        &self.path_buf
    }

    /// Keep the temp path
    pub fn persist(&mut self) {
        self.persist = true;
    }

    pub fn create_as_file(&self) -> io::Result<()> {
        let mut builder = fs::OpenOptions::new();
        builder.write(true).create_new(true);
        builder.open(self.path())?;
        Ok(())
    }

    pub fn create_as_dir(&self) -> io::Result<()> {
        let builder = fs::DirBuilder::new();
        builder.create(self.path())?;
        Ok(())
    }
}

impl std::convert::AsRef<Path> for TempPath {
    fn as_ref(&self) -> &Path {
        self.path()
    }
}
<commit_msg>[temppath] Support custom temp directories<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use rand::RngCore;
use std::{
    fs, io,
    path::{Path, PathBuf},
};

/// A simple wrapper for creating a temporary directory that is automatically deleted when it's
/// dropped. Used in lieu of tempfile due to the large number of dependencies.
#[derive(Debug, PartialEq)]
pub struct TempPath {
    path_buf: PathBuf,
    persist: bool,
}

impl Drop for TempPath {
    fn drop(&mut self) {
        if !self.persist {
            fs::remove_dir_all(&self.path_buf)
                .or_else(|_| fs::remove_file(&self.path_buf))
                .unwrap_or(());
        }
    }
}

impl TempPath {
    /// Create new, uninitialized temporary path in the system temp directory.
    pub fn new() -> Self {
        Self::new_with_temp_dir(std::env::temp_dir())
    }

    /// Create new, uninitialized temporary path in the specified directory.
    pub fn new_with_temp_dir(temp_dir: PathBuf) -> Self {
        let mut temppath = temp_dir;
        let mut rng = rand::thread_rng();
        let mut bytes = [0_u8; 16];
        rng.fill_bytes(&mut bytes);
        temppath.push(hex::encode(&bytes));

        TempPath {
            path_buf: temppath,
            persist: false,
        }
    }

    /// Return the underlying path to this temporary directory.
    pub fn path(&self) -> &Path {
        &self.path_buf
    }

    /// Keep the temp path
    pub fn persist(&mut self) {
        self.persist = true;
    }

    pub fn create_as_file(&self) -> io::Result<()> {
        let mut builder = fs::OpenOptions::new();
        builder.write(true).create_new(true);
        builder.open(self.path())?;
        Ok(())
    }

    pub fn create_as_dir(&self) -> io::Result<()> {
        let builder = fs::DirBuilder::new();
        builder.create(self.path())?;
        Ok(())
    }
}

impl std::convert::AsRef<Path> for TempPath {
    fn as_ref(&self) -> &Path {
        self.path()
    }
}
<|endoftext|>


<commit_before>use std::f64::consts::PI;

use crate::{
    render::RenderContext2D,
    render_object::*,
    utils,
    utils::{Brush, Point, Rectangle, Thickness},
};

pub struct RectangleRenderObject;

impl RectangleRenderObject {
    // Renders rectangle with border and without radius.
    fn render_bordered_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        rect: Rectangle,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        render_context_2_d.rect(rect.x(), rect.y(), rect.width(), rect.height());

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    // Builds rectangle path with radius and without border.
    fn render_rounded_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
    ) {
        let r = x + width;
        let b = y + height;
        render_context_2_d.move_to(x + radius, y);
        render_context_2_d.line_to(r - radius, y);
        render_context_2_d.quadratic_curve_to(r, y, r, y + radius);
        render_context_2_d.line_to(r, y + height - radius);
        render_context_2_d.quadratic_curve_to(r, b, r - radius, b);
        render_context_2_d.line_to(x + radius, b);
        render_context_2_d.quadratic_curve_to(x, b, x, b - radius);
        render_context_2_d.line_to(x, y + radius);
        render_context_2_d.quadratic_curve_to(x, y, x + radius, y);
        render_context_2_d.close_path();
    }

    fn render_circle(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
    ) {
        render_context_2_d.move_to(x, y);
        render_context_2_d.arc(x + width / 2.0, y + height / 2.0, radius, 0.0, 2. * PI);
        render_context_2_d.close_path();
    }

    fn render_bordered_circle(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        self.render_circle(render_context_2_d, x, y, width, height, radius);

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    // Renders rectangle with border and radius.
    fn render_rounded_bordered_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        rect: Rectangle,
        radius: f64,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        self.render_rounded_rect_path(
            render_context_2_d,
            rect.x(),
            rect.y(),
            rect.width(),
            rect.height(),
            radius,
        );

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }
}

impl Into<Box<dyn RenderObject>> for RectangleRenderObject {
    fn into(self) -> Box<dyn RenderObject> {
        Box::new(self)
    }
}

impl RenderObject for RectangleRenderObject {
    fn render_self(&self, ctx: &mut Context, global_position: &Point) {
        let (bounds, background, border_radius, border_thickness, border_brush) = {
            let widget = ctx.widget();
            (
                widget.clone::<Rectangle>("bounds"),
                widget.get::<Brush>("background").clone(),
                widget.clone_or_default::<f64>("border_radius"),
                widget.clone_or_default::<Thickness>("border_width"),
                widget.clone_or_default::<Brush>("border_brush"),
            )
        };

        if (bounds.width() == 0.0
            || bounds.height() == 0.0
            || (background.is_transparent() && border_brush.is_transparent()))
            && (border_thickness.left == 0.0
                && border_thickness.top == 0.0
                && border_thickness.right == 0.0
                && border_thickness.bottom == 0.0)
        {
            return;
        }

        let has_thickness = border_thickness.left > 0.0
            || border_thickness.top > 0.0
            || border_thickness.right > 0.0
            || border_thickness.bottom > 0.0;

        ctx.render_context_2_d().begin_path();

        if bounds.width() == bounds.height() && border_radius >= bounds.width() / 2.0 {
            if !has_thickness {
                self.render_circle(
                    ctx.render_context_2_d(),
                    global_position.x() + bounds.x(),
                    global_position.y() + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                    border_radius,
                );
                ctx.render_context_2_d().set_fill_style(background);
                ctx.render_context_2_d().fill();
            } else {
                self.render_bordered_circle(
                    ctx.render_context_2_d(),
                    global_position.x() + bounds.x(),
                    global_position.y() + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                    border_radius,
                    background,
                    border_brush,
                    border_thickness,
                );
            }
            return;
        } else if border_radius > 0. && has_thickness {
            self.render_rounded_bordered_rect_path(
                ctx.render_context_2_d(),
                Rectangle::new(
                    *global_position + bounds.position(),
                    bounds.width(),
                    bounds.height(),
                ),
                border_radius,
                background,
                border_brush,
                border_thickness,
            );
        } else if border_radius > 0. {
            self.render_rounded_rect_path(
                ctx.render_context_2_d(),
                global_position.x() + bounds.x(),
                global_position.y() + bounds.y(),
                bounds.width(),
                bounds.height(),
                border_radius,
            );

            ctx.render_context_2_d().set_fill_style(background);
            ctx.render_context_2_d().fill();
        } else if has_thickness {
            self.render_bordered_rect_path(
                ctx.render_context_2_d(),
                Rectangle::new(
                    *global_position + bounds.position(),
                    bounds.width(),
                    bounds.height(),
                ),
                background,
                border_brush,
                border_thickness,
            );
        } else {
            ctx.render_context_2_d().rect(
                global_position.x() + bounds.x(),
                global_position.y() + bounds.y(),
                bounds.width(),
                bounds.height(),
            );
            ctx.render_context_2_d().set_fill_style(background);
            ctx.render_context_2_d().fill();
        }
    }
}
<commit_msg>fixed circle border drawing.<commit_after>use std::f64::consts::PI;

use crate::{
    render::RenderContext2D,
    render_object::*,
    utils,
    utils::{Brush, Point, Rectangle, Thickness},
};

pub struct RectangleRenderObject;

impl RectangleRenderObject {
    // Renders rectangle with border and without radius.
    fn render_bordered_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        rect: Rectangle,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        render_context_2_d.rect(rect.x(), rect.y(), rect.width(), rect.height());

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    // Builds rectangle path with radius and without border.
    fn render_rounded_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
    ) {
        let r = x + width;
        let b = y + height;
        render_context_2_d.move_to(x + radius, y);
        render_context_2_d.line_to(r - radius, y);
        render_context_2_d.quadratic_curve_to(r, y, r, y + radius);
        render_context_2_d.line_to(r, y + height - radius);
        render_context_2_d.quadratic_curve_to(r, b, r - radius, b);
        render_context_2_d.line_to(x + radius, b);
        render_context_2_d.quadratic_curve_to(x, b, x, b - radius);
        render_context_2_d.line_to(x, y + radius);
        render_context_2_d.quadratic_curve_to(x, y, x + radius, y);
        render_context_2_d.close_path();
    }

    fn render_circle(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
    ) {
        render_context_2_d.arc(x + width / 2.0, y + height / 2.0, radius, 0., 2. * PI);
        render_context_2_d.close_path();
    }

    fn render_bordered_circle(
        &self,
        render_context_2_d: &mut RenderContext2D,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        radius: f64,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        self.render_circle(render_context_2_d, x, y, width, height, radius);

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }

    // Renders rectangle with border and radius.
    fn render_rounded_bordered_rect_path(
        &self,
        render_context_2_d: &mut RenderContext2D,
        rect: Rectangle,
        radius: f64,
        brush: utils::Brush,
        border_brush: utils::Brush,
        border_thickness: Thickness,
    ) {
        self.render_rounded_rect_path(
            render_context_2_d,
            rect.x(),
            rect.y(),
            rect.width(),
            rect.height(),
            radius,
        );

        if !brush.is_transparent() {
            render_context_2_d.set_fill_style(brush);
            render_context_2_d.fill();
        }

        if !border_brush.is_transparent() {
            render_context_2_d.set_line_width(border_thickness.left());
            render_context_2_d.set_stroke_style(border_brush);
            render_context_2_d.stroke();
        }
    }
}

impl Into<Box<dyn RenderObject>> for RectangleRenderObject {
    fn into(self) -> Box<dyn RenderObject> {
        Box::new(self)
    }
}

impl RenderObject for RectangleRenderObject {
    fn render_self(&self, ctx: &mut Context, global_position: &Point) {
        let (bounds, background, border_radius, border_thickness, border_brush) = {
            let widget = ctx.widget();
            (
                widget.clone::<Rectangle>("bounds"),
                widget.get::<Brush>("background").clone(),
                widget.clone_or_default::<f64>("border_radius"),
                widget.clone_or_default::<Thickness>("border_width"),
                widget.clone_or_default::<Brush>("border_brush"),
            )
        };

        if (bounds.width() == 0.0
            || bounds.height() == 0.0
            || (background.is_transparent() && border_brush.is_transparent()))
            && (border_thickness.left == 0.0
                && border_thickness.top == 0.0
                && border_thickness.right == 0.0
                && border_thickness.bottom == 0.0)
        {
            return;
        }

        let has_thickness = border_thickness.left > 0.0
            || border_thickness.top > 0.0
            || border_thickness.right > 0.0
            || border_thickness.bottom > 0.0;

        ctx.render_context_2_d().begin_path();

        if bounds.width() == bounds.height() && border_radius >= bounds.width() / 2.0 {
            if !has_thickness {
                self.render_circle(
                    ctx.render_context_2_d(),
                    global_position.x() + bounds.x(),
                    global_position.y() + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                    border_radius,
                );
                ctx.render_context_2_d().set_fill_style(background);
                ctx.render_context_2_d().fill();
            } else {
                self.render_bordered_circle(
                    ctx.render_context_2_d(),
                    global_position.x() + bounds.x(),
                    global_position.y() + bounds.y(),
                    bounds.width(),
                    bounds.height(),
                    border_radius,
                    background,
                    border_brush,
                    border_thickness,
                );
            }
            return;
        } else if border_radius > 0. && has_thickness {
            self.render_rounded_bordered_rect_path(
                ctx.render_context_2_d(),
                Rectangle::new(
                    *global_position + bounds.position(),
                    bounds.width(),
                    bounds.height(),
                ),
                border_radius,
                background,
                border_brush,
                border_thickness,
            );
        } else if border_radius > 0. {
            self.render_rounded_rect_path(
                ctx.render_context_2_d(),
                global_position.x() + bounds.x(),
                global_position.y() + bounds.y(),
                bounds.width(),
                bounds.height(),
                border_radius,
            );

            ctx.render_context_2_d().set_fill_style(background);
            ctx.render_context_2_d().fill();
        } else if has_thickness {
            self.render_bordered_rect_path(
                ctx.render_context_2_d(),
                Rectangle::new(
                    *global_position + bounds.position(),
                    bounds.width(),
                    bounds.height(),
                ),
                background,
                border_brush,
                border_thickness,
            );
        } else {
            ctx.render_context_2_d().rect(
                global_position.x() + bounds.x(),
                global_position.y() + bounds.y(),
                bounds.width(),
                bounds.height(),
            );
            ctx.render_context_2_d().set_fill_style(background);
            ctx.render_context_2_d().fill();
        }
    }
}
<|endoftext|>