<commit_before>use std::collections::HashMap;
use std::fmt;

use semver::Version;
use url::Url;

use core::PackageId;
use util::{ToSemver, ToUrl};
use util::errors::{CargoResult, CargoResultExt};

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct PackageIdSpec {
    name: String,
    version: Option<Version>,
    url: Option<Url>,
}

impl PackageIdSpec {
    pub fn parse(spec: &str) -> CargoResult<PackageIdSpec> {
        if spec.contains('/') {
            if let Ok(url) = spec.to_url() {
                return PackageIdSpec::from_url(url);
            }
            if !spec.contains("://") {
                if let Ok(url) = Url::parse(&format!("cargo://{}", spec)) {
                    return PackageIdSpec::from_url(url);
                }
            }
        }
        let mut parts = spec.splitn(2, ':');
        let name = parts.next().unwrap();
        let version = match parts.next() {
            Some(version) => Some(Version::parse(version)?),
            None => None,
        };
        for ch in name.chars() {
            if !ch.is_alphanumeric() && ch != '_' && ch != '-' {
                bail!("invalid character in pkgid `{}`: `{}`", spec, ch)
            }
        }
        Ok(PackageIdSpec {
            name: name.to_string(),
            version,
            url: None,
        })
    }

    pub fn query_str<'a, I>(spec: &str, i: I) -> CargoResult<&'a PackageId>
    where
        I: IntoIterator<Item = &'a PackageId>,
    {
        let spec = PackageIdSpec::parse(spec)
            .chain_err(|| format_err!("invalid package id specification: `{}`", spec))?;
        spec.query(i)
    }

    pub fn from_package_id(package_id: &PackageId) -> PackageIdSpec {
        PackageIdSpec {
            name: package_id.name().to_string(),
            version: Some(package_id.version().clone()),
            url: Some(package_id.source_id().url().clone()),
        }
    }

    fn from_url(mut url: Url) -> CargoResult<PackageIdSpec> {
        if url.query().is_some() {
            bail!("cannot have a query string in a pkgid: {}", url)
        }
        let frag = url.fragment().map(|s| s.to_owned());
        url.set_fragment(None);
        let (name, version) = {
            let mut path = url.path_segments()
                .ok_or_else(|| format_err!("pkgid urls must have a path: {}", url))?;
            let path_name = path.next_back().ok_or_else(|| {
                format_err!(
                    "pkgid urls must have at least one path \
                     component: {}",
                    url
                )
            })?;
            match frag {
                Some(fragment) => {
                    let mut parts = fragment.splitn(2, ':');
                    let name_or_version = parts.next().unwrap();
                    match parts.next() {
                        Some(part) => {
                            let version = part.to_semver()?;
                            (name_or_version.to_string(), Some(version))
                        }
                        None => {
                            if name_or_version.chars().next().unwrap().is_alphabetic() {
                                (name_or_version.to_string(), None)
                            } else {
                                let version = name_or_version.to_semver()?;
                                (path_name.to_string(), Some(version))
                            }
                        }
                    }
                }
                None => (path_name.to_string(), None),
            }
        };
        Ok(PackageIdSpec {
            name,
            version,
            url: Some(url),
        })
    }

    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn version(&self) -> Option<&Version> {
        self.version.as_ref()
    }
    pub fn url(&self) -> Option<&Url> {
        self.url.as_ref()
    }

    pub fn set_url(&mut self, url: Url) {
        self.url = Some(url);
    }

    pub fn matches(&self, package_id: &PackageId) -> bool {
        if self.name() != &*package_id.name() {
            return false;
        }

        if let Some(ref v) = self.version {
            if v != package_id.version() {
                return false;
            }
        }

        match self.url {
            Some(ref u) => u == package_id.source_id().url(),
            None => true,
        }
    }

    pub fn query<'a, I>(&self, i: I) -> CargoResult<&'a PackageId>
    where
        I: IntoIterator<Item = &'a PackageId>,
    {
        let mut ids = i.into_iter().filter(|p| self.matches(*p));
        let ret = match ids.next() {
            Some(id) => id,
            None => bail!(
                "package id specification `{}` \
                 matched no packages",
                self
            ),
        };
        return match ids.next() {
            Some(other) => {
                let mut msg = format!(
                    "There are multiple `{}` packages in \
                     your project, and the specification \
                     `{}` is ambiguous.\n\
                     Please re-run this command \
                     with `-p <spec>` where `<spec>` is one \
                     of the following:",
                    self.name(),
                    self
                );
                let mut vec = vec![ret, other];
                vec.extend(ids);
                minimize(&mut msg, &vec, self);
                Err(format_err!("{}", msg))
            }
            None => Ok(ret),
        };

        fn minimize(msg: &mut String, ids: &[&PackageId], spec: &PackageIdSpec) {
            let mut version_cnt = HashMap::new();
            for id in ids {
                *version_cnt.entry(id.version()).or_insert(0) += 1;
            }
            for id in ids {
                if version_cnt[id.version()] == 1 {
                    msg.push_str(&format!("\n  {}:{}", spec.name(), id.version()));
                } else {
                    msg.push_str(&format!("\n  {}", PackageIdSpec::from_package_id(*id)));
                }
            }
        }
    }
}

