<commit_before>// Copyright 2016 Serde YAML Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;

use dtoa;
use linked_hash_map::LinkedHashMap;
use serde::{self, Serialize, Deserialize};
use yaml_rust::Yaml;

use error::Error;
use de::Deserializer;
use ser::Serializer;

#[derive(Clone, PartialOrd, Debug)]
pub enum Value {
    Null,
    Bool(bool),
    I64(i64),
    F64(f64),
    String(String),
    Sequence(Sequence),
    Mapping(Mapping),
}

pub type Sequence = Vec<Value>;
pub type Mapping = LinkedHashMap<Value, Value>;

/// Shortcut function to encode a `T` into a YAML `Value`.
///
/// ```rust
/// use serde_yaml::{Value, to_value};
/// let val = to_value("foo");
/// assert_eq!(val, Value::String("foo".to_owned()))
/// ```
pub fn to_value<T: ?Sized>(value: &T) -> Result<Value, Error>
    where T: Serialize
{
    value.serialize(Serializer).map(Into::into)
}

/// Shortcut function to decode a YAML `Value` into a `T`.
///
/// ```rust
/// use serde_yaml::{Value, from_value};
/// let val = Value::String("foo".to_owned());
/// assert_eq!("foo", from_value::<String>(val).unwrap());
/// ```
pub fn from_value<T>(value: Value) -> Result<T, Error>
    where T: Deserialize
{
    Deserialize::deserialize(Deserializer::new(value.into()))
}

impl Value {
    pub fn is_null(&self) -> bool {
        if let Value::Null = *self {
            true
        } else {
            false
        }
    }

    pub fn is_bool(&self) -> bool {
        self.as_bool().is_some()
    }

    pub fn as_bool(&self) -> Option<bool> {
        match *self {
            Value::Bool(b) => Some(b),
            _ => None,
        }
    }

    pub fn is_i64(&self) -> bool {
        self.as_i64().is_some()
    }

    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            Value::I64(i) => Some(i),
            _ => None,
        }
    }

    pub fn is_f64(&self) -> bool {
        self.as_f64().is_some()
    }

    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            Value::F64(i) => Some(i),
            _ => None,
        }
    }

    pub fn is_string(&self) -> bool {
        self.as_str().is_some()
    }

    pub fn as_str(&self) -> Option<&str> {
        match *self {
            Value::String(ref s) => Some(s),
            _ => None,
        }
    }

    pub fn is_sequence(&self) -> bool {
        self.as_sequence().is_some()
    }

    pub fn as_sequence(&self) -> Option<&Sequence> {
        match *self {
            Value::Sequence(ref seq) => Some(seq),
            _ => None,
        }
    }

    pub fn as_sequence_mut(&mut self) -> Option<&mut Sequence> {
        match *self {
            Value::Sequence(ref mut seq) => Some(seq),
            _ => None,
        }
    }

    pub fn is_mapping(&self) -> bool {
        self.as_mapping().is_some()
    }

    pub fn as_mapping(&self) -> Option<&Mapping> {
        match *self {
            Value::Mapping(ref map) => Some(map),
            _ => None,
        }
    }

    pub fn as_mapping_mut(&mut self) -> Option<&mut Mapping> {
        match *self {
            Value::Mapping(ref mut map) => Some(map),
            _ => None,
        }
    }
}

impl From<Yaml> for Value {
    fn from(yaml: Yaml) -> Self {
        match yaml {
            Yaml::Real(f) => {
                match f.parse() {
                    Ok(f) => Value::F64(f),
                    Err(_) => Value::String(f),
                }
            }
            Yaml::Integer(i) => Value::I64(i),
            Yaml::String(s) => Value::String(s),
            Yaml::Boolean(b) => Value::Bool(b),
            Yaml::Array(array) =>  {
                Value::Sequence(array.into_iter()
                                     .map(Into::into)
                                     .collect())
            }
            Yaml::Hash(hash) => {
                Value::Mapping(hash.into_iter()
                                   .map(|(k, v)| (k.into(), v.into()))
                                   .collect())
            }
            Yaml::Alias(_) => panic!("alias unsupported"),
            Yaml::Null => Value::Null,
            Yaml::BadValue => panic!("bad value"),
        }
    }
}

impl From<Value> for Yaml {
    fn from(value: Value) -> Self {
        match value {
            Value::Null => Yaml::Null,
            Value::Bool(b) => Yaml::Boolean(b),
            Value::I64(i) => Yaml::Integer(i),
            Value::F64(f) => {
                let mut buf = Vec::new();
                dtoa::write(&mut buf, f).unwrap();
                Yaml::Real(String::from_utf8(buf).unwrap())
            }
            Value::String(s) => Yaml::String(s),
            Value::Sequence(seq) => {
                Yaml::Array(seq.into_iter()
                               .map(Into::into)
                               .collect())
            }
            Value::Mapping(map) => {
                Yaml::Hash(map.into_iter()
                              .map(|(k, v)| (k.into(), v.into()))
                              .collect())
            }
        }
    }
}

impl Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: serde::Serializer
    {
        match *self {
            Value::Null => serializer.serialize_unit(),
            Value::Bool(b) => serializer.serialize_bool(b),
            Value::I64(i) => serializer.serialize_i64(i),
            Value::F64(f) => serializer.serialize_f64(f),
            Value::String(ref s) => serializer.serialize_str(s),
            Value::Sequence(ref seq) => seq.serialize(serializer),
            Value::Mapping(ref hash) => {
                use serde::ser::SerializeMap;
                let mut map = try!(serializer.serialize_map(Some(hash.len())));
                for (k, v) in hash {
                    try!(map.serialize_key(k));
                    try!(map.serialize_value(v));
                }
                map.end()
            }
        }
    }
}

impl Deserialize for Value {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: serde::Deserializer
    {
        struct ValueVisitor;

        impl serde::de::Visitor for ValueVisitor {
            type Value = Value;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("any YAML value")
            }

            fn visit_bool<E>(self, b: bool) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Bool(b))
            }

            fn visit_i64<E>(self, i: i64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::I64(i))
            }

            fn visit_u64<E>(self, u: u64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::I64(u as i64))
            }

            fn visit_f64<E>(self, f: f64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::F64(f))
            }

            fn visit_str<E>(self, s: &str) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::String(s.to_owned()))
            }

            fn visit_string<E>(self, s: String) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::String(s))
            }

            fn visit_unit<E>(self) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Null)
            }

            fn visit_none<E>(self) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Null)
            }

            fn visit_some<D>(
                self,
                deserializer: D
            ) -> Result<Value, D::Error>
                where D: serde::Deserializer
            {
                Deserialize::deserialize(deserializer)
            }

            fn visit_seq<V>(self, visitor: V) -> Result<Value, V::Error>
                where V: serde::de::SeqVisitor
            {
                use serde::de::impls::VecVisitor;
                let values = try!(VecVisitor::new().visit_seq(visitor));
                Ok(Value::Sequence(values))
            }

            fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
                where V: serde::de::MapVisitor
            {
                let mut values = LinkedHashMap::with_capacity(visitor.size_hint().0);

                while let Some((key, value)) = try!(visitor.visit()) {
                    values.insert(key, value);
                }

                Ok(Value::Mapping(values))
            }
        }

        deserializer.deserialize(ValueVisitor)
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Value) -> bool {
        match (self, other) {
            (&Value::Null, &Value::Null) => true,
            (&Value::Bool(a), &Value::Bool(b)) => a == b,
            (&Value::I64(a), &Value::I64(b)) => a == b,
            (&Value::F64(a), &Value::F64(b)) => {
                if a.is_nan() && b.is_nan() {
                    // compare NaN for bitwise equality
                    let (a, b): (i64, i64) = unsafe {
                        (mem::transmute(a), mem::transmute(b))
                    };
                    a == b
                } else {
                    a == b
                }
            }
            (&Value::String(ref a), &Value::String(ref b)) => a == b,
            (&Value::Sequence(ref a), &Value::Sequence(ref b)) => a == b,
            (&Value::Mapping(ref a), &Value::Mapping(ref b)) => a == b,
            _ => false,
        }
    }
}