impl fmt::Display for PackageIdSpec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut printed_name = false;
        match self.url {
            Some(ref url) => {
                if url.scheme() == "cargo" {
                    write!(f, "{}{}", url.host().unwrap(), url.path())?;
                } else {
                    write!(f, "{}", url)?;
                }
                if url.path_segments().unwrap().next_back().unwrap() != self.name {
                    printed_name = true;
                    write!(f, "#{}", self.name)?;
                }
            }
            None => {
                printed_name = true;
                write!(f, "{}", self.name)?
            }
        }
        if let Some(ref v) = self.version {
            write!(f, "{}{}", if printed_name { ":" } else { "#" }, v)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use core::{PackageId, SourceId};
    use super::PackageIdSpec;
    use url::Url;
    use semver::Version;

    #[test]
    fn good_parsing() {
        fn ok(spec: &str, expected: PackageIdSpec) {
            let parsed = PackageIdSpec::parse(spec).unwrap();
            assert_eq!(parsed, expected);
            assert_eq!(parsed.to_string(), spec);
        }

        ok(
            "http://crates.io/foo#1.2.3",
            PackageIdSpec {
                name: "foo".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: Some(Url::parse("http://crates.io/foo").unwrap()),
            },
        );
        ok(
            "http://crates.io/foo#bar:1.2.3",
            PackageIdSpec {
                name: "bar".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: Some(Url::parse("http://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo",
            PackageIdSpec {
                name: "foo".to_string(),
                version: None,
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#1.2.3",
            PackageIdSpec {
                name: "foo".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#bar",
            PackageIdSpec {
                name: "bar".to_string(),
                version: None,
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#bar:1.2.3",
            PackageIdSpec {
                name: "bar".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "foo",
            PackageIdSpec {
                name: "foo".to_string(),
                version: None,
                url: None,
            },
        );
        ok(
            "foo:1.2.3",
            PackageIdSpec {
                name: "foo".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: None,
            },
        );
    }

    #[test]
    fn bad_parsing() {
        assert!(PackageIdSpec::parse("baz:").is_err());
        assert!(PackageIdSpec::parse("baz:*").is_err());
        assert!(PackageIdSpec::parse("baz:1.0").is_err());
        assert!(PackageIdSpec::parse("http://baz:1.0").is_err());
        assert!(PackageIdSpec::parse("http://#baz:1.0").is_err());
    }

    #[test]
    fn matching() {
        let url = Url::parse("http://example.com").unwrap();
        let sid = SourceId::for_registry(&url).unwrap();
        let foo = PackageId::new("foo", "1.2.3", &sid).unwrap();
        let bar = PackageId::new("bar", "1.2.3", &sid).unwrap();

        assert!(PackageIdSpec::parse("foo").unwrap().matches(&foo));
        assert!(!PackageIdSpec::parse("foo").unwrap().matches(&bar));
        assert!(PackageIdSpec::parse("foo:1.2.3").unwrap().matches(&foo));
        assert!(!PackageIdSpec::parse("foo:1.2.2").unwrap().matches(&foo));
    }
}
<commit_msg>Add docs<commit_after>use std::collections::HashMap;
use std::fmt;

use semver::Version;
use url::Url;

use core::PackageId;
use util::{ToSemver, ToUrl};
use util::errors::{CargoResult, CargoResultExt};

/// Some or all of the data required to indentify a package:
///
///  1. the package name (a `String`, required)
///  2. the package version (a `Version`, optional)
///  3. the package source (a `Url`, optional)
///
/// If any of the optional fields are omitted, then the package id may be ambiguous, there may be
/// more than one package/version/url combo that will match. However, often just the name is
/// sufficient to uniquely define a package id.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct PackageIdSpec {
    name: String,
    version: Option<Version>,
    url: Option<Url>,
}

impl PackageIdSpec {
    /// Parses a spec string and returns a `PackageIdSpec` if the string was valid.
    ///
    /// # Examples
    /// Some examples of valid strings
    ///
    /// ```
    /// use cargo::core::PackageIdSpec;
    ///
    /// let specs = vec![
    ///     "http://crates.io/foo#1.2.3",
    ///     "http://crates.io/foo#bar:1.2.3",
    ///     "crates.io/foo",
    ///     "crates.io/foo#1.2.3",
    ///     "crates.io/foo#bar",
    ///     "crates.io/foo#bar:1.2.3",
    ///     "foo",
    ///     "foo:1.2.3",
    /// ];
    /// for spec in specs {
    ///     assert!(PackageIdSpec::parse(spec).is_ok());
    /// }
    pub fn parse(spec: &str) -> CargoResult<PackageIdSpec> {
        if spec.contains('/') {
            if let Ok(url) = spec.to_url() {
                return PackageIdSpec::from_url(url);
            }
            if !spec.contains("://") {
                if let Ok(url) = Url::parse(&format!("cargo://{}", spec)) {
                    return PackageIdSpec::from_url(url);
                }
            }
        }
        let mut parts = spec.splitn(2, ':');
        let name = parts.next().unwrap();
        let version = match parts.next() {
            Some(version) => Some(Version::parse(version)?),
            None => None,
        };
        for ch in name.chars() {
            if !ch.is_alphanumeric() && ch != '_' && ch != '-' {
                bail!("invalid character in pkgid `{}`: `{}`", spec, ch)
            }
        }
        Ok(PackageIdSpec {
            name: name.to_string(),
            version,
            url: None,
        })
    }

    /// Roughly equivalent to `PackageIdSpec::parse(spec)?.query(i)`
    pub fn query_str<'a, I>(spec: &str, i: I) -> CargoResult<&'a PackageId>
    where
        I: IntoIterator<Item = &'a PackageId>,
    {
        let spec = PackageIdSpec::parse(spec)
            .chain_err(|| format_err!("invalid package id specification: `{}`", spec))?;
        spec.query(i)
    }

    /// Convert a `PackageId` to a `PackageIdSpec`, which will have both the `Version` and `Url`
    /// fields filled in.
    pub fn from_package_id(package_id: &PackageId) -> PackageIdSpec {
        PackageIdSpec {
            name: package_id.name().to_string(),
            version: Some(package_id.version().clone()),
            url: Some(package_id.source_id().url().clone()),
        }
    }

    /// Tries to convert a valid `Url` to a `PackageIdSpec`.
    fn from_url(mut url: Url) -> CargoResult<PackageIdSpec> {
        if url.query().is_some() {
            bail!("cannot have a query string in a pkgid: {}", url)
        }
        let frag = url.fragment().map(|s| s.to_owned());
        url.set_fragment(None);
        let (name, version) = {
            let mut path = url.path_segments()
                .ok_or_else(|| format_err!("pkgid urls must have a path: {}", url))?;
            let path_name = path.next_back().ok_or_else(|| {
                format_err!(
                    "pkgid urls must have at least one path \
                     component: {}",
                    url
                )
            })?;
            match frag {
                Some(fragment) => {
                    let mut parts = fragment.splitn(2, ':');
                    let name_or_version = parts.next().unwrap();
                    match parts.next() {
                        Some(part) => {
                            let version = part.to_semver()?;
                            (name_or_version.to_string(), Some(version))
                        }
                        None => {
                            if name_or_version.chars().next().unwrap().is_alphabetic() {
                                (name_or_version.to_string(), None)
                            } else {
                                let version = name_or_version.to_semver()?;
                                (path_name.to_string(), Some(version))
                            }
                        }
                    }
                }
                None => (path_name.to_string(), None),
            }
        };
        Ok(PackageIdSpec {
            name,
            version,
            url: Some(url),
        })
    }

    /// Getter for `name` field.
    pub fn name(&self) -> &str {
        &self.name
    }
    /// Getter for `version` field.
    pub fn version(&self) -> Option<&Version> {
        self.version.as_ref()
    }
    /// Getter for `url` field.
    pub fn url(&self) -> Option<&Url> {
        self.url.as_ref()
    }

    /// Setter for `url` field.
    pub fn set_url(&mut self, url: Url) {
        self.url = Some(url);
    }

    /// Checkes whether the given `PackageId` matches the `PackageIdSpec`.
    pub fn matches(&self, package_id: &PackageId) -> bool {
        if self.name() != &*package_id.name() {
            return false;
        }

        if let Some(ref v) = self.version {
            if v != package_id.version() {
                return false;
            }
        }

        match self.url {
            Some(ref u) => u == package_id.source_id().url(),
            None => true,
        }
    }

    /// Checks a list of `PackageId`s to find 1 that matches this `PackageIdSpec`. If 0, 2, or
    /// more are found, then this returns an error.
    pub fn query<'a, I>(&self, i: I) -> CargoResult<&'a PackageId>
    where
        I: IntoIterator<Item = &'a PackageId>,
    {
        let mut ids = i.into_iter().filter(|p| self.matches(*p));
        let ret = match ids.next() {
            Some(id) => id,
            None => bail!(
                "package id specification `{}` \
                 matched no packages",
                self
            ),
        };
        return match ids.next() {
            Some(other) => {
                let mut msg = format!(
                    "There are multiple `{}` packages in \
                     your project, and the specification \
                     `{}` is ambiguous.\n\
                     Please re-run this command \
                     with `-p <spec>` where `<spec>` is one \
                     of the following:",
                    self.name(),
                    self
                );
                let mut vec = vec![ret, other];
                vec.extend(ids);
                minimize(&mut msg, &vec, self);
                Err(format_err!("{}", msg))
            }
            None => Ok(ret),
        };

        fn minimize(msg: &mut String, ids: &[&PackageId], spec: &PackageIdSpec) {
            let mut version_cnt = HashMap::new();
            for id in ids {
                *version_cnt.entry(id.version()).or_insert(0) += 1;
            }
            for id in ids {
                if version_cnt[id.version()] == 1 {
                    msg.push_str(&format!("\n  {}:{}", spec.name(), id.version()));
                } else {
                    msg.push_str(&format!("\n  {}", PackageIdSpec::from_package_id(*id)));
                }
            }
        }
    }
}

impl fmt::Display for PackageIdSpec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut printed_name = false;
        match self.url {
            Some(ref url) => {
                if url.scheme() == "cargo" {
                    write!(f, "{}{}", url.host().unwrap(), url.path())?;
                } else {
                    write!(f, "{}", url)?;
                }
                if url.path_segments().unwrap().next_back().unwrap() != self.name {
                    printed_name = true;
                    write!(f, "#{}", self.name)?;
                }
            }
            None => {
                printed_name = true;
                write!(f, "{}", self.name)?
            }
        }
        if let Some(ref v) = self.version {
            write!(f, "{}{}", if printed_name { ":" } else { "#" }, v)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use core::{PackageId, SourceId};
    use super::PackageIdSpec;
    use url::Url;
    use semver::Version;

    #[test]
    fn good_parsing() {
        fn ok(spec: &str, expected: PackageIdSpec) {
            let parsed = PackageIdSpec::parse(spec).unwrap();
            assert_eq!(parsed, expected);
            assert_eq!(parsed.to_string(), spec);
        }

        ok(
            "http://crates.io/foo#1.2.3",
            PackageIdSpec {
                name: "foo".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: Some(Url::parse("http://crates.io/foo").unwrap()),
            },
        );
        ok(
            "http://crates.io/foo#bar:1.2.3",
            PackageIdSpec {
                name: "bar".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: Some(Url::parse("http://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo",
            PackageIdSpec {
                name: "foo".to_string(),
                version: None,
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#1.2.3",
            PackageIdSpec {
                name: "foo".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#bar",
            PackageIdSpec {
                name: "bar".to_string(),
                version: None,
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "crates.io/foo#bar:1.2.3",
            PackageIdSpec {
                name: "bar".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: Some(Url::parse("cargo://crates.io/foo").unwrap()),
            },
        );
        ok(
            "foo",
            PackageIdSpec {
                name: "foo".to_string(),
                version: None,
                url: None,
            },
        );
        ok(
            "foo:1.2.3",
            PackageIdSpec {
                name: "foo".to_string(),
                version: Some(Version::parse("1.2.3").unwrap()),
                url: None,
            },
        );
    }

    #[test]
    fn bad_parsing() {
        assert!(PackageIdSpec::parse("baz:").is_err());
        assert!(PackageIdSpec::parse("baz:*").is_err());
        assert!(PackageIdSpec::parse("baz:1.0").is_err());
        assert!(PackageIdSpec::parse("http://baz:1.0").is_err());
        assert!(PackageIdSpec::parse("http://#baz:1.0").is_err());
    }

    #[test]
    fn matching() {
        let url = Url::parse("http://example.com").unwrap();
        let sid = SourceId::for_registry(&url).unwrap();
        let foo = PackageId::new("foo", "1.2.3", &sid).unwrap();
        let bar = PackageId::new("bar", "1.2.3", &sid).unwrap();

        assert!(PackageIdSpec::parse("foo").unwrap().matches(&foo));
        assert!(!PackageIdSpec::parse("foo").unwrap().matches(&bar));
        assert!(PackageIdSpec::parse("foo:1.2.3").unwrap().matches(&foo));
        assert!(!PackageIdSpec::parse("foo:1.2.2").unwrap().matches(&foo));
    }
}
<|endoftext|>


<commit_before>use wire::data::PeerMsg;
use file::PartialFile;
use metainfo::MetaInfo;
use convert::TryInto;
use sha1::{Sha1, Digest};

pub trait PeerHandler {
    fn handshake(&mut self, info: &MetaInfo, pf: &PartialFile) -> Vec<PeerMsg>;
    fn on_message_receive(&mut self, info: &MetaInfo, pf: &PartialFile, msg: PeerMsg) -> Vec<PeerMsg>;
    fn peer_choking(&mut self, info: &MetaInfo, pf: &PartialFile) -> bool;
    fn peer_interested(&self, info: &MetaInfo, pf: &PartialFile) -> bool;
}

pub struct BasicHandler(String);

impl BasicHandler {
    pub fn new(id: String) -> BasicHandler {
        BasicHandler(id)
    }
}

const _protocol_id: &'static str = "rustorrent-beta";

impl PeerHandler for BasicHandler {
    fn handshake(&mut self, info: &MetaInfo, pf: &PartialFile) -> Vec<PeerMsg> {
        /*if let Ok(info) = info.info.try_into() {
            let sha1 = Sha1::new();
            let hash = sha1.update(info).digest().bytes();
            let id = self.id.to_string().into_bytes();
            id.resize(0);
            PeerMsg::HandShake(_protocol_id.to_string().into_bytes(), hash, id)
        } else {
            vec![]
        }*/
        unimplemented!();
    }
    fn on_message_receive(&mut self, info: &MetaInfo, pf: &PartialFile, msg: PeerMsg) -> Vec<PeerMsg> {
        unimplemented!()
    }
    fn peer_choking(&mut self, info: &MetaInfo, pf: &PartialFile) -> bool {
        unimplemented!()
    }
    fn peer_interested(&self, info: &MetaInfo, pf: &PartialFile) -> bool {
        unimplemented!()
    }

}
<commit_msg>Add handshake method<commit_after>use wire::data::PeerMsg;
use file::PartialFile;
use metainfo::MetaInfo;
use convert::TryInto;
use sha1::{Sha1, Digest};
use metainfo::SHA1Hash20b;

pub trait PeerHandler {
    fn handshake(&mut self, info_hash: &SHA1Hash20b, pf: &PartialFile) -> Vec<PeerMsg>;
    fn on_message_receive(&mut self, info: &MetaInfo, pf: &PartialFile, msg: PeerMsg) -> Vec<PeerMsg>;
    fn peer_choking(&mut self, info: &MetaInfo, pf: &PartialFile) -> bool;
    fn peer_interested(&self, info: &MetaInfo, pf: &PartialFile) -> bool;
}

pub struct BasicHandler(String);

impl BasicHandler {
    pub fn new(id: String) -> BasicHandler {
        BasicHandler(id)
    }
}

const _protocol_id: &'static str = "rustorrent-beta";

impl PeerHandler for BasicHandler {
    fn handshake(&mut self, hash: &SHA1Hash20b, pf: &PartialFile) -> Vec<PeerMsg> {
        /*let sha1 = Sha1::new();
        let hash = sha1.update(info).digest().bytes();*/
        let mut id = self.0.to_string().into_bytes();
        id.resize(20, 0);
        vec![PeerMsg::HandShake(_protocol_id.to_string(), hash.clone(), id)]
    }
    fn on_message_receive(&mut self, info: &MetaInfo, pf: &PartialFile, msg: PeerMsg) -> Vec<PeerMsg> {
        unimplemented!()
    }
    fn peer_choking(&mut self, info: &MetaInfo, pf: &PartialFile) -> bool {
        unimplemented!()
    }
    fn peer_interested(&self, info: &MetaInfo, pf: &PartialFile) -> bool {
        unimplemented!()
    }

}
<|endoftext|>


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

use crate::gl_context::GLContextFactory;
use crate::webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThread};
use canvas_traits::webgl::webgl_channel;
use canvas_traits::webgl::DOMToTextureCommand;
use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver};
use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler};
use euclid::Size2D;
use fnv::FnvHashMap;
use gleam::gl;
use gleam::gl::Gl;
use servo_config::prefs::PREFS;
use std::rc::Rc;

/// WebGL Threading API entry point that lives in the constellation.
pub struct WebGLThreads(WebGLSender<WebGLMsg>);

impl WebGLThreads {
    /// Creates a new WebGLThreads object
    pub fn new(
        gl_factory: GLContextFactory,
        webrender_gl: Rc<dyn gl::Gl>,
        webrender_api_sender: webrender_api::RenderApiSender,
        webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
    ) -> (
        WebGLThreads,
        Box<dyn webrender::ExternalImageHandler>,
        Option<Box<dyn webrender::OutputImageHandler>>,
    ) {
        // This implementation creates a single `WebGLThread` for all the pipelines.
        let channel = WebGLThread::start(
            gl_factory,
            webrender_api_sender,
            webvr_compositor.map(|c| WebVRRenderWrapper(c)),
        );
        let output_handler = if PREFS.is_dom_to_texture_enabled() {
            Some(Box::new(OutputHandler::new(
                webrender_gl.clone(),
                channel.clone(),
            )))
        } else {
            None
        };
        let external =
            WebGLExternalImageHandler::new(WebGLExternalImages::new(webrender_gl, channel.clone()));
        (
            WebGLThreads(channel),
            Box::new(external),
            output_handler.map(|b| b as Box<_>),
        )
    }

    /// Gets the WebGLThread handle for each script pipeline.
    pub fn pipeline(&self) -> WebGLPipeline {
        // This mode creates a single thread, so the existing WebGLChan is just cloned.
        WebGLPipeline(WebGLChan(self.0.clone()))
    }

    /// Sends a exit message to close the WebGLThreads and release all WebGLContexts.
    pub fn exit(&self) -> Result<(), &'static str> {
        self.0
            .send(WebGLMsg::Exit)
            .map_err(|_| "Failed to send Exit message")
    }
}

/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads.
struct WebGLExternalImages {
    webrender_gl: Rc<dyn gl::Gl>,
    webgl_channel: WebGLSender<WebGLMsg>,
    // Used to avoid creating a new channel on each received WebRender request.
    lock_channel: (
        WebGLSender<(u32, Size2D<i32>, usize)>,
        WebGLReceiver<(u32, Size2D<i32>, usize)>,
    ),
}

impl WebGLExternalImages {
    fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
        Self {
            webrender_gl,
            webgl_channel: channel,
            lock_channel: webgl_channel().unwrap(),
        }
    }
}

impl WebGLExternalImageApi for WebGLExternalImages {
    fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>) {
        // WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue.
        // The WebGLMsg::Lock message inserts a fence in the WebGL command queue.
        self.webgl_channel
            .send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone()))
            .unwrap();
        let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap();
        // The next glWaitSync call is run on the WR thread and it's used to synchronize the two
        // flows of OpenGL commands in order to avoid WR using a semi-ready WebGL texture.
        // glWaitSync doesn't block WR thread, it affects only internal OpenGL subsystem.
        self.webrender_gl
            .wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED);
        (image_id, size)
    }

    fn unlock(&mut self, ctx_id: WebGLContextId) {
        self.webgl_channel.send(WebGLMsg::Unlock(ctx_id)).unwrap();
    }
}

/// Wrapper to send WebVR commands used in `WebGLThread`.
struct WebVRRenderWrapper(Box<dyn WebVRRenderHandler>);

impl WebVRRenderHandler for WebVRRenderWrapper {
    fn handle(&mut self, gl: &dyn Gl, command: WebVRCommand, texture: Option<(u32, Size2D<i32>)>) {
        self.0.handle(gl, command, texture);
    }
}

/// struct used to implement DOMToTexture feature and webrender::OutputImageHandler trait.
type OutputHandlerData = Option<(u32, Size2D<i32>)>;
struct OutputHandler {
    webrender_gl: Rc<dyn gl::Gl>,
    webgl_channel: WebGLSender<WebGLMsg>,
    // Used to avoid creating a new channel on each received WebRender request.
    lock_channel: (
        WebGLSender<OutputHandlerData>,
        WebGLReceiver<OutputHandlerData>,
    ),
    sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>,
}

impl OutputHandler {
    fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
        Self {
            webrender_gl,
            webgl_channel: channel,
            lock_channel: webgl_channel().unwrap(),
            sync_objects: Default::default(),
        }
    }
}

/// Bridge between the WR frame outputs and WebGL to implement DOMToTexture synchronization.
impl webrender::OutputImageHandler for OutputHandler {
    fn lock(
        &mut self,
        id: webrender_api::PipelineId,
    ) -> Option<(u32, webrender_api::DeviceIntSize)> {
        // Insert a fence in the WR command queue
        let gl_sync = self
            .webrender_gl
            .fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
        // The lock command adds a WaitSync call on the WebGL command flow.
        let command = DOMToTextureCommand::Lock(id, gl_sync as usize, self.lock_channel.0.clone());
        self.webgl_channel
            .send(WebGLMsg::DOMToTextureCommand(command))
            .unwrap();
        self.lock_channel.1.recv().unwrap().map(|(tex_id, size)| {
            (
                tex_id,
                webrender_api::DeviceIntSize::new(size.width, size.height),
            )
        })
    }

    fn unlock(&mut self, id: webrender_api::PipelineId) {
        if let Some(gl_sync) = self.sync_objects.remove(&id) {
            // Flush the Sync object into the GPU's command queue to guarantee that it it's signaled.
            self.webrender_gl.flush();
            // Mark the sync object for deletion.
            self.webrender_gl.delete_sync(gl_sync);
        }
    }
}
<commit_msg>Fixed inconsistency in import of gl::Gl<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::gl_context::GLContextFactory;
use crate::webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThread};
use canvas_traits::webgl::webgl_channel;
use canvas_traits::webgl::DOMToTextureCommand;
use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver};
use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler};
use euclid::Size2D;
use fnv::FnvHashMap;
use gleam::gl;
use servo_config::prefs::PREFS;
use std::rc::Rc;