impl Eq for Value {}

impl Hash for Value {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            Value::Null => 0.hash(state),
            Value::Bool(b) => (1, b).hash(state),
            Value::I64(i) => (2, i).hash(state),
            Value::F64(_) => {
                // you should feel bad for using f64 as a map key
                3.hash(state);
            }
            Value::String(ref s) => (4, s).hash(state),
            Value::Sequence(ref seq) => (5, seq).hash(state),
            Value::Mapping(ref map) => (6, map).hash(state),
        }
    }
}
<commit_msg>Fix to_value doc tests<commit_after>// Copyright 2016 Serde YAML Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;

use dtoa;
use linked_hash_map::LinkedHashMap;
use serde::{self, Serialize, Deserialize};
use yaml_rust::Yaml;

use error::Error;
use de::Deserializer;
use ser::Serializer;

#[derive(Clone, PartialOrd, Debug)]
pub enum Value {
    Null,
    Bool(bool),
    I64(i64),
    F64(f64),
    String(String),
    Sequence(Sequence),
    Mapping(Mapping),
}

pub type Sequence = Vec<Value>;
pub type Mapping = LinkedHashMap<Value, Value>;

/// Shortcut function to encode a `T` into a YAML `Value`.
///
/// ```rust
/// use serde_yaml::{Value, to_value};
/// let val = to_value("foo").unwrap();
/// assert_eq!(val, Value::String("foo".to_owned()))
/// ```
pub fn to_value<T: ?Sized>(value: &T) -> Result<Value, Error>
    where T: Serialize
{
    value.serialize(Serializer).map(Into::into)
}

/// Shortcut function to decode a YAML `Value` into a `T`.
///
/// ```rust
/// use serde_yaml::{Value, from_value};
/// let val = Value::String("foo".to_owned());
/// assert_eq!("foo", from_value::<String>(val).unwrap());
/// ```
pub fn from_value<T>(value: Value) -> Result<T, Error>
    where T: Deserialize
{
    Deserialize::deserialize(Deserializer::new(value.into()))
}

impl Value {
    pub fn is_null(&self) -> bool {
        if let Value::Null = *self {
            true
        } else {
            false
        }
    }

    pub fn is_bool(&self) -> bool {
        self.as_bool().is_some()
    }

    pub fn as_bool(&self) -> Option<bool> {
        match *self {
            Value::Bool(b) => Some(b),
            _ => None,
        }
    }

    pub fn is_i64(&self) -> bool {
        self.as_i64().is_some()
    }

    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            Value::I64(i) => Some(i),
            _ => None,
        }
    }

    pub fn is_f64(&self) -> bool {
        self.as_f64().is_some()
    }

    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            Value::F64(i) => Some(i),
            _ => None,
        }
    }

    pub fn is_string(&self) -> bool {
        self.as_str().is_some()
    }

    pub fn as_str(&self) -> Option<&str> {
        match *self {
            Value::String(ref s) => Some(s),
            _ => None,
        }
    }

    pub fn is_sequence(&self) -> bool {
        self.as_sequence().is_some()
    }

    pub fn as_sequence(&self) -> Option<&Sequence> {
        match *self {
            Value::Sequence(ref seq) => Some(seq),
            _ => None,
        }
    }

    pub fn as_sequence_mut(&mut self) -> Option<&mut Sequence> {
        match *self {
            Value::Sequence(ref mut seq) => Some(seq),
            _ => None,
        }
    }

    pub fn is_mapping(&self) -> bool {
        self.as_mapping().is_some()
    }

    pub fn as_mapping(&self) -> Option<&Mapping> {
        match *self {
            Value::Mapping(ref map) => Some(map),
            _ => None,
        }
    }

    pub fn as_mapping_mut(&mut self) -> Option<&mut Mapping> {
        match *self {
            Value::Mapping(ref mut map) => Some(map),
            _ => None,
        }
    }
}

impl From<Yaml> for Value {
    fn from(yaml: Yaml) -> Self {
        match yaml {
            Yaml::Real(f) => {
                match f.parse() {
                    Ok(f) => Value::F64(f),
                    Err(_) => Value::String(f),
                }
            }
            Yaml::Integer(i) => Value::I64(i),
            Yaml::String(s) => Value::String(s),
            Yaml::Boolean(b) => Value::Bool(b),
            Yaml::Array(array) =>  {
                Value::Sequence(array.into_iter()
                                     .map(Into::into)
                                     .collect())
            }
            Yaml::Hash(hash) => {
                Value::Mapping(hash.into_iter()
                                   .map(|(k, v)| (k.into(), v.into()))
                                   .collect())
            }
            Yaml::Alias(_) => panic!("alias unsupported"),
            Yaml::Null => Value::Null,
            Yaml::BadValue => panic!("bad value"),
        }
    }
}

impl From<Value> for Yaml {
    fn from(value: Value) -> Self {
        match value {
            Value::Null => Yaml::Null,
            Value::Bool(b) => Yaml::Boolean(b),
            Value::I64(i) => Yaml::Integer(i),
            Value::F64(f) => {
                let mut buf = Vec::new();
                dtoa::write(&mut buf, f).unwrap();
                Yaml::Real(String::from_utf8(buf).unwrap())
            }
            Value::String(s) => Yaml::String(s),
            Value::Sequence(seq) => {
                Yaml::Array(seq.into_iter()
                               .map(Into::into)
                               .collect())
            }
            Value::Mapping(map) => {
                Yaml::Hash(map.into_iter()
                              .map(|(k, v)| (k.into(), v.into()))
                              .collect())
            }
        }
    }
}

impl Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: serde::Serializer
    {
        match *self {
            Value::Null => serializer.serialize_unit(),
            Value::Bool(b) => serializer.serialize_bool(b),
            Value::I64(i) => serializer.serialize_i64(i),
            Value::F64(f) => serializer.serialize_f64(f),
            Value::String(ref s) => serializer.serialize_str(s),
            Value::Sequence(ref seq) => seq.serialize(serializer),
            Value::Mapping(ref hash) => {
                use serde::ser::SerializeMap;
                let mut map = try!(serializer.serialize_map(Some(hash.len())));
                for (k, v) in hash {
                    try!(map.serialize_key(k));
                    try!(map.serialize_value(v));
                }
                map.end()
            }
        }
    }
}

impl Deserialize for Value {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: serde::Deserializer
    {
        struct ValueVisitor;

        impl serde::de::Visitor for ValueVisitor {
            type Value = Value;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("any YAML value")
            }