/// WebGL Threading API entry point that lives in the constellation.
pub struct WebGLThreads(WebGLSender<WebGLMsg>);

impl WebGLThreads {
    /// Creates a new WebGLThreads object
    pub fn new(
        gl_factory: GLContextFactory,
        webrender_gl: Rc<dyn gl::Gl>,
        webrender_api_sender: webrender_api::RenderApiSender,
        webvr_compositor: Option<Box<dyn WebVRRenderHandler>>,
    ) -> (
        WebGLThreads,
        Box<dyn webrender::ExternalImageHandler>,
        Option<Box<dyn webrender::OutputImageHandler>>,
    ) {
        // This implementation creates a single `WebGLThread` for all the pipelines.
        let channel = WebGLThread::start(
            gl_factory,
            webrender_api_sender,
            webvr_compositor.map(|c| WebVRRenderWrapper(c)),
        );
        let output_handler = if PREFS.is_dom_to_texture_enabled() {
            Some(Box::new(OutputHandler::new(
                webrender_gl.clone(),
                channel.clone(),
            )))
        } else {
            None
        };
        let external =
            WebGLExternalImageHandler::new(WebGLExternalImages::new(webrender_gl, channel.clone()));
        (
            WebGLThreads(channel),
            Box::new(external),
            output_handler.map(|b| b as Box<_>),
        )
    }

    /// Gets the WebGLThread handle for each script pipeline.
    pub fn pipeline(&self) -> WebGLPipeline {
        // This mode creates a single thread, so the existing WebGLChan is just cloned.
        WebGLPipeline(WebGLChan(self.0.clone()))
    }

    /// Sends a exit message to close the WebGLThreads and release all WebGLContexts.
    pub fn exit(&self) -> Result<(), &'static str> {
        self.0
            .send(WebGLMsg::Exit)
            .map_err(|_| "Failed to send Exit message")
    }
}