            fn visit_bool<E>(self, b: bool) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Bool(b))
            }

            fn visit_i64<E>(self, i: i64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::I64(i))
            }

            fn visit_u64<E>(self, u: u64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::I64(u as i64))
            }

            fn visit_f64<E>(self, f: f64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::F64(f))
            }

            fn visit_str<E>(self, s: &str) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::String(s.to_owned()))
            }

            fn visit_string<E>(self, s: String) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::String(s))
            }

            fn visit_unit<E>(self) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Null)
            }

            fn visit_none<E>(self) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Null)
            }

            fn visit_some<D>(
                self,
                deserializer: D
            ) -> Result<Value, D::Error>
                where D: serde::Deserializer
            {
                Deserialize::deserialize(deserializer)
            }

            fn visit_seq<V>(self, visitor: V) -> Result<Value, V::Error>
                where V: serde::de::SeqVisitor
            {
                use serde::de::impls::VecVisitor;
                let values = try!(VecVisitor::new().visit_seq(visitor));
                Ok(Value::Sequence(values))
            }

            fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
                where V: serde::de::MapVisitor
            {
                let mut values = LinkedHashMap::with_capacity(visitor.size_hint().0);

                while let Some((key, value)) = try!(visitor.visit()) {
                    values.insert(key, value);
                }

                Ok(Value::Mapping(values))
            }
        }

        deserializer.deserialize(ValueVisitor)
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Value) -> bool {
        match (self, other) {
            (&Value::Null, &Value::Null) => true,
            (&Value::Bool(a), &Value::Bool(b)) => a == b,
            (&Value::I64(a), &Value::I64(b)) => a == b,
            (&Value::F64(a), &Value::F64(b)) => {
                if a.is_nan() && b.is_nan() {
                    // compare NaN for bitwise equality
                    let (a, b): (i64, i64) = unsafe {
                        (mem::transmute(a), mem::transmute(b))
                    };
                    a == b
                } else {
                    a == b
                }
            }
            (&Value::String(ref a), &Value::String(ref b)) => a == b,
            (&Value::Sequence(ref a), &Value::Sequence(ref b)) => a == b,
            (&Value::Mapping(ref a), &Value::Mapping(ref b)) => a == b,
            _ => false,
        }
    }
}

impl Eq for Value {}

impl Hash for Value {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            Value::Null => 0.hash(state),
            Value::Bool(b) => (1, b).hash(state),
            Value::I64(i) => (2, i).hash(state),
            Value::F64(_) => {
                // you should feel bad for using f64 as a map key
                3.hash(state);
            }
            Value::String(ref s) => (4, s).hash(state),
            Value::Sequence(ref seq) => (5, seq).hash(state),
            Value::Mapping(ref map) => (6, map).hash(state),
        }
    }
}
<|endoftext|>


<commit_before>//! Plugin which transforms `dc::File` to point at local clones of GitHub
//! repositories.

use docker_compose::v2 as dc;
use std::marker::PhantomData;

use ext::service::ServiceExt;
use plugins;
use plugins::{Operation, PluginNew, PluginTransform};
use project::Project;
use util::{ConductorPathExt, Error};

/// Transforms `dc::File` to point at local clones of GitHub
//! repositories.
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct Plugin {
    /// Placeholder field for future hidden fields, to keep this from being
    /// directly constructable.
    _placeholder: PhantomData<()>,
}

impl plugins::Plugin for Plugin {
    fn name(&self) -> &'static str {
        Self::plugin_name()
    }
}

impl PluginNew for Plugin {
    fn plugin_name() -> &'static str {
        "repos"
    }

    fn new(_project: &Project) -> Result<Self, Error> {
        Ok(Plugin { _placeholder: PhantomData })
    }
}

impl PluginTransform for Plugin {
    fn transform(&self,
                 op: Operation,
                 ctx: &plugins::Context,
                 file: &mut dc::File)
                 -> Result<(), Error> {
        // Give up immediately if we're not doing this for local output.
        if op != Operation::Output {
            return Ok(());
        }

        // Update each service to point to our locally cloned repos.
        let project = ctx.project;
        for service in &mut file.services.values_mut() {

            // Handle the main repo associated with this service.
            if let Some(git_url) = try!(service.git_url()).cloned() {
                if let Some(repo) = project.repos().find_by_git_url(&git_url) {
                    if repo.is_cloned(project) {
                        // Build an absolute path to our repo's clone directory.
                        let path = try!(repo.path(project).to_absolute());

                        // Mount the local build directory inside the
                        // container.
                        let srcdir = try!(service.source_mount_dir());
                        let mount = dc::VolumeMount::host(&path, &srcdir);
                        service.volumes.push(dc::value(mount));

                        // Update the `build` field if present.
                        if let Some(ref mut build) = service.build {
                            build.context = dc::value(dc::Context::Dir(path));
                        }
                    }
                }
            }

            // Look for library repos as well.
            for (label, mount_as) in &service.labels {
                let prefix = "io.fdy.conductor.lib.";
                if label.starts_with(prefix) {
                    let key = &label[prefix.len()..];
                    let repo = try!(project.repos()
                        .find_by_lib_key(key)
                        .ok_or_else(|| {
                            err!("no library <{}> defined in `config/libraries.yml`",
                                 key)
                        }));

                    let path = try!(repo.path(project).to_absolute());
                    let mount = dc::VolumeMount::host(&path, mount_as);
                    service.volumes.push(dc::value(mount));
                }
            }
        }

        Ok(())
    }
}
<commit_msg>Fix a broken comment<commit_after>//! Plugin which transforms `dc::File` to point at local clones of GitHub
//! repositories.

use docker_compose::v2 as dc;
use std::marker::PhantomData;

use ext::service::ServiceExt;
use plugins;
use plugins::{Operation, PluginNew, PluginTransform};
use project::Project;
use util::{ConductorPathExt, Error};

/// Transforms `dc::File` to point at local clones of GitHub
/// repositories.
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct Plugin {
    /// Placeholder field for future hidden fields, to keep this from being
    /// directly constructable.
    _placeholder: PhantomData<()>,
}

impl plugins::Plugin for Plugin {
    fn name(&self) -> &'static str {
        Self::plugin_name()
    }
}

impl PluginNew for Plugin {
    fn plugin_name() -> &'static str {
        "repos"
    }

    fn new(_project: &Project) -> Result<Self, Error> {
        Ok(Plugin { _placeholder: PhantomData })
    }
}

impl PluginTransform for Plugin {
    fn transform(&self,
                 op: Operation,
                 ctx: &plugins::Context,
                 file: &mut dc::File)
                 -> Result<(), Error> {
        // Give up immediately if we're not doing this for local output.
        if op != Operation::Output {
            return Ok(());
        }

        // Update each service to point to our locally cloned repos.
        let project = ctx.project;
        for service in &mut file.services.values_mut() {

            // Handle the main repo associated with this service.
            if let Some(git_url) = try!(service.git_url()).cloned() {
                if let Some(repo) = project.repos().find_by_git_url(&git_url) {
                    if repo.is_cloned(project) {
                        // Build an absolute path to our repo's clone directory.
                        let path = try!(repo.path(project).to_absolute());

                        // Mount the local build directory inside the
                        // container.
                        let srcdir = try!(service.source_mount_dir());
                        let mount = dc::VolumeMount::host(&path, &srcdir);
                        service.volumes.push(dc::value(mount));

                        // Update the `build` field if present.
                        if let Some(ref mut build) = service.build {
                            build.context = dc::value(dc::Context::Dir(path));
                        }
                    }
                }
            }

            // Look for library repos as well.
            for (label, mount_as) in &service.labels {
                let prefix = "io.fdy.conductor.lib.";
                if label.starts_with(prefix) {
                    let key = &label[prefix.len()..];
                    let repo = try!(project.repos()
                        .find_by_lib_key(key)
                        .ok_or_else(|| {
                            err!("no library <{}> defined in `config/libraries.yml`",
                                 key)
                        }));

                    let path = try!(repo.path(project).to_absolute());
                    let mount = dc::VolumeMount::host(&path, mount_as);
                    service.volumes.push(dc::value(mount));
                }
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

use std::fmt;
use std::result;
use std::str::FromStr;

use regex::Regex;

use error::Error;

#[derive(Clone, Debug, Eq, Hash, PartialEq, RustcDecodable, RustcEncodable)]
pub struct ServiceGroup {
    pub service: String,
    pub group: String,
    pub organization: Option<String>,
}

impl ServiceGroup {
    pub fn new<T: Into<String>>(service: T, group: T, organization: Option<String>) -> Self {
        ServiceGroup {
            service: service.into(),
            group: group.into(),
            organization: organization,
        }
    }
}

impl fmt::Display for ServiceGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.organization.as_ref() {
            Some(org) => write!(f, "{}.{}@{}", self.service, self.group, org),
            None => write!(f, "{}.{}", self.service, self.group),
        }
    }
}

impl FromStr for ServiceGroup {
    type Err = Error;

    fn from_str(value: &str) -> result::Result<Self, Self::Err> {
        let regex = Regex::new(r"^([A-Za-z_0-9]+)\.([A-Za-z_0-9]+)(@([A-Za-z_0-9]+))?$").unwrap();
        let caps = match regex.captures(value) {
            Some(c) => c,
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let name = match caps.at(1) {
            Some(n) => n,
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };

        let group = match caps.at(2) {
            Some(n) => n,
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };

        // you can't specify a key with an "@", but without an org
        // ex: "foo.bar@"
        if value.ends_with('@') {
            return Err(Error::InvalidServiceGroup(value.to_string()));
        };

        Ok(ServiceGroup::new(name, group, caps.at(4).map(|s| s.to_string())))
    }
}

#[test]
fn service_groups_with_org() {
    let x = ServiceGroup::from_str("foo.bar").unwrap();
    assert!(x.service == "foo".to_string());
    assert!(x.group == "bar".to_string());
    assert!(x.organization.is_none());

    let y = ServiceGroup::from_str("foo.bar@baz").unwrap();
    assert!(y.service == "foo".to_string());
    assert!(y.group == "bar".to_string());
    assert!(y.organization.unwrap() == "baz");

    assert!(ServiceGroup::from_str("foo.bar@").is_err());
    assert!(ServiceGroup::from_str("f.oo.bar@baz").is_err());
    assert!(ServiceGroup::from_str("foo@baz").is_err());
}
<commit_msg>Support wider range of service group string parsing.<commit_after>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

use std::fmt;
use std::result;
use std::str::FromStr;

use regex::Regex;

use error::Error;

lazy_static! {
    static ref FROM_STR_RE: Regex =
        Regex::new(r"\A(?P<service>[^.]+)\.(?P<group>[^.@]+)(@(?P<organization>.+))?\z").unwrap();
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, RustcDecodable, RustcEncodable)]
pub struct ServiceGroup {
    pub service: String,
    pub group: String,
    pub organization: Option<String>,
}

impl ServiceGroup {
    pub fn new<S1, S2>(service: S1, group: S2, organization: Option<String>) -> Self
        where S1: Into<String>,
              S2: Into<String>
    {
        ServiceGroup {
            service: service.into(),
            group: group.into(),
            organization: organization,
        }
    }
}

impl fmt::Display for ServiceGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.organization.as_ref() {
            Some(org) => write!(f, "{}.{}@{}", self.service, self.group, org),
            None => write!(f, "{}.{}", self.service, self.group),
        }
    }
}

impl FromStr for ServiceGroup {
    type Err = Error;