/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads.
struct WebGLExternalImages {
    webrender_gl: Rc<dyn gl::Gl>,
    webgl_channel: WebGLSender<WebGLMsg>,
    // Used to avoid creating a new channel on each received WebRender request.
    lock_channel: (
        WebGLSender<(u32, Size2D<i32>, usize)>,
        WebGLReceiver<(u32, Size2D<i32>, usize)>,
    ),
}

impl WebGLExternalImages {
    fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
        Self {
            webrender_gl,
            webgl_channel: channel,
            lock_channel: webgl_channel().unwrap(),
        }
    }
}

impl WebGLExternalImageApi for WebGLExternalImages {
    fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>) {
        // WebGL Thread has it's own GL command queue that we need to synchronize with the WR GL command queue.
        // The WebGLMsg::Lock message inserts a fence in the WebGL command queue.
        self.webgl_channel
            .send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone()))
            .unwrap();
        let (image_id, size, gl_sync) = self.lock_channel.1.recv().unwrap();
        // The next glWaitSync call is run on the WR thread and it's used to synchronize the two
        // flows of OpenGL commands in order to avoid WR using a semi-ready WebGL texture.
        // glWaitSync doesn't block WR thread, it affects only internal OpenGL subsystem.
        self.webrender_gl
            .wait_sync(gl_sync as gl::GLsync, 0, gl::TIMEOUT_IGNORED);
        (image_id, size)
    }

    fn unlock(&mut self, ctx_id: WebGLContextId) {
        self.webgl_channel.send(WebGLMsg::Unlock(ctx_id)).unwrap();
    }
}

/// Wrapper to send WebVR commands used in `WebGLThread`.
struct WebVRRenderWrapper(Box<dyn WebVRRenderHandler>);

impl WebVRRenderHandler for WebVRRenderWrapper {
    fn handle(
        &mut self,
        gl: &dyn gl::Gl,
        command: WebVRCommand,
        texture: Option<(u32, Size2D<i32>)>,
    ) {
        self.0.handle(gl, command, texture);
    }
}

/// struct used to implement DOMToTexture feature and webrender::OutputImageHandler trait.
type OutputHandlerData = Option<(u32, Size2D<i32>)>;
struct OutputHandler {
    webrender_gl: Rc<dyn gl::Gl>,
    webgl_channel: WebGLSender<WebGLMsg>,
    // Used to avoid creating a new channel on each received WebRender request.
    lock_channel: (
        WebGLSender<OutputHandlerData>,
        WebGLReceiver<OutputHandlerData>,
    ),
    sync_objects: FnvHashMap<webrender_api::PipelineId, gl::GLsync>,
}

impl OutputHandler {
    fn new(webrender_gl: Rc<dyn gl::Gl>, channel: WebGLSender<WebGLMsg>) -> Self {
        Self {
            webrender_gl,
            webgl_channel: channel,
            lock_channel: webgl_channel().unwrap(),
            sync_objects: Default::default(),
        }
    }
}

/// Bridge between the WR frame outputs and WebGL to implement DOMToTexture synchronization.
impl webrender::OutputImageHandler for OutputHandler {
    fn lock(
        &mut self,
        id: webrender_api::PipelineId,
    ) -> Option<(u32, webrender_api::DeviceIntSize)> {
        // Insert a fence in the WR command queue
        let gl_sync = self
            .webrender_gl
            .fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
        // The lock command adds a WaitSync call on the WebGL command flow.
        let command = DOMToTextureCommand::Lock(id, gl_sync as usize, self.lock_channel.0.clone());
        self.webgl_channel
            .send(WebGLMsg::DOMToTextureCommand(command))
            .unwrap();
        self.lock_channel.1.recv().unwrap().map(|(tex_id, size)| {
            (
                tex_id,
                webrender_api::DeviceIntSize::new(size.width, size.height),
            )
        })
    }

    fn unlock(&mut self, id: webrender_api::PipelineId) {
        if let Some(gl_sync) = self.sync_objects.remove(&id) {
            // Flush the Sync object into the GPU's command queue to guarantee that it it's signaled.
            self.webrender_gl.flush();
            // Mark the sync object for deletion.
            self.webrender_gl.delete_sync(gl_sync);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 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 serde_json::{self,
                 json,
                 Value as Json};
use std::{fs::{create_dir_all,
               read_to_string,
               File},
          io::Write,
          path::Path};
use toml::Value;

use crate::{common::{templating::TemplateRenderer,
                     ui::{Status,
                          UIWriter,
                          UI}},
            error::Result};

pub fn start(ui: &mut UI,
             template_path: &Path,
             default_toml_path: &Path,
             user_toml_path: Option<&Path>,
             mock_data_path: Option<&Path>,
             print: bool,
             render: bool,
             render_dir: &Path,
             quiet: bool)
             -> Result<()> {
    // Strip the file name out of our passed template
    let file_name = match Path::new(&template_path).file_name() {
        Some(name) => Path::new(name),
        None => {
            panic!(format!("Something went wrong getting filename of {:?}",
                           &template_path))
        }
    };

    if !(quiet) {
        ui.begin(format!("Rendering: {:?} into: {:?} as: {:?}",
                         template_path, render_dir, file_name))?;
        ui.br()?;
    }

    // read our template from file
    let template = read_to_string(&template_path)?;

    // create a "data" json struct
    let mut data = json!({});

    if !(quiet) {
        // import default.toml values, convert to JSON
        ui.begin(format!("Importing default.toml: {:?}", &default_toml_path))?;
    }

    // we should always have a default.toml, would be nice to "autodiscover" based on package name,
    // for now assume we're working in the plan dir if --default-toml not passed
    let default_toml = read_to_string(&default_toml_path)?;

    // merge default into data struct
    merge(&mut data, toml_to_json(&default_toml)?);

    // import default.toml values, convert to JSON
    let user_toml = match user_toml_path {
        Some(path) => {
            if !(quiet) {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing user.toml: {:?}", path))?;
            }
            read_to_string(path)?
        }
        None => String::new(),
    };
    // merge default into data struct
    merge(&mut data, toml_to_json(&user_toml)?);

    // read mock data if provided
    let mock_data = match mock_data_path {
        Some(path) => {
            if !(quiet) {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing override file: {:?}", path))?;
            }
            read_to_string(path)?
        }
        // return an empty json block if '--mock-data' isn't defined.
        // this allows us to merge an empty JSON block
        None => "{}".to_string(),
    };
    // merge mock data into data
    merge(&mut data, serde_json::from_str(&mock_data)?);

    // create a template renderer
    let mut renderer = TemplateRenderer::new();
    // register our template
    renderer.register_template_string(&template, &template)
            .expect("Could not register template content");
    // render our JSON override in our template.
    let rendered_template = renderer.render(&template, &data)?;

    if print {
        if !(quiet) {
            ui.br()?;
            ui.warn(format!("###======== Rendered template: {:?}", &template_path))?;
        }

        println!("{}", rendered_template);

        if !(quiet) {
            ui.warn(format!("========### End rendered template: {:?}", &template_path))?;
        }
    }

    if render {
        // Render our template file
        create_with_template(ui, &render_dir, &file_name, &rendered_template, quiet)?;
    }

    if !(quiet) {
        ui.br()?;
    }
    Ok(())
}

fn toml_to_json(cfg: &str) -> Result<Json> {
    let toml_value = cfg.parse::<Value>()?;
    let toml_string = serde_json::to_string(&toml_value)?;
    let json = serde_json::from_str(&format!(r#"{{ "cfg": {} }}"#, &toml_string))?;
    Ok(json)
}

// merge two Json structs
fn merge(a: &mut Json, b: Json) {
    if let Json::Object(a_map) = a {
        if let Json::Object(b_map) = b {
            for (k, v) in b_map {
                merge(a_map.entry(k).or_insert(Json::Null), v);
            }
            return;
        }
    }
    *a = b;
}

fn create_with_template(ui: &mut UI,
                        render_dir: &std::path::Path,
                        file_name: &std::path::Path,
                        template: &str,
                        quiet: bool)
                        -> Result<()> {
    let path = Path::new(&render_dir).join(&file_name);
    if !(quiet) {
        ui.status(Status::Creating, format!("file: {:?}", path))?;
    }

    create_dir_all(render_dir)?;

    // Write file to disk
    File::create(path).and_then(|mut file| file.write(template.as_bytes()))?;
    Ok(())
}
<commit_msg>!(quiet) to !quiet<commit_after>// Copyright (c) 2019 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 serde_json::{self,
                 json,
                 Value as Json};
use std::{fs::{create_dir_all,
               read_to_string,
               File},
          io::Write,
          path::Path};
use toml::Value;

use crate::{common::{templating::TemplateRenderer,
                     ui::{Status,
                          UIWriter,
                          UI}},
            error::Result};

pub fn start(ui: &mut UI,
             template_path: &Path,
             default_toml_path: &Path,
             user_toml_path: Option<&Path>,
             mock_data_path: Option<&Path>,
             print: bool,
             render: bool,
             render_dir: &Path,
             quiet: bool)
             -> Result<()> {
    // Strip the file name out of our passed template
    let file_name = match Path::new(&template_path).file_name() {
        Some(name) => Path::new(name),
        None => {
            panic!(format!("Something went wrong getting filename of {:?}",
                           &template_path))
        }
    };

    if !quiet {
        ui.begin(format!("Rendering: {:?} into: {:?} as: {:?}",
                         template_path, render_dir, file_name))?;
        ui.br()?;
    }

    // read our template from file
    let template = read_to_string(&template_path)?;

    // create a "data" json struct
    let mut data = json!({});

    if !quiet {
        // import default.toml values, convert to JSON
        ui.begin(format!("Importing default.toml: {:?}", &default_toml_path))?;
    }

    // we should always have a default.toml, would be nice to "autodiscover" based on package name,
    // for now assume we're working in the plan dir if --default-toml not passed
    let default_toml = read_to_string(&default_toml_path)?;

    // merge default into data struct
    merge(&mut data, toml_to_json(&default_toml)?);

    // import default.toml values, convert to JSON
    let user_toml = match user_toml_path {
        Some(path) => {
            if !quiet {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing user.toml: {:?}", path))?;
            }
            read_to_string(path)?
        }
        None => String::new(),
    };
    // merge default into data struct
    merge(&mut data, toml_to_json(&user_toml)?);

    // read mock data if provided
    let mock_data = match mock_data_path {
        Some(path) => {
            if !quiet {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing override file: {:?}", path))?;
            }
            read_to_string(path)?
        }
        // return an empty json block if '--mock-data' isn't defined.
        // this allows us to merge an empty JSON block
        None => "{}".to_string(),
    };
    // merge mock data into data
    merge(&mut data, serde_json::from_str(&mock_data)?);

    // create a template renderer
    let mut renderer = TemplateRenderer::new();
    // register our template
    renderer.register_template_string(&template, &template)
            .expect("Could not register template content");
    // render our JSON override in our template.
    let rendered_template = renderer.render(&template, &data)?;

    if print {
        if !quiet {
            ui.br()?;
            ui.warn(format!("###======== Rendered template: {:?}", &template_path))?;
        }

        println!("{}", rendered_template);

        if !quiet {
            ui.warn(format!("========### End rendered template: {:?}", &template_path))?;
        }
    }

    if render {
        // Render our template file
        create_with_template(ui, &render_dir, &file_name, &rendered_template, quiet)?;
    }

    if !quiet {
        ui.br()?;
    }
    Ok(())
}

fn toml_to_json(cfg: &str) -> Result<Json> {
    let toml_value = cfg.parse::<Value>()?;
    let toml_string = serde_json::to_string(&toml_value)?;
    let json = serde_json::from_str(&format!(r#"{{ "cfg": {} }}"#, &toml_string))?;
    Ok(json)
}

// merge two Json structs
fn merge(a: &mut Json, b: Json) {
    if let Json::Object(a_map) = a {
        if let Json::Object(b_map) = b {
            for (k, v) in b_map {
                merge(a_map.entry(k).or_insert(Json::Null), v);
            }
            return;
        }
    }
    *a = b;
}

fn create_with_template(ui: &mut UI,
                        render_dir: &std::path::Path,
                        file_name: &std::path::Path,
                        template: &str,
                        quiet: bool)
                        -> Result<()> {
    let path = Path::new(&render_dir).join(&file_name);
    if !quiet {
        ui.status(Status::Creating, format!("file: {:?}", path))?;
    }

    create_dir_all(render_dir)?;

    // Write file to disk
    File::create(path).and_then(|mut file| file.write(template.as_bytes()))?;
    Ok(())
}
<|endoftext|>


<commit_before>use super::bls::PublicKey;
use super::Hash256;
use crate::test_utils::TestRandom;
use rand::RngCore;
use ssz::{Decodable, DecodeError, Encodable, SszStream};
use std::convert;

const STATUS_FLAG_INITIATED_EXIT: u8 = 1;
const STATUS_FLAG_WITHDRAWABLE: u8 = 2;

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum StatusFlags {
    InitiatedExit,
    Withdrawable,
}

impl convert::From<u8> for StatusFlags {
    fn from(status_flag: u8) -> Self {
        match status_flag {
            STATUS_FLAG_INITIATED_EXIT => StatusFlags::InitiatedExit,
            STATUS_FLAG_WITHDRAWABLE => StatusFlags::Withdrawable,
            _ => unreachable!(),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ValidatorRecord {
    pub pubkey: PublicKey,
    pub withdrawal_credentials: Hash256,
    pub randao_commitment: Hash256,
    pub randao_layers: u64,
    pub activation_slot: u64,
    pub exit_slot: u64,
    pub withdrawal_slot: u64,
    pub penalized_slot: u64,
    pub exit_count: u64,
    pub status_flags: StatusFlags,
    pub custody_commitment: Hash256,
    pub latest_custody_reseed_slot: u64,
    pub penultimate_custody_reseed_slot: u64,
}

impl Encodable for StatusFlags {
    fn ssz_append(&self, s: &mut SszStream) {
        let byte: u8 = match self {
            StatusFlags::InitiatedExit => STATUS_FLAG_INITIATED_EXIT,
            StatusFlags::Withdrawable => STATUS_FLAG_WITHDRAWABLE,
        };
        s.append(&byte);
    }
}

impl Decodable for StatusFlags {
    fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
        let (byte, i) = u8::ssz_decode(bytes, i)?;
        let status = match byte {
            1 => StatusFlags::InitiatedExit,
            2 => StatusFlags::Withdrawable,
            _ => return Err(DecodeError::Invalid),
        };
        Ok((status, i))
    }
}

impl<T: RngCore> TestRandom<T> for StatusFlags {
    fn random_for_test(rng: &mut T) -> Self {
        let options = vec![StatusFlags::InitiatedExit, StatusFlags::Withdrawable];
        options[(rng.next_u32() as usize) % options.len()].clone()
    }
}

impl Encodable for ValidatorRecord {
    fn ssz_append(&self, s: &mut SszStream) {
        s.append(&self.pubkey);
        s.append(&self.withdrawal_credentials);
        s.append(&self.randao_commitment);
        s.append(&self.randao_layers);
        s.append(&self.activation_slot);
        s.append(&self.exit_slot);
        s.append(&self.withdrawal_slot);
        s.append(&self.penalized_slot);
        s.append(&self.exit_count);
        s.append(&self.status_flags);
        s.append(&self.custody_commitment);
        s.append(&self.latest_custody_reseed_slot);
        s.append(&self.penultimate_custody_reseed_slot);
    }
}

impl Decodable for ValidatorRecord {
    fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
        let (pubkey, i) = <_>::ssz_decode(bytes, i)?;
        let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?;
        let (randao_commitment, i) = <_>::ssz_decode(bytes, i)?;
        let (randao_layers, i) = <_>::ssz_decode(bytes, i)?;
        let (activation_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (exit_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (withdrawal_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (penalized_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (exit_count, i) = <_>::ssz_decode(bytes, i)?;
        let (status_flags, i) = <_>::ssz_decode(bytes, i)?;
        let (custody_commitment, i) = <_>::ssz_decode(bytes, i)?;
        let (latest_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (penultimate_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;

        Ok((
            Self {
                pubkey,
                withdrawal_credentials,
                randao_commitment,
                randao_layers,
                activation_slot,
                exit_slot,
                withdrawal_slot,
                penalized_slot,
                exit_count,
                status_flags,
                custody_commitment,
                latest_custody_reseed_slot,
                penultimate_custody_reseed_slot,
            },
            i,
        ))
    }
}

impl<T: RngCore> TestRandom<T> for ValidatorRecord {
    fn random_for_test(rng: &mut T) -> Self {
        Self {
            pubkey: <_>::random_for_test(rng),
            withdrawal_credentials: <_>::random_for_test(rng),
            randao_commitment: <_>::random_for_test(rng),
            randao_layers: <_>::random_for_test(rng),
            activation_slot: <_>::random_for_test(rng),
            exit_slot: <_>::random_for_test(rng),
            withdrawal_slot: <_>::random_for_test(rng),
            penalized_slot: <_>::random_for_test(rng),
            exit_count: <_>::random_for_test(rng),
            status_flags: <_>::random_for_test(rng),
            custody_commitment: <_>::random_for_test(rng),
            latest_custody_reseed_slot: <_>::random_for_test(rng),
            penultimate_custody_reseed_slot: <_>::random_for_test(rng),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::ssz::ssz_encode;
    use super::*;
    use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};

    #[test]
    pub fn test_ssz_round_trip() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let original = ValidatorRecord::random_for_test(&mut rng);

        let bytes = ssz_encode(&original);
        let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();

        assert_eq!(original, decoded);
    }

    #[test]
    pub fn test_validator_status_ssz_round_trip() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let original = StatusFlags::random_for_test(&mut rng);

        let bytes = ssz_encode(&original);
        let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();

        assert_eq!(original, decoded);
    }
}
<commit_msg>Covers the 'missing' `StatusFlag` when the value is `0`.<commit_after>use super::bls::PublicKey;
use super::Hash256;
use crate::test_utils::TestRandom;
use rand::RngCore;
use ssz::{Decodable, DecodeError, Encodable, SszStream};
use std::convert;

const STATUS_FLAG_INITIATED_EXIT: u8 = 1;
const STATUS_FLAG_WITHDRAWABLE: u8 = 2;

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum StatusFlags {
    InitiatedExit,
    Withdrawable,
}

impl convert::From<u8> for StatusFlags {
    fn from(status_flag: u8) -> Self {
        match status_flag {
            STATUS_FLAG_INITIATED_EXIT => StatusFlags::InitiatedExit,
            STATUS_FLAG_WITHDRAWABLE => StatusFlags::Withdrawable,
            _ => unreachable!(),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ValidatorRecord {
    pub pubkey: PublicKey,
    pub withdrawal_credentials: Hash256,
    pub randao_commitment: Hash256,
    pub randao_layers: u64,
    pub activation_slot: u64,
    pub exit_slot: u64,
    pub withdrawal_slot: u64,
    pub penalized_slot: u64,
    pub exit_count: u64,
    pub status_flags: Option<StatusFlags>,
    pub custody_commitment: Hash256,
    pub latest_custody_reseed_slot: u64,
    pub penultimate_custody_reseed_slot: u64,
}

impl Encodable for StatusFlags {
    fn ssz_append(&self, s: &mut SszStream) {
        let byte: u8 = match self {
            StatusFlags::InitiatedExit => STATUS_FLAG_INITIATED_EXIT,
            StatusFlags::Withdrawable => STATUS_FLAG_WITHDRAWABLE,
        };
        s.append(&byte);
    }
}

impl Decodable for StatusFlags {
    fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
        let (byte, i) = u8::ssz_decode(bytes, i)?;
        let status = match byte {
            1 => StatusFlags::InitiatedExit,
            2 => StatusFlags::Withdrawable,
            _ => return Err(DecodeError::Invalid),
        };
        Ok((status, i))
    }
}

impl<T: RngCore> TestRandom<T> for StatusFlags {
    fn random_for_test(rng: &mut T) -> Self {
        let options = vec![StatusFlags::InitiatedExit, StatusFlags::Withdrawable];
        options[(rng.next_u32() as usize) % options.len()].clone()
    }
}

impl Encodable for ValidatorRecord {
    fn ssz_append(&self, s: &mut SszStream) {
        s.append(&self.pubkey);
        s.append(&self.withdrawal_credentials);
        s.append(&self.randao_commitment);
        s.append(&self.randao_layers);
        s.append(&self.activation_slot);
        s.append(&self.exit_slot);
        s.append(&self.withdrawal_slot);
        s.append(&self.penalized_slot);
        s.append(&self.exit_count);
        if let Some(status_flags) = self.status_flags {
            s.append(&status_flags);
        } else {
            s.append(&(0 as u8));
        }
        s.append(&self.custody_commitment);
        s.append(&self.latest_custody_reseed_slot);
        s.append(&self.penultimate_custody_reseed_slot);
    }
}

impl Decodable for ValidatorRecord {
    fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
        let (pubkey, i) = <_>::ssz_decode(bytes, i)?;
        let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?;
        let (randao_commitment, i) = <_>::ssz_decode(bytes, i)?;
        let (randao_layers, i) = <_>::ssz_decode(bytes, i)?;
        let (activation_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (exit_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (withdrawal_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (penalized_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (exit_count, i) = <_>::ssz_decode(bytes, i)?;
        let (status_flags_byte, i): (u8, usize) = <_>::ssz_decode(bytes, i)?;
        let (custody_commitment, i) = <_>::ssz_decode(bytes, i)?;
        let (latest_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (penultimate_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;

        let status_flags = if status_flags_byte == 0u8 {
            None
        } else {
            Some(StatusFlags::from(status_flags_byte))
        };

        Ok((
            Self {
                pubkey,
                withdrawal_credentials,
                randao_commitment,
                randao_layers,
                activation_slot,
                exit_slot,
                withdrawal_slot,
                penalized_slot,
                exit_count,
                status_flags,
                custody_commitment,
                latest_custody_reseed_slot,
                penultimate_custody_reseed_slot,
            },
            i,
        ))
    }
}

impl<T: RngCore> TestRandom<T> for ValidatorRecord {
    fn random_for_test(rng: &mut T) -> Self {
        Self {
            pubkey: <_>::random_for_test(rng),
            withdrawal_credentials: <_>::random_for_test(rng),
            randao_commitment: <_>::random_for_test(rng),
            randao_layers: <_>::random_for_test(rng),
            activation_slot: <_>::random_for_test(rng),
            exit_slot: <_>::random_for_test(rng),
            withdrawal_slot: <_>::random_for_test(rng),
            penalized_slot: <_>::random_for_test(rng),
            exit_count: <_>::random_for_test(rng),
            status_flags: Some(<_>::random_for_test(rng)),
            custody_commitment: <_>::random_for_test(rng),
            latest_custody_reseed_slot: <_>::random_for_test(rng),
            penultimate_custody_reseed_slot: <_>::random_for_test(rng),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::ssz::ssz_encode;
    use super::*;
    use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};

    #[test]
    pub fn test_ssz_round_trip() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let original = ValidatorRecord::random_for_test(&mut rng);

        let bytes = ssz_encode(&original);
        let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();

        assert_eq!(original, decoded);
    }

    #[test]
    pub fn test_validator_status_ssz_round_trip() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let original = StatusFlags::random_for_test(&mut rng);

        let bytes = ssz_encode(&original);
        let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();

        assert_eq!(original, decoded);
    }
}
<|endoftext|>


<commit_before>struct Processor {
	accumulator:     u8,
	index_x:         u8,
	index_y:         u8,
	stack_pointer:   u8,
	program_counter: u16,
	status:          u8,
}

impl Processor {
}

#[deriving(PartialEq)]
#[deriving(Eq)]
#[deriving(PartialOrd)]
#[deriving(Ord)]
struct Address(u16);

impl Address {
	fn to_int(&self) -> u16 {
		match *self {
			Address(address_) => address_
		}
	}

	fn min() -> Address { Address(0x0100) }
	fn max() -> Address { Address(0x01ff) }

	fn is_valid(&self) -> bool {
		Address::min() <= *self && *self <= Address::max()
	}
}

struct Memory {
	bytes: [u8,.. 256]
}

impl Memory {
	fn address_to_byte_offset(address: &Address) -> uint {
		(address.to_int() - Address::min().to_int()) as uint
	}

	fn get_byte(&self, address: &Address) -> u8 {
		if !address.is_valid()
		{
			fail!("Invalid address.");
		}
		else
		{
			return self.bytes[Memory::address_to_byte_offset(address)];
		}
	}

	// Sets the byte at the given address to the given value and returns the
	// previous value at the address.
	fn set_byte(&mut self, address: &Address, value: u8) -> u8 {
		if !address.is_valid()
		{
			fail!("Invalid address.");
		}
		else
		{
			let old_value = self.get_byte(address);
			self.bytes[Memory::address_to_byte_offset(address)] = value;

			return old_value;
		}
	}
}

fn main() {
	println!("Hello, world!")
}
<commit_msg>Add add_with_carry().<commit_after>mod Registers {
	// Each status flag should be 0 or 1.
	pub struct Status {
		pub carry:        u8,
		pub zero:         u8,
		pub interrupt:    u8,
		pub decimal_mode: u8,
		pub brk:          u8,
		pub unused:       u8,
		pub overflow:     u8,
		pub sign:         u8
	}

	impl Status {
		pub fn to_byte(&self) -> u8 {
			  self.carry        << 0
			| self.zero         << 1
			| self.interrupt    << 2
			| self.decimal_mode << 3
			| self.brk          << 4
			| self.unused       << 5
			| self.overflow     << 6
			| self.sign         << 7
		}
	}

	pub struct Registers {
		pub accumulator:     i8,
		pub index_x:         i8,
		pub index_y:         i8,
		pub stack_pointer:   u8,
		pub program_counter: u16,
		pub status:          Status
	}

	impl Registers {
	}
}

mod Address {
	#[deriving(PartialEq)]
	#[deriving(Eq)]
	#[deriving(PartialOrd)]
	#[deriving(Ord)]
	pub struct Address(u16);

	impl Address {
		/* TODO akeeton: Hide struct Address(u16) "constructor."
		pub fn new(address_: u16) -> Address {
			Address(address_)
		}
		*/

		pub fn to_int(&self) -> u16 {
			match *self {
				Address(address_) => address_
			}
		}

		pub fn min() -> Address { Address(0x0100) }
		pub fn max() -> Address { Address(0x01ff) }

		pub fn is_valid(&self) -> bool {
			Address::min() <= *self && *self <= Address::max()
		}
	}
}

mod Memory {
	use Address::Address;

	pub struct Memory {
		bytes: [u8,.. 256]
	}

	impl Memory {
		fn address_to_byte_offset(address: &Address) -> uint {
			(address.to_int() - Address::min().to_int()) as uint
		}

		pub fn get_byte(&self, address: &Address) -> u8 {
			if !address.is_valid()
			{
				fail!("Invalid address.");
			}
			else
			{
				return self.bytes[Memory::address_to_byte_offset(address)];
			}
		}

		// Sets the byte at the given address to the given value and returns the
		// previous value at the address.
		pub fn set_byte(&mut self, address: &Address, value: u8) -> u8 {
			if !address.is_valid()
			{
				fail!("Invalid address.");
			}
			else
			{
				let old_value = self.get_byte(address);
				self.bytes[Memory::address_to_byte_offset(address)] = value;

				return old_value;
			}
		}
	}
}

mod Machine {
	use Registers::Registers;
	use Address::Address;
	use Memory::Memory;

	struct Machine {
		registers: Registers,
		memory:    Memory
	}

	impl Machine {
		// TODO akeeton: Implement binary-coded decimal.
		pub fn add_with_carry(&mut self, value: i8) {
			let a: int = self.registers.accumulator  as int;
			let c: int = self.registers.status.carry as int;

			let a_new_full: int = a + c + value as int;
			let a_new:      i8  = a_new_full as i8;

			self.registers.accumulator     = a_new;
			self.registers.status.carry    = if a_new_full == a_new as int { 0 } else { 1 };
			self.registers.status.zero     = if a_new == 0 { 1 } else { 0 };
			self.registers.status.sign     = if a_new  < 0 { 1 } else { 0 };
			self.registers.status.overflow =
				if (a < 0 && value < 0 && a_new >= 0)
				|| (a > 0 && value > 0 && a_new <= 0)
				{ 1 }
				else
				{ 0 }
		}
	}
}

fn main() {
	println!("Hello, world!")
}
<|endoftext|>


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

//! Values for CSS Box Alignment properties
//!
//! https://drafts.csswg.org/css-align/

use std::fmt;
use style_traits::{CssWriter, ToCss};
use values::computed::{Context, ToComputedValue};
use values::specified;

pub use super::specified::{AlignContent, JustifyContent, AlignItems, SelfAlignment};
pub use super::specified::{AlignSelf, JustifySelf};

/// The computed value for the `justify-items` property.
///
/// Need to carry around both the specified and computed value to handle the
/// special legacy keyword. Sigh.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct JustifyItems {
    /// The specified value for the property. Can contain `auto`.
    pub specified: specified::JustifyItems,
    /// The computed value for the property. Cannot contain `auto`.
    pub computed: specified::JustifyItems,
}

impl ToCss for JustifyItems {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
        where W: fmt::Write,
    {
        self.computed.to_css(dest)
    }
}

impl JustifyItems {
    /// Returns the `auto` value.
    pub fn auto() -> Self {
        Self {
            specified: specified::JustifyItems::auto(),
            computed: specified::JustifyItems::normal(),
        }
    }
}

impl ToComputedValue for specified::JustifyItems {
    type ComputedValue = JustifyItems;

    /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>
    fn to_computed_value(&self, _context: &Context) -> JustifyItems {
        use values::specified::align;
        let specified = *self;
        let computed =
            if self.0 != align::AlignFlags::AUTO {
                *self
            } else {
                // If the inherited value of `justify-items` includes the
                // `legacy` keyword, `auto` computes to the inherited value,
                // but we assume it computes to `normal`, and handle that
                // special-case in StyleAdjuster.
                Self::normal()
            };

        JustifyItems { specified, computed }
    }

    #[inline]
    fn from_computed_value(computed: &JustifyItems) -> Self {
        computed.specified
    }
}
<commit_msg>Auto merge of #20210 - emilio:justify-items-kinda-sucks, r=Manishearth<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Values for CSS Box Alignment properties
//!
//! https://drafts.csswg.org/css-align/

use values::computed::{Context, ToComputedValue};
use values::specified;

pub use super::specified::{AlignContent, JustifyContent, AlignItems, SelfAlignment};
pub use super::specified::{AlignSelf, JustifySelf};

/// The computed value for the `justify-items` property.
///
/// Need to carry around both the specified and computed value to handle the
/// special legacy keyword without destroying style sharing.
///
/// In particular, `justify-items` is a reset property, so we ought to be able
/// to share its computed representation across elements as long as they match
/// the same rules. Except that it's not true if the specified value for
/// `justify-items` is `auto` and the computed value of the parent has the
/// `legacy` modifier.
///
/// So instead of computing `auto` "normally" looking at get_parent_position(),
/// marking it as uncacheable, we carry the specified value around and handle
/// the special case in `StyleAdjuster` instead, only when the result of the
/// computation would vary.
///
/// Note that we also need to special-case this property in matching.rs, in
/// order to properly handle changes to the legacy keyword... This all kinda
/// sucks :(.
///
/// See the discussion in https://bugzil.la/1384542.
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)]
pub struct JustifyItems {
    /// The specified value for the property. Can contain `auto`.
    #[css(skip)]
    pub specified: specified::JustifyItems,
    /// The computed value for the property. Cannot contain `auto`.
    pub computed: specified::JustifyItems,
}

impl JustifyItems {
    /// Returns the `auto` value.
    pub fn auto() -> Self {
        Self {
            specified: specified::JustifyItems::auto(),
            computed: specified::JustifyItems::normal(),
        }
    }
}

impl ToComputedValue for specified::JustifyItems {
    type ComputedValue = JustifyItems;

    /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>
    fn to_computed_value(&self, _context: &Context) -> JustifyItems {
        use values::specified::align;
        let specified = *self;
        let computed =
            if self.0 != align::AlignFlags::AUTO {
                *self
            } else {
                // If the inherited value of `justify-items` includes the
                // `legacy` keyword, `auto` computes to the inherited value,
                // but we assume it computes to `normal`, and handle that
                // special-case in StyleAdjuster.
                Self::normal()
            };

        JustifyItems { specified, computed }
    }

    #[inline]
    fn from_computed_value(computed: &JustifyItems) -> Self {
        computed.specified
    }
}
<|endoftext|>


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

//! Generic types for the handling of [images].
//!
//! [images]: https://drafts.csswg.org/css-images/#image-values

use Atom;
use cssparser::serialize_identifier;
use custom_properties;
use std::fmt;
use style_traits::ToCss;
use values::computed::{Context, ToComputedValue};

/// An [image].
///
/// [image]: https://drafts.csswg.org/css-images/#image-values
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Image<Gradient, MozImageRect, ImageUrl> {
    /// A `<url()>` image.
    Url(ImageUrl),
    /// A `<gradient>` image.  Gradients are rather large, and not nearly as
    /// common as urls, so we box them here to keep the size of this enum sane.
    Gradient(Box<Gradient>),
    /// A `-moz-image-rect` image.  Also fairly large and rare.
    Rect(Box<MozImageRect>),
    /// A `-moz-element(# <element-id>)`
    Element(Atom),
    /// A paint worklet image.
    /// https://drafts.css-houdini.org/css-paint-api/
    #[cfg(feature = "servo")]
    PaintWorklet(PaintWorklet),
}

// FIXME(nox): Implement TCV for Box<T> and derive this.
impl<Gradient: ToComputedValue,
     MozImageRect: ToComputedValue,
     ImageUrl: ToComputedValue> ToComputedValue for Image<Gradient, MozImageRect, ImageUrl> {
    type ComputedValue = Image<<Gradient as ToComputedValue>::ComputedValue,
                               <MozImageRect as ToComputedValue>::ComputedValue,
                               <ImageUrl as ToComputedValue>::ComputedValue>;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            Image::Url(ref url) => Image::Url(url.to_computed_value(context)),
            Image::Gradient(ref gradient) =>
                Image::Gradient(Box::new(gradient.to_computed_value(context))),
            Image::Rect(ref rect) => Image::Rect(Box::new(rect.to_computed_value(context))),
            Image::Element(ref atom) => Image::Element(atom.to_computed_value(context)),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref worklet) => Image::PaintWorklet(worklet.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            Image::Url(ref url) => Image::Url(ImageUrl::from_computed_value(url)),
            Image::Gradient(ref boxed_gradient) =>
                Image::Gradient(Box::new(Gradient::from_computed_value(&*boxed_gradient))),
            Image::Rect(ref boxed_rect) =>
                Image::Rect(Box::new(MozImageRect::from_computed_value(&*boxed_rect))),
            Image::Element(ref atom) => Image::Element(Atom::from_computed_value(atom)),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref worklet) =>
                Image::PaintWorklet(PaintWorklet::from_computed_value(worklet)),
        }
    }
}

/// A CSS gradient.
/// https://drafts.csswg.org/css-images/#gradients
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> {
    /// Gradients can be linear or radial.
    pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
    /// The color stops and interpolation hints.
    pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
    /// True if this is a repeating gradient.
    #[compute(clone)]
    pub repeating: bool,
    /// Compatibility mode.
    #[compute(clone)]
    pub compat_mode: CompatMode,
}

#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
pub enum CompatMode {
    /// Modern syntax.
    Modern,
    /// `-webkit` prefix.
    WebKit,
    /// `-moz` prefix
    Moz,
}

/// A gradient kind.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> {
    /// A linear gradient.
    Linear(LineDirection),
    /// A radial gradient.
    Radial(EndingShape<Length, LengthOrPercentage>, Position, Option<Angle>),
}

/// A radial gradient's ending shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum EndingShape<Length, LengthOrPercentage> {
    /// A circular gradient.
    Circle(Circle<Length>),
    /// An elliptic gradient.
    Ellipse(Ellipse<LengthOrPercentage>),
}

/// A circle shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Circle<Length> {
    /// A circle radius.
    Radius(Length),
    /// A circle extent.
    Extent(ShapeExtent),
}

/// An ellipse shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Ellipse<LengthOrPercentage> {
    /// An ellipse pair of radii.
    Radii(LengthOrPercentage, LengthOrPercentage),
    /// An ellipse extent.
    Extent(ShapeExtent),
}

/// https://drafts.csswg.org/css-images/#typedef-extent-keyword
define_css_keyword_enum!(ShapeExtent:
    "closest-side" => ClosestSide,
    "farthest-side" => FarthestSide,
    "closest-corner" => ClosestCorner,
    "farthest-corner" => FarthestCorner,
    "contain" => Contain,
    "cover" => Cover
);
add_impls_for_keyword_enum!(ShapeExtent);

/// A gradient item.
/// https://drafts.csswg.org/css-images-4/#color-stop-syntax
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
pub enum GradientItem<Color, LengthOrPercentage> {
    /// A color stop.
    ColorStop(ColorStop<Color, LengthOrPercentage>),
    /// An interpolation hint.
    InterpolationHint(LengthOrPercentage),
}

/// A color stop.
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
#[derive(Clone, Copy, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ColorStop<Color, LengthOrPercentage> {
    /// The color of this stop.
    pub color: Color,
    /// The position of this stop.
    pub position: Option<LengthOrPercentage>,
}

/// Specified values for a paint worklet.
/// https://drafts.css-houdini.org/css-paint-api/
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PaintWorklet {
    /// The name the worklet was registered with.
    pub name: Atom,
    /// The arguments for the worklet.
    /// TODO: store a parsed representation of the arguments.
    pub arguments: Vec<custom_properties::SpecifiedValue>,
}

trivial_to_computed_value!(PaintWorklet);

impl ToCss for PaintWorklet {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str("paint(")?;
        serialize_identifier(&*self.name.to_string(), dest)?;
        for argument in &self.arguments {
            dest.write_str(", ")?;
            argument.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

/// Values for `moz-image-rect`.
///
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[css(comma, function)]
#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
    pub url: MozImageRectUrl,
    pub top: NumberOrPercentage,
    pub right: NumberOrPercentage,
    pub bottom: NumberOrPercentage,
    pub left: NumberOrPercentage,
}

impl<G, R, U> fmt::Debug for Image<G, R, U>
    where G: fmt::Debug, R: fmt::Debug, U: fmt::Debug + ToCss
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Image::Url(ref url) => url.to_css(f),
            Image::Gradient(ref grad) => grad.fmt(f),
            Image::Rect(ref rect) => rect.fmt(f),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.fmt(f),
            Image::Element(ref selector) => {
                f.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), f)?;
                f.write_str(")")
            },
        }
    }
}

impl<G, R, U> ToCss for Image<G, R, U>
    where G: ToCss, R: ToCss, U: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Image::Url(ref url) => url.to_css(dest),
            Image::Gradient(ref gradient) => gradient.to_css(dest),
            Image::Rect(ref rect) => rect.to_css(dest),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
            Image::Element(ref selector) => {
                dest.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), dest)?;
                dest.write_str(")")
            },
        }
    }
}