    fn from_str(value: &str) -> result::Result<Self, Self::Err> {
        let caps = match FROM_STR_RE.captures(value) {
            Some(c) => c,
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let service = match caps.name("service") {
            Some(s) => s.to_string(),
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let group = match caps.name("group") {
            Some(g) => g.to_string(),
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let organization = match caps.name("organization") {
            Some(o) => Some(o.to_string()),
            None => None,
        };

        Ok(ServiceGroup::new(service, group, organization))
    }
}

#[cfg(test)]
mod test {
    use std::str::FromStr;

    use super::ServiceGroup;

    #[test]
    fn service_group_fields() {
        let sg = ServiceGroup {
            service: "kayla".to_string(),
            group: "album".to_string(),
            organization: Some("flying_colors".to_string()),
        };
        assert_eq!(sg.service, "kayla");
        assert_eq!(sg.group, "album");
        assert_eq!(sg.organization, Some("flying_colors".to_string()));
    }

    #[test]
    fn fmt_without_organization() {
        let sg = ServiceGroup::new("kayla", "album", None);
        assert_eq!(&sg.to_string(), "kayla.album");

        let sg = ServiceGroup::new("blue-ocean", "album-track", None);
        assert_eq!(&sg.to_string(), "blue-ocean.album-track");
    }

    #[test]
    fn fmt_with_organization() {
        let sg = ServiceGroup::new("kayla", "album", Some("flying_colors".to_string()));
        assert_eq!(&sg.to_string(), "kayla.album@flying_colors");

        let sg = ServiceGroup::new("blue-ocean", "album-track", Some("f-l_y".to_string()));
        assert_eq!(&sg.to_string(), "blue-ocean.album-track@f-l_y");
    }

    #[test]
    fn from_str_without_organization() {
        let expected = ServiceGroup::new("kayla", "album", None);
        let actual = ServiceGroup::from_str("kayla.album").unwrap();
        assert_eq!(expected, actual);

        let expected = ServiceGroup::new("blue-ocean", "track-from_album", None);
        let actual = ServiceGroup::from_str("blue-ocean.track-from_album").unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn from_str_with_organization() {
        let expected = ServiceGroup::new("kayla", "album", Some("flying_colors".to_string()));
        let actual = ServiceGroup::from_str("kayla.album@flying_colors").unwrap();
        assert_eq!(expected, actual);

        let expected = ServiceGroup::new("blue-ocean",
                                         "track-from_album",
                                         Some("f-l_y".to_string()));
        let actual = ServiceGroup::from_str("blue-ocean.track-from_album@f-l_y").unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    #[should_panic(expected = "not.allowed@")]
    fn from_str_ending_with_at() {
        ServiceGroup::from_str("not.allowed@").unwrap();
    }

    #[test]
    #[should_panic(expected = "only.one.period@allowed")]
    fn from_str_too_many_periods() {
        ServiceGroup::from_str("only.one.period@allowed").unwrap();
    }

    #[test]
    #[should_panic(expected = "oh-noes")]
    fn from_str_not_enough_periods() {
        ServiceGroup::from_str("oh-noes").unwrap();
    }
}
<|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/. */

use app_units::Au;
use azure::azure_hl::BackendType;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
use azure::scaled_font::FontInfo;
use azure::scaled_font::ScaledFont;
use fnv::FnvHasher;
use font::{Font, FontGroup, FontHandleMethods};
use font_cache_thread::FontCacheThread;
use font_template::FontTemplateDescriptor;
use heapsize::HeapSizeOf;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use smallvec::SmallVec;
use std::cell::RefCell;
use std::collections::HashMap;
use std::default::Default;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use string_cache::Atom;
use style::computed_values::{font_style, font_variant};
use style::properties::style_structs::ServoFont;
use webrender_traits;

#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
    ScaledFont::new(BackendType::Skia, FontInfo::FontData(&template.bytes),
                    pt_size.to_f32_px())
}

#[cfg(target_os = "macos")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
    let cgfont = template.ctfont(pt_size.to_f64_px()).as_ref().unwrap().copy_to_CGFont();
    ScaledFont::new(BackendType::Skia, &cgfont, pt_size.to_f32_px())
}

static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8;      // Matches FireFox (see gfxFont.h)

#[derive(Debug)]
struct LayoutFontCacheEntry {
    family: String,
    font: Option<Rc<RefCell<Font>>>,
}

#[derive(Debug)]
struct FallbackFontCacheEntry {
    font: Rc<RefCell<Font>>,
}

/// A cached azure font (per paint thread) that
/// can be shared by multiple text runs.
#[derive(Debug)]
struct PaintFontCacheEntry {
    pt_size: Au,
    identifier: Atom,
    font: Rc<RefCell<ScaledFont>>,
}

/// An epoch for the font context cache. The cache is flushed if the current epoch does not match
/// this one.
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;

/// The FontContext represents the per-thread/thread state necessary for
/// working with fonts. It is the public API used by the layout and
/// paint code. It talks directly to the font cache thread where
/// required.
#[derive(Debug)]
pub struct FontContext {
    platform_handle: FontContextHandle,
    font_cache_thread: FontCacheThread,

    /// TODO: See bug https://github.com/servo/servo/issues/3300.
    layout_font_cache: Vec<LayoutFontCacheEntry>,
    fallback_font_cache: Vec<FallbackFontCacheEntry>,

    /// Strong reference as the paint FontContext is (for now) recycled
    /// per frame. TODO: Make this weak when incremental redraw is done.
    paint_font_cache: Vec<PaintFontCacheEntry>,

    layout_font_group_cache:
        HashMap<LayoutFontGroupCacheKey, Rc<FontGroup>, BuildHasherDefault<FnvHasher>>,

    epoch: usize,
}

impl FontContext {
    pub fn new(font_cache_thread: FontCacheThread) -> FontContext {
        let handle = FontContextHandle::new();
        FontContext {
            platform_handle: handle,
            font_cache_thread: font_cache_thread,
            layout_font_cache: vec!(),
            fallback_font_cache: vec!(),
            paint_font_cache: vec!(),
            layout_font_group_cache: HashMap::with_hasher(Default::default()),
            epoch: 0,
        }
    }

    /// Create a font for use in layout calculations.
    fn create_layout_font(&self,
                          template: Arc<FontTemplateData>,
                          descriptor: FontTemplateDescriptor,
                          pt_size: Au,
                          variant: font_variant::T,
                          font_key: Option<webrender_traits::FontKey>) -> Result<Font, ()> {
        // TODO: (Bug #3463): Currently we only support fake small-caps
        // painting. We should also support true small-caps (where the
        // font supports it) in the future.
        let actual_pt_size = match variant {
            font_variant::T::small_caps => pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR),
            font_variant::T::normal => pt_size,
        };

        let handle: Result<FontHandle, _> =
            FontHandleMethods::new_from_template(&self.platform_handle, template,
                                                 Some(actual_pt_size));

        handle.map(|handle|
            Font::new(handle, variant, descriptor, pt_size, actual_pt_size, font_key))
    }

    fn expire_font_caches_if_necessary(&mut self) {
        let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
        if current_epoch == self.epoch {
            return
        }

        self.layout_font_cache.clear();
        self.fallback_font_cache.clear();
        self.paint_font_cache.clear();
        self.layout_font_group_cache.clear();
        self.epoch = current_epoch
    }

    /// Create a group of fonts for use in layout calculations. May return
    /// a cached font if this font instance has already been used by
    /// this context.
    pub fn layout_font_group_for_style(&mut self, style: Arc<ServoFont>)
                                       -> Rc<FontGroup> {
        self.expire_font_caches_if_necessary();

        let layout_font_group_cache_key = LayoutFontGroupCacheKey {
            pointer: style.clone(),
            size: style.font_size,
        };
        if let Some(ref cached_font_group) = self.layout_font_group_cache.get(
                &layout_font_group_cache_key) {
            return (*cached_font_group).clone()
        }

        // TODO: The font context holds a strong ref to the cached fonts
        // so they will never be released. Find out a good time to drop them.

        let desc = FontTemplateDescriptor::new(style.font_weight,
                                               style.font_stretch,
                                               style.font_style == font_style::T::italic ||
                                                style.font_style == font_style::T::oblique);

        let mut fonts: SmallVec<[Rc<RefCell<Font>>; 8]> = SmallVec::new();

        for family in &style.font_family.0 {
            // GWTODO: Check on real pages if this is faster as Vec() or HashMap().
            let mut cache_hit = false;
            for cached_font_entry in &self.layout_font_cache {
                if cached_font_entry.family == family.name() {
                    match cached_font_entry.font {
                        None => {
                            cache_hit = true;
                            break;
                        }
                        Some(ref cached_font_ref) => {
                            let cached_font = (*cached_font_ref).borrow();
                            if cached_font.descriptor == desc &&
                               cached_font.requested_pt_size == style.font_size &&
                               cached_font.variant == style.font_variant {
                                fonts.push((*cached_font_ref).clone());
                                cache_hit = true;
                                break;
                            }
                        }
                    }
                }
            }

            if !cache_hit {
                let template_info = self.font_cache_thread.find_font_template(family.clone(),
                                                                             desc.clone());
                match template_info {
                    Some(template_info) => {
                        let layout_font = self.create_layout_font(template_info.font_template,
                                                                  desc.clone(),
                                                                  style.font_size,
                                                                  style.font_variant,
                                                                  template_info.font_key);
                        let font = match layout_font {
                            Ok(layout_font) => {
                                let layout_font = Rc::new(RefCell::new(layout_font));
                                fonts.push(layout_font.clone());

                                Some(layout_font)
                            }
                            Err(_) => None
                        };

                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_owned(),
                            font: font
                        });
                    }
                    None => {
                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_owned(),
                            font: None,
                        });
                    }
                }
            }
        }

        // If unable to create any of the specified fonts, create one from the
        // list of last resort fonts for this platform.
        if fonts.is_empty() {
            let mut cache_hit = false;
            for cached_font_entry in &self.fallback_font_cache {
                let cached_font = cached_font_entry.font.borrow();
                if cached_font.descriptor == desc &&
                            cached_font.requested_pt_size == style.font_size &&
                            cached_font.variant == style.font_variant {
                    fonts.push(cached_font_entry.font.clone());
                    cache_hit = true;
                    break;
                }
            }

            if !cache_hit {
                let template_info = self.font_cache_thread.last_resort_font_template(desc.clone());
                let layout_font = self.create_layout_font(template_info.font_template,
                                                          desc.clone(),
                                                          style.font_size,
                                                          style.font_variant,
                                                          template_info.font_key);
                match layout_font {
                    Ok(layout_font) => {
                        let layout_font = Rc::new(RefCell::new(layout_font));
                        self.fallback_font_cache.push(FallbackFontCacheEntry {
                            font: layout_font.clone(),
                        });
                        fonts.push(layout_font);
                    }
                    Err(_) => debug!("Failed to create fallback layout font!")
                }
            }
        }

        let font_group = Rc::new(FontGroup::new(fonts));
        self.layout_font_group_cache.insert(layout_font_group_cache_key, font_group.clone());
        font_group
    }

    /// Create a paint font for use with azure. May return a cached
    /// reference if already used by this font context.
    pub fn paint_font_from_template(&mut self,
                                        template: &Arc<FontTemplateData>,
                                        pt_size: Au)
                                        -> Rc<RefCell<ScaledFont>> {
        for cached_font in &self.paint_font_cache {
            if cached_font.pt_size == pt_size &&
               cached_font.identifier == template.identifier {
                return cached_font.font.clone();
            }
        }

        let paint_font = Rc::new(RefCell::new(create_scaled_font(template, pt_size)));
        self.paint_font_cache.push(PaintFontCacheEntry {
            font: paint_font.clone(),
            pt_size: pt_size,
            identifier: template.identifier.clone(),
        });
        paint_font
    }

    /// Returns a reference to the font cache thread.
    pub fn font_cache_thread(&self) -> FontCacheThread {
        self.font_cache_thread.clone()
    }
}

impl HeapSizeOf for FontContext {
    fn heap_size_of_children(&self) -> usize {
        // FIXME(njn): Measure other fields eventually.
        self.platform_handle.heap_size_of_children()
    }
}

#[derive(Debug)]
struct LayoutFontGroupCacheKey {
    pointer: Arc<ServoFont>,
    size: Au,
}

impl PartialEq for LayoutFontGroupCacheKey {
    fn eq(&self, other: &LayoutFontGroupCacheKey) -> bool {
        self.pointer == other.pointer && self.size == other.size
    }
}

impl Eq for LayoutFontGroupCacheKey {}

impl Hash for LayoutFontGroupCacheKey {
    fn hash<H>(&self, hasher: &mut H) where H: Hasher {
        self.pointer.hash.hash(hasher)
    }
}

#[inline]
pub fn invalidate_font_caches() {
    FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
}
<commit_msg>Auto merge of #11229 - Ms2ger:create_layout_font, r=emilio<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/. */

use app_units::Au;
use azure::azure_hl::BackendType;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
use azure::scaled_font::FontInfo;
use azure::scaled_font::ScaledFont;
use fnv::FnvHasher;
use font::{Font, FontGroup, FontHandleMethods};
use font_cache_thread::FontCacheThread;
use font_template::FontTemplateDescriptor;
use heapsize::HeapSizeOf;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use smallvec::SmallVec;
use std::cell::RefCell;
use std::collections::HashMap;
use std::default::Default;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use string_cache::Atom;
use style::computed_values::{font_style, font_variant};
use style::properties::style_structs::ServoFont;
use webrender_traits;

#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
    ScaledFont::new(BackendType::Skia, FontInfo::FontData(&template.bytes),
                    pt_size.to_f32_px())
}

#[cfg(target_os = "macos")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
    let cgfont = template.ctfont(pt_size.to_f64_px()).as_ref().unwrap().copy_to_CGFont();
    ScaledFont::new(BackendType::Skia, &cgfont, pt_size.to_f32_px())
}

static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8;      // Matches FireFox (see gfxFont.h)

#[derive(Debug)]
struct LayoutFontCacheEntry {
    family: String,
    font: Option<Rc<RefCell<Font>>>,
}

#[derive(Debug)]
struct FallbackFontCacheEntry {
    font: Rc<RefCell<Font>>,
}

/// A cached azure font (per paint thread) that
/// can be shared by multiple text runs.
#[derive(Debug)]
struct PaintFontCacheEntry {
    pt_size: Au,
    identifier: Atom,
    font: Rc<RefCell<ScaledFont>>,
}

/// An epoch for the font context cache. The cache is flushed if the current epoch does not match
/// this one.
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;

/// The FontContext represents the per-thread/thread state necessary for
/// working with fonts. It is the public API used by the layout and
/// paint code. It talks directly to the font cache thread where
/// required.
#[derive(Debug)]
pub struct FontContext {
    platform_handle: FontContextHandle,
    font_cache_thread: FontCacheThread,

    /// TODO: See bug https://github.com/servo/servo/issues/3300.
    layout_font_cache: Vec<LayoutFontCacheEntry>,
    fallback_font_cache: Vec<FallbackFontCacheEntry>,

    /// Strong reference as the paint FontContext is (for now) recycled
    /// per frame. TODO: Make this weak when incremental redraw is done.
    paint_font_cache: Vec<PaintFontCacheEntry>,

    layout_font_group_cache:
        HashMap<LayoutFontGroupCacheKey, Rc<FontGroup>, BuildHasherDefault<FnvHasher>>,

    epoch: usize,
}

impl FontContext {
    pub fn new(font_cache_thread: FontCacheThread) -> FontContext {
        let handle = FontContextHandle::new();
        FontContext {
            platform_handle: handle,
            font_cache_thread: font_cache_thread,
            layout_font_cache: vec!(),
            fallback_font_cache: vec!(),
            paint_font_cache: vec!(),
            layout_font_group_cache: HashMap::with_hasher(Default::default()),
            epoch: 0,
        }
    }

    /// Create a font for use in layout calculations.
    fn create_layout_font(&self,
                          template: Arc<FontTemplateData>,
                          descriptor: FontTemplateDescriptor,
                          pt_size: Au,
                          variant: font_variant::T,
                          font_key: Option<webrender_traits::FontKey>) -> Result<Font, ()> {
        // TODO: (Bug #3463): Currently we only support fake small-caps
        // painting. We should also support true small-caps (where the
        // font supports it) in the future.
        let actual_pt_size = match variant {
            font_variant::T::small_caps => pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR),
            font_variant::T::normal => pt_size,
        };

        let handle = try!(FontHandle::new_from_template(&self.platform_handle,
                                                        template,
                                                        Some(actual_pt_size)));

        Ok(Font::new(handle, variant, descriptor, pt_size, actual_pt_size, font_key))
    }

    fn expire_font_caches_if_necessary(&mut self) {
        let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
        if current_epoch == self.epoch {
            return
        }

        self.layout_font_cache.clear();
        self.fallback_font_cache.clear();
        self.paint_font_cache.clear();
        self.layout_font_group_cache.clear();
        self.epoch = current_epoch
    }

    /// Create a group of fonts for use in layout calculations. May return
    /// a cached font if this font instance has already been used by
    /// this context.
    pub fn layout_font_group_for_style(&mut self, style: Arc<ServoFont>)
                                       -> Rc<FontGroup> {
        self.expire_font_caches_if_necessary();

        let layout_font_group_cache_key = LayoutFontGroupCacheKey {
            pointer: style.clone(),
            size: style.font_size,
        };
        if let Some(ref cached_font_group) = self.layout_font_group_cache.get(
                &layout_font_group_cache_key) {
            return (*cached_font_group).clone()
        }

        // TODO: The font context holds a strong ref to the cached fonts
        // so they will never be released. Find out a good time to drop them.

        let desc = FontTemplateDescriptor::new(style.font_weight,
                                               style.font_stretch,
                                               style.font_style == font_style::T::italic ||
                                                style.font_style == font_style::T::oblique);

        let mut fonts: SmallVec<[Rc<RefCell<Font>>; 8]> = SmallVec::new();

        for family in &style.font_family.0 {
            // GWTODO: Check on real pages if this is faster as Vec() or HashMap().
            let mut cache_hit = false;
            for cached_font_entry in &self.layout_font_cache {
                if cached_font_entry.family == family.name() {
                    match cached_font_entry.font {
                        None => {
                            cache_hit = true;
                            break;
                        }
                        Some(ref cached_font_ref) => {
                            let cached_font = (*cached_font_ref).borrow();
                            if cached_font.descriptor == desc &&
                               cached_font.requested_pt_size == style.font_size &&
                               cached_font.variant == style.font_variant {
                                fonts.push((*cached_font_ref).clone());
                                cache_hit = true;
                                break;
                            }
                        }
                    }
                }
            }

            if !cache_hit {
                let template_info = self.font_cache_thread.find_font_template(family.clone(),
                                                                             desc.clone());
                match template_info {
                    Some(template_info) => {
                        let layout_font = self.create_layout_font(template_info.font_template,
                                                                  desc.clone(),
                                                                  style.font_size,
                                                                  style.font_variant,
                                                                  template_info.font_key);
                        let font = match layout_font {
                            Ok(layout_font) => {
                                let layout_font = Rc::new(RefCell::new(layout_font));
                                fonts.push(layout_font.clone());

                                Some(layout_font)
                            }
                            Err(_) => None
                        };

                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_owned(),
                            font: font
                        });
                    }
                    None => {
                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_owned(),
                            font: None,
                        });
                    }
                }
            }
        }

        // If unable to create any of the specified fonts, create one from the
        // list of last resort fonts for this platform.
        if fonts.is_empty() {
            let mut cache_hit = false;
            for cached_font_entry in &self.fallback_font_cache {
                let cached_font = cached_font_entry.font.borrow();
                if cached_font.descriptor == desc &&
                            cached_font.requested_pt_size == style.font_size &&
                            cached_font.variant == style.font_variant {
                    fonts.push(cached_font_entry.font.clone());
                    cache_hit = true;
                    break;
                }
            }

            if !cache_hit {
                let template_info = self.font_cache_thread.last_resort_font_template(desc.clone());
                let layout_font = self.create_layout_font(template_info.font_template,
                                                          desc.clone(),
                                                          style.font_size,
                                                          style.font_variant,
                                                          template_info.font_key);
                match layout_font {
                    Ok(layout_font) => {
                        let layout_font = Rc::new(RefCell::new(layout_font));
                        self.fallback_font_cache.push(FallbackFontCacheEntry {
                            font: layout_font.clone(),
                        });
                        fonts.push(layout_font);
                    }
                    Err(_) => debug!("Failed to create fallback layout font!")
                }
            }
        }

        let font_group = Rc::new(FontGroup::new(fonts));
        self.layout_font_group_cache.insert(layout_font_group_cache_key, font_group.clone());
        font_group
    }

    /// Create a paint font for use with azure. May return a cached
    /// reference if already used by this font context.
    pub fn paint_font_from_template(&mut self,
                                        template: &Arc<FontTemplateData>,
                                        pt_size: Au)
                                        -> Rc<RefCell<ScaledFont>> {
        for cached_font in &self.paint_font_cache {
            if cached_font.pt_size == pt_size &&
               cached_font.identifier == template.identifier {
                return cached_font.font.clone();
            }
        }

        let paint_font = Rc::new(RefCell::new(create_scaled_font(template, pt_size)));
        self.paint_font_cache.push(PaintFontCacheEntry {
            font: paint_font.clone(),
            pt_size: pt_size,
            identifier: template.identifier.clone(),
        });
        paint_font
    }

    /// Returns a reference to the font cache thread.
    pub fn font_cache_thread(&self) -> FontCacheThread {
        self.font_cache_thread.clone()
    }
}