impl<D, L, LoP, P, C, A> ToCss for Gradient<D, L, LoP, P, C, A>
    where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, A: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.compat_mode {
            CompatMode::WebKit => dest.write_str("-webkit-")?,
            CompatMode::Moz => dest.write_str("-moz-")?,
            _ => {},
        }

        if self.repeating {
            dest.write_str("repeating-")?;
        }
        dest.write_str(self.kind.label())?;
        dest.write_str("-gradient(")?;
        let mut skip_comma = match self.kind {
            GradientKind::Linear(ref direction)
                if direction.points_downwards(self.compat_mode) => true,
            GradientKind::Linear(ref direction) => {
                direction.to_css(dest, self.compat_mode)?;
                false
            },
            GradientKind::Radial(ref shape, ref position, ref angle) => {
                let omit_shape = match *shape {
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
                        true
                    },
                    _ => false,
                };
                if self.compat_mode == CompatMode::Modern {
                    if !omit_shape {
                        shape.to_css(dest)?;
                        dest.write_str(" ")?;
                    }
                    dest.write_str("at ")?;
                    position.to_css(dest)?;
                } else {
                    position.to_css(dest)?;
                    if let Some(ref a) = *angle {
                        dest.write_str(" ")?;
                        a.to_css(dest)?;
                    }
                    if !omit_shape {
                        dest.write_str(", ")?;
                        shape.to_css(dest)?;
                    }
                }
                false
            },
        };
        for item in &self.items {
            if !skip_comma {
                dest.write_str(", ")?;
            }
            skip_comma = false;
            item.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

impl<D, L, LoP, P, A> GradientKind<D, L, LoP, P, A> {
    fn label(&self) -> &str {
        match *self {
            GradientKind::Linear(..) => "linear",
            GradientKind::Radial(..) => "radial",
        }
    }
}

/// The direction of a linear gradient.
pub trait LineDirection {
    /// Whether this direction points towards, and thus can be omitted.
    fn points_downwards(&self, compat_mode: CompatMode) -> bool;

    /// Serialises this direction according to the compatibility mode.
    fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
        where W: fmt::Write;
}

impl<L> ToCss for Circle<L>
where
    L: ToCss,
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    where
        W: fmt::Write,
    {
        match *self {
            Circle::Extent(ShapeExtent::FarthestCorner) |
            Circle::Extent(ShapeExtent::Cover) => {
                dest.write_str("circle")
            },
            Circle::Extent(keyword) => {
                dest.write_str("circle ")?;
                keyword.to_css(dest)
            },
            Circle::Radius(ref length) => {
                length.to_css(dest)
            },
        }
    }
}