impl HeapSizeOf for FontContext {
    fn heap_size_of_children(&self) -> usize {
        // FIXME(njn): Measure other fields eventually.
        self.platform_handle.heap_size_of_children()
    }
}

#[derive(Debug)]
struct LayoutFontGroupCacheKey {
    pointer: Arc<ServoFont>,
    size: Au,
}

impl PartialEq for LayoutFontGroupCacheKey {
    fn eq(&self, other: &LayoutFontGroupCacheKey) -> bool {
        self.pointer == other.pointer && self.size == other.size
    }
}

impl Eq for LayoutFontGroupCacheKey {}

impl Hash for LayoutFontGroupCacheKey {
    fn hash<H>(&self, hasher: &mut H) where H: Hasher {
        self.pointer.hash.hash(hasher)
    }
}

#[inline]
pub fn invalidate_font_caches() {
    FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
}
<|endoftext|>


<commit_before>use {clamp, Point2f};
use sampling::Distribution1D;

#[derive(Debug)]
pub struct Distribution2D {
    p_conditional_v: Vec<Distribution1D>,
    p_marginal: Distribution1D,
}

impl Distribution2D {
    pub fn new(func: &[f32], nu: usize, nv: usize) -> Distribution2D {
        let mut p_conditional_v = Vec::with_capacity(nv);
        for v in 0..nv {
            // compute conditional sampling distribution for v_tilde
            p_conditional_v.push(Distribution1D::new(&func[v * nu..(v + 1) * nu]));
        }
        // compute marginal sampling distribution p[v_tilde]
        let mut marginal_func = Vec::with_capacity(nv);
        for v in 0..nv {
            marginal_func.push(p_conditional_v[v].func_int)
        }

        Distribution2D {
            p_conditional_v: p_conditional_v,
            p_marginal: Distribution1D::new(&marginal_func[..]),
        }
    }

    pub fn sample_continuous(&self, u: &Point2f) -> (Point2f, f32) {
        let (d_0, pdf_0, v) = self.p_marginal.sample_continuous(u[0]);
        let (d_1, pdf_1, _) = self.p_conditional_v[v].sample_continuous(u[1]);

        (Point2f::new(d_0, d_1), pdf_0 * pdf_1)
    }

    pub fn pdf(&self, p: &Point2f) -> f32 {
        let iu = clamp(p[0] as usize * self.p_conditional_v[0].count(),
                       0,
                       self.p_conditional_v[0].count() - 1);
        let iv = clamp(p[1] as usize * self.p_marginal.count(),
                       0,
                       self.p_marginal.count() - 1);

        self.p_conditional_v[iv].func[iu] / self.p_marginal.func_int
    }
}
<commit_msg>Fix some bug in Distribution2D<commit_after>use {clamp, Point2f};
use sampling::Distribution1D;

#[derive(Debug)]
pub struct Distribution2D {
    p_conditional_v: Vec<Distribution1D>,
    p_marginal: Distribution1D,
}

impl Distribution2D {
    pub fn new(func: &[f32], nu: usize, nv: usize) -> Distribution2D {
        let mut p_conditional_v = Vec::with_capacity(nv);
        for v in 0..nv {
            // compute conditional sampling distribution for v_tilde
            p_conditional_v.push(Distribution1D::new(&func[v * nu..(v + 1) * nu]));
        }
        // compute marginal sampling distribution p[v_tilde]
        let mut marginal_func = Vec::with_capacity(nv);
        for v in 0..nv {
            marginal_func.push(p_conditional_v[v].func_int)
        }

        Distribution2D {
            p_conditional_v: p_conditional_v,
            p_marginal: Distribution1D::new(&marginal_func[..]),
        }
    }

    pub fn sample_continuous(&self, u: &Point2f) -> (Point2f, f32) {
        let (d_1, pdf_1, v) = self.p_marginal.sample_continuous(u[1]);
        let (d_0, pdf_0, _) = self.p_conditional_v[v].sample_continuous(u[0]);

        (Point2f::new(d_0, d_1), pdf_0 * pdf_1)
    }

    pub fn pdf(&self, p: &Point2f) -> f32 {
        let iu = clamp(
            p[0] as usize * self.p_conditional_v[0].count(),
            0,
            self.p_conditional_v[0].count() - 1,
        );
        let iv = clamp(
            p[1] as usize * self.p_marginal.count(),
            0,
            self.p_marginal.count() - 1,
        );

        self.p_conditional_v[iv].func[iu] / self.p_marginal.func_int
    }
}
<|endoftext|>


<commit_before>use ast;

use session::Session;
use resolve::expr;
pub use exec::Step;
pub use resolve::scope::{ Scope, Item };
use resolve::types::{self, Shape};
use eval::DataMode;

pub fn resolve_module<'s>(session: &'s Session<'s>, ast: &'s ast::Module) -> Scope<'s> {
    let mut scope = session.prelude.clone();

    for _import in ast.imports.iter() {
        panic!("Imports unimplemented");
    }

    resolve_letdef(session, &mut scope, &ast.lets);

    for def in ast.defs.iter() {
        let ed = Item::Def(session.closure_arena.alloc(EventClosure{ ast:def, parent_scope: scope.clone() }));
        scope.names.insert(def.name.to_string(), ed);
    }

    scope
}