impl<C, L> fmt::Debug for ColorStop<C, L>
    where C: fmt::Debug, L: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.color)?;
        if let Some(ref pos) = self.position {
            write!(f, " {:?}", pos)?;
        }
        Ok(())
    }
}
<commit_msg>Derive ToComputedValue for Image<..><commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Generic types for the handling of [images].
//!
//! [images]: https://drafts.csswg.org/css-images/#image-values

use Atom;
use cssparser::serialize_identifier;
use custom_properties;
use std::fmt;
use style_traits::ToCss;

/// An [image].
///
/// [image]: https://drafts.csswg.org/css-images/#image-values
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, PartialEq, ToComputedValue)]
pub enum Image<Gradient, MozImageRect, ImageUrl> {
    /// A `<url()>` image.
    Url(ImageUrl),
    /// A `<gradient>` image.  Gradients are rather large, and not nearly as
    /// common as urls, so we box them here to keep the size of this enum sane.
    Gradient(Box<Gradient>),
    /// A `-moz-image-rect` image.  Also fairly large and rare.
    Rect(Box<MozImageRect>),
    /// A `-moz-element(# <element-id>)`
    Element(Atom),
    /// A paint worklet image.
    /// https://drafts.css-houdini.org/css-paint-api/
    #[cfg(feature = "servo")]
    PaintWorklet(PaintWorklet),
}

/// A CSS gradient.
/// https://drafts.csswg.org/css-images/#gradients
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> {
    /// Gradients can be linear or radial.
    pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
    /// The color stops and interpolation hints.
    pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
    /// True if this is a repeating gradient.
    #[compute(clone)]
    pub repeating: bool,
    /// Compatibility mode.
    #[compute(clone)]
    pub compat_mode: CompatMode,
}

#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
pub enum CompatMode {
    /// Modern syntax.
    Modern,
    /// `-webkit` prefix.
    WebKit,
    /// `-moz` prefix
    Moz,
}

/// A gradient kind.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> {
    /// A linear gradient.
    Linear(LineDirection),
    /// A radial gradient.
    Radial(EndingShape<Length, LengthOrPercentage>, Position, Option<Angle>),
}

/// A radial gradient's ending shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum EndingShape<Length, LengthOrPercentage> {
    /// A circular gradient.
    Circle(Circle<Length>),
    /// An elliptic gradient.
    Ellipse(Ellipse<LengthOrPercentage>),
}

/// A circle shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Circle<Length> {
    /// A circle radius.
    Radius(Length),
    /// A circle extent.
    Extent(ShapeExtent),
}

/// An ellipse shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Ellipse<LengthOrPercentage> {
    /// An ellipse pair of radii.
    Radii(LengthOrPercentage, LengthOrPercentage),
    /// An ellipse extent.
    Extent(ShapeExtent),
}

/// https://drafts.csswg.org/css-images/#typedef-extent-keyword
define_css_keyword_enum!(ShapeExtent:
    "closest-side" => ClosestSide,
    "farthest-side" => FarthestSide,
    "closest-corner" => ClosestCorner,
    "farthest-corner" => FarthestCorner,
    "contain" => Contain,
    "cover" => Cover
);
add_impls_for_keyword_enum!(ShapeExtent);

/// A gradient item.
/// https://drafts.csswg.org/css-images-4/#color-stop-syntax
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
pub enum GradientItem<Color, LengthOrPercentage> {
    /// A color stop.
    ColorStop(ColorStop<Color, LengthOrPercentage>),
    /// An interpolation hint.
    InterpolationHint(LengthOrPercentage),
}

/// A color stop.
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
#[derive(Clone, Copy, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ColorStop<Color, LengthOrPercentage> {
    /// The color of this stop.
    pub color: Color,
    /// The position of this stop.
    pub position: Option<LengthOrPercentage>,
}

/// Specified values for a paint worklet.
/// https://drafts.css-houdini.org/css-paint-api/
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PaintWorklet {
    /// The name the worklet was registered with.
    pub name: Atom,
    /// The arguments for the worklet.
    /// TODO: store a parsed representation of the arguments.
    pub arguments: Vec<custom_properties::SpecifiedValue>,
}

trivial_to_computed_value!(PaintWorklet);

impl ToCss for PaintWorklet {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str("paint(")?;
        serialize_identifier(&*self.name.to_string(), dest)?;
        for argument in &self.arguments {
            dest.write_str(", ")?;
            argument.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

/// Values for `moz-image-rect`.
///
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[css(comma, function)]
#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
    pub url: MozImageRectUrl,
    pub top: NumberOrPercentage,
    pub right: NumberOrPercentage,
    pub bottom: NumberOrPercentage,
    pub left: NumberOrPercentage,
}

impl<G, R, U> fmt::Debug for Image<G, R, U>
    where G: fmt::Debug, R: fmt::Debug, U: fmt::Debug + ToCss
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Image::Url(ref url) => url.to_css(f),
            Image::Gradient(ref grad) => grad.fmt(f),
            Image::Rect(ref rect) => rect.fmt(f),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.fmt(f),
            Image::Element(ref selector) => {
                f.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), f)?;
                f.write_str(")")
            },
        }
    }
}

impl<G, R, U> ToCss for Image<G, R, U>
    where G: ToCss, R: ToCss, U: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Image::Url(ref url) => url.to_css(dest),
            Image::Gradient(ref gradient) => gradient.to_css(dest),
            Image::Rect(ref rect) => rect.to_css(dest),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
            Image::Element(ref selector) => {
                dest.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), dest)?;
                dest.write_str(")")
            },
        }
    }
}

impl<D, L, LoP, P, C, A> ToCss for Gradient<D, L, LoP, P, C, A>
    where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, A: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.compat_mode {
            CompatMode::WebKit => dest.write_str("-webkit-")?,
            CompatMode::Moz => dest.write_str("-moz-")?,
            _ => {},
        }

        if self.repeating {
            dest.write_str("repeating-")?;
        }
        dest.write_str(self.kind.label())?;
        dest.write_str("-gradient(")?;
        let mut skip_comma = match self.kind {
            GradientKind::Linear(ref direction)
                if direction.points_downwards(self.compat_mode) => true,
            GradientKind::Linear(ref direction) => {
                direction.to_css(dest, self.compat_mode)?;
                false
            },
            GradientKind::Radial(ref shape, ref position, ref angle) => {
                let omit_shape = match *shape {
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
                        true
                    },
                    _ => false,
                };
                if self.compat_mode == CompatMode::Modern {
                    if !omit_shape {
                        shape.to_css(dest)?;
                        dest.write_str(" ")?;
                    }
                    dest.write_str("at ")?;
                    position.to_css(dest)?;
                } else {
                    position.to_css(dest)?;
                    if let Some(ref a) = *angle {
                        dest.write_str(" ")?;
                        a.to_css(dest)?;
                    }
                    if !omit_shape {
                        dest.write_str(", ")?;
                        shape.to_css(dest)?;
                    }
                }
                false
            },
        };
        for item in &self.items {
            if !skip_comma {
                dest.write_str(", ")?;
            }
            skip_comma = false;
            item.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

impl<D, L, LoP, P, A> GradientKind<D, L, LoP, P, A> {
    fn label(&self) -> &str {
        match *self {
            GradientKind::Linear(..) => "linear",
            GradientKind::Radial(..) => "radial",
        }
    }
}

/// The direction of a linear gradient.
pub trait LineDirection {
    /// Whether this direction points towards, and thus can be omitted.
    fn points_downwards(&self, compat_mode: CompatMode) -> bool;

    /// Serialises this direction according to the compatibility mode.
    fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
        where W: fmt::Write;
}

impl<L> ToCss for Circle<L>
where
    L: ToCss,
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    where
        W: fmt::Write,
    {
        match *self {
            Circle::Extent(ShapeExtent::FarthestCorner) |
            Circle::Extent(ShapeExtent::Cover) => {
                dest.write_str("circle")
            },
            Circle::Extent(keyword) => {
                dest.write_str("circle ")?;
                keyword.to_css(dest)
            },
            Circle::Radius(ref length) => {
                length.to_css(dest)
            },
        }
    }
}

impl<C, L> fmt::Debug for ColorStop<C, L>
    where C: fmt::Debug, L: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.color)?;
        if let Some(ref pos) = self.position {
            write!(f, " {:?}", pos)?;
        }
        Ok(())
    }
}
<|endoftext|>