/// A user-defined event
pub struct EventClosure<'s> {
    pub ast: &'s ast::Def,
    pub parent_scope: Scope<'s>,
}

impl<'s> EventClosure<'s> {
    pub fn resolve_call(&self,
                        session: &'s Session<'s>,
                        shape_down: &Shape,
                        param: Item<'s>) -> (Shape, Step) {

        // TODO: use declared shape
        let shape_up = Shape::Val(types::Bottom, DataMode{ up: true, down: true });

        let mut scope = self.parent_scope.child(); // Base on lexical parent
        expr::assign(session, &mut scope, &self.ast.param, param);
        let steptree = resolve_seq(session, &scope, shape_down, &shape_up, &self.ast.block);
        (shape_up, steptree)
    }
}

/// A body associated with an event call
pub struct EventBodyClosure<'s> {
    ast: &'s ast::Block,
    parent_scope: Scope<'s>,
}

fn resolve_action<'s>(session: &'s Session<'s>,
                      scope: &Scope<'s>,
                      shape_down: &Shape,
                      shape_up: &Shape,
                      action: &'s ast::Action) -> Step {
    match *action {
        ast::Action::Seq(ref block) => resolve_seq(session, scope, shape_down, shape_up, block),
        ast::Action::Call(ref expr, ref arg, ref body) => {
            let arg = expr::rexpr(session, scope, arg);
            let body = body.as_ref().map(|x| {
                EventBodyClosure { ast: x, parent_scope: scope.child() }
            });

            if body.is_some() {
                unimplemented!();
            }

            match expr::rexpr(session, scope, expr) {
                Item::Def(entity) => {
                    let (_shape_child, step) = entity.resolve_call(session, shape_down, arg);
                    step
                }
                _ => panic!("Not callable"),
            }
        }
        ast::Action::Token(ref expr, ref body) => {
            debug!("Token: {:?}", expr);
            if body.is_some() { panic!("Body unimplemented"); }
            Step::Token(expr::rexpr(session, scope, expr).into_message(shape_down))
        }
        ast::Action::On(ref expr, ref body) => {
            let mut body_scope = scope.child();
            let msg = expr::lexpr(session, &mut body_scope, expr).into_message(shape_up);
            let body_step = match *body {
                Some(ref body) => resolve_seq(session, &body_scope, shape_down, shape_up, body),
                None => Step::Nop,
            };
            Step::TokenTop(msg, box body_step)
        }
        ast::Action::Repeat(ref count_ast, ref block) => {
            let count = expr::value(session, scope, count_ast);
            Step::Repeat(count, box resolve_seq(session, scope, shape_down, shape_up, block))
        }
    }
}

fn resolve_seq<'s>(session: &'s Session<'s>,
                  pscope: &Scope<'s>,
                  shape_down: &Shape,
                  shape_up: &Shape,
                  block: &'s ast::Block) -> Step {
    let mut scope = pscope.child();
    resolve_letdef(session, &mut scope, &block.lets);

    let steps = block.actions.iter().map(|action|
        resolve_action(session, &scope, shape_down, shape_up, action)
    ).collect();

    Step::Seq(steps)
}

pub fn resolve_letdef<'s>(session: &'s Session<'s>, scope: &mut Scope<'s>, lets: &[ast::LetDef]) {
    for &ast::LetDef(ref name, ref expr) in lets.iter() {
        let item = expr::rexpr(session, scope, expr);
        scope.bind(&name, item);
    }
}
<commit_msg>Remove unnecessary lifetime constraints on resolve_block<commit_after>use ast;

use session::Session;
use resolve::expr;
pub use exec::Step;
pub use resolve::scope::{ Scope, Item };
use resolve::types::{self, Shape};
use eval::DataMode;

pub fn resolve_module<'s>(session: &'s Session<'s>, ast: &'s ast::Module) -> Scope<'s> {
    let mut scope = session.prelude.clone();

    for _import in ast.imports.iter() {
        panic!("Imports unimplemented");
    }

    resolve_letdef(session, &mut scope, &ast.lets);

    for def in ast.defs.iter() {
        let ed = Item::Def(session.closure_arena.alloc(EventClosure{ ast:def, parent_scope: scope.clone() }));
        scope.names.insert(def.name.to_string(), ed);
    }

    scope
}

/// A user-defined event
pub struct EventClosure<'s> {
    pub ast: &'s ast::Def,
    pub parent_scope: Scope<'s>,
}

impl<'s> EventClosure<'s> {
    pub fn resolve_call(&self,
                        session: &'s Session<'s>,
                        shape_down: &Shape,
                        param: Item<'s>) -> (Shape, Step) {

        // TODO: use declared shape
        let shape_up = Shape::Val(types::Bottom, DataMode{ up: true, down: true });

        let mut scope = self.parent_scope.child(); // Base on lexical parent
        expr::assign(session, &mut scope, &self.ast.param, param);
        let steptree = resolve_seq(session, &scope, shape_down, &shape_up, &self.ast.block);
        (shape_up, steptree)
    }
}

/// A body associated with an event call
pub struct EventBodyClosure<'s> {
    ast: &'s ast::Block,
    parent_scope: Scope<'s>,
}

fn resolve_action<'s>(session: &'s Session<'s>,
                      scope: &Scope<'s>,
                      shape_down: &Shape,
                      shape_up: &Shape,
                      action: &ast::Action) -> Step {
    match *action {
        ast::Action::Seq(ref block) => resolve_seq(session, scope, shape_down, shape_up, block),
        ast::Action::Call(ref expr, ref arg, ref body) => {
            let arg = expr::rexpr(session, scope, arg);
            let body = body.as_ref().map(|x| {
                EventBodyClosure { ast: x, parent_scope: scope.child() }
            });

            if body.is_some() {
                unimplemented!();
            }

            match expr::rexpr(session, scope, expr) {
                Item::Def(entity) => {
                    let (_shape_child, step) = entity.resolve_call(session, shape_down, arg);
                    step
                }
                _ => panic!("Not callable"),
            }
        }
        ast::Action::Token(ref expr, ref body) => {
            debug!("Token: {:?}", expr);
            if body.is_some() { panic!("Body unimplemented"); }
            Step::Token(expr::rexpr(session, scope, expr).into_message(shape_down))
        }
        ast::Action::On(ref expr, ref body) => {
            let mut body_scope = scope.child();
            let msg = expr::lexpr(session, &mut body_scope, expr).into_message(shape_up);
            let body_step = match *body {
                Some(ref body) => resolve_seq(session, &body_scope, shape_down, shape_up, body),
                None => Step::Nop,
            };
            Step::TokenTop(msg, box body_step)
        }
        ast::Action::Repeat(ref count_ast, ref block) => {
            let count = expr::value(session, scope, count_ast);
            Step::Repeat(count, box resolve_seq(session, scope, shape_down, shape_up, block))
        }
    }
}

fn resolve_seq<'s>(session: &'s Session<'s>,
                  pscope: &Scope<'s>,
                  shape_down: &Shape,
                  shape_up: &Shape,
                  block: &ast::Block) -> Step {
    let mut scope = pscope.child();
    resolve_letdef(session, &mut scope, &block.lets);

    let steps = block.actions.iter().map(|action|
        resolve_action(session, &scope, shape_down, shape_up, action)
    ).collect();

    Step::Seq(steps)
}

pub fn resolve_letdef<'s>(session: &'s Session<'s>, scope: &mut Scope<'s>, lets: &[ast::LetDef]) {
    for &ast::LetDef(ref name, ref expr) in lets.iter() {
        let item = expr::rexpr(session, scope, expr);
        scope.bind(&name, item);
    }
}
<|endoftext|>