<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/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::webgl::{ActiveAttribInfo, ActiveUniformInfo, WebGLCommand, WebGLError};
use canvas_traits::webgl::{WebGLMsgSender, WebGLProgramId, WebGLResult, webgl_channel};
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::WebGLProgramBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::root::{DomRoot, MutNullableDom};
use dom::bindings::str::DOMString;
use dom::webglactiveinfo::WebGLActiveInfo;
use dom::webglobject::WebGLObject;
use dom::webglrenderingcontext::MAX_UNIFORM_AND_ATTRIBUTE_LEN;
use dom::webglshader::WebGLShader;
use dom::webgluniformlocation::WebGLUniformLocation;
use dom::window::Window;
use dom_struct::dom_struct;
use fnv::FnvHashSet;
use std::cell::{Cell, Ref};

#[dom_struct]
pub struct WebGLProgram {
    webgl_object: WebGLObject,
    id: WebGLProgramId,
    is_deleted: Cell<bool>,
    link_called: Cell<bool>,
    linked: Cell<bool>,
    fragment_shader: MutNullableDom<WebGLShader>,
    vertex_shader: MutNullableDom<WebGLShader>,
    #[ignore_malloc_size_of = "Defined in ipc-channel"]
    renderer: WebGLMsgSender,
    active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>,
    active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>,
}

impl WebGLProgram {
    fn new_inherited(renderer: WebGLMsgSender,
                     id: WebGLProgramId)
                     -> WebGLProgram {
        WebGLProgram {
            webgl_object: WebGLObject::new_inherited(),
            id: id,
            is_deleted: Cell::new(false),
            link_called: Cell::new(false),
            linked: Cell::new(false),
            fragment_shader: Default::default(),
            vertex_shader: Default::default(),
            renderer: renderer,
            active_attribs: DomRefCell::new(vec![].into()),
            active_uniforms: DomRefCell::new(vec![].into()),
        }
    }

    pub fn maybe_new(window: &Window, renderer: WebGLMsgSender)
                     -> Option<DomRoot<WebGLProgram>> {
        let (sender, receiver) = webgl_channel().unwrap();
        renderer.send(WebGLCommand::CreateProgram(sender)).unwrap();

        let result = receiver.recv().unwrap();
        result.map(|program_id| WebGLProgram::new(window, renderer, program_id))
    }

    pub fn new(window: &Window,
               renderer: WebGLMsgSender,
               id: WebGLProgramId)
               -> DomRoot<WebGLProgram> {
        reflect_dom_object(Box::new(WebGLProgram::new_inherited(renderer, id)),
                           window,
                           WebGLProgramBinding::Wrap)
    }
}

impl WebGLProgram {
    pub fn id(&self) -> WebGLProgramId {
        self.id
    }

    /// glDeleteProgram
    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            let _ = self.renderer.send(WebGLCommand::DeleteProgram(self.id));

            if let Some(shader) = self.fragment_shader.get() {
                shader.decrement_attached_counter();
            }

            if let Some(shader) = self.vertex_shader.get() {
                shader.decrement_attached_counter();
            }
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    pub fn is_linked(&self) -> bool {
        self.linked.get()
    }

    /// glLinkProgram
    pub fn link(&self) -> WebGLResult<()>  {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        self.linked.set(false);
        *self.active_attribs.borrow_mut() = vec![].into();

        match self.fragment_shader.get() {
            Some(ref shader) if shader.successfully_compiled() => {},
            _ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
        }

        match self.vertex_shader.get() {
            Some(ref shader) if shader.successfully_compiled() => {},
            _ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
        }

        let (sender, receiver) = webgl_channel().unwrap();
        self.renderer.send(WebGLCommand::LinkProgram(self.id, sender)).unwrap();
        let link_info = receiver.recv().unwrap();

        {
            let mut used_locs = FnvHashSet::default();
            let mut used_names = FnvHashSet::default();
            for active_attrib in &*link_info.active_attribs {
                if active_attrib.location == -1 {
                    continue;
                }
                let columns = match active_attrib.type_ {
                    constants::FLOAT_MAT2 => 2,
                    constants::FLOAT_MAT3 => 3,
                    constants::FLOAT_MAT4 => 4,
                    _ => 1,
                };
                assert!(used_names.insert(&*active_attrib.name));
                for column in 0..columns {
                    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.31
                    if !used_locs.insert(active_attrib.location as u32 + column) {
                        return Ok(());
                    }
                }
            }
            for active_uniform in &*link_info.active_uniforms {
                // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.41
                if !used_names.insert(&*active_uniform.base_name) {
                    return Ok(());
                }
            }
        }

        self.linked.set(link_info.linked);
        *self.active_attribs.borrow_mut() = link_info.active_attribs;
        *self.active_uniforms.borrow_mut() = link_info.active_uniforms;
        Ok(())
    }

    pub fn active_attribs(&self) -> Ref<[ActiveAttribInfo]> {
        Ref::map(self.active_attribs.borrow(), |attribs| &**attribs)
    }

    pub fn active_uniforms(&self) -> Ref<[ActiveUniformInfo]> {
        Ref::map(self.active_uniforms.borrow(), |uniforms| &**uniforms)
    }

    /// glValidateProgram
    pub fn validate(&self) -> WebGLResult<()> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        self.renderer.send(WebGLCommand::ValidateProgram(self.id)).unwrap();
        Ok(())
    }

    /// glAttachShader
    pub fn attach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
        if self.is_deleted() || shader.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        let shader_slot = match shader.gl_type() {
            constants::FRAGMENT_SHADER => &self.fragment_shader,
            constants::VERTEX_SHADER => &self.vertex_shader,
            _ => {
                error!("detachShader: Unexpected shader type");
                return Err(WebGLError::InvalidValue);
            }
        };

        // TODO(emilio): Differentiate between same shader already assigned and other previous
        // shader.
        if shader_slot.get().is_some() {
            return Err(WebGLError::InvalidOperation);
        }

        shader_slot.set(Some(shader));
        shader.increment_attached_counter();

        self.renderer.send(WebGLCommand::AttachShader(self.id, shader.id())).unwrap();

        Ok(())
    }

    /// glDetachShader
    pub fn detach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        let shader_slot = match shader.gl_type() {
            constants::FRAGMENT_SHADER => &self.fragment_shader,
            constants::VERTEX_SHADER => &self.vertex_shader,
            _ => {
                error!("detachShader: Unexpected shader type");
                return Err(WebGLError::InvalidValue);
            }
        };

        match shader_slot.get() {
            Some(ref attached_shader) if attached_shader.id() != shader.id() =>
                return Err(WebGLError::InvalidOperation),
            None =>
                return Err(WebGLError::InvalidOperation),
            _ => {}
        }

        shader_slot.set(None);
        shader.decrement_attached_counter();

        self.renderer.send(WebGLCommand::DetachShader(self.id, shader.id())).unwrap();

        Ok(())
    }

    /// glBindAttribLocation
    pub fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
            return Err(WebGLError::InvalidValue);
        }

        // Check if the name is reserved
        if name.starts_with("gl_") || name.starts_with("webgl") || name.starts_with("_webgl_") {
            return Err(WebGLError::InvalidOperation);
        }

        self.renderer
            .send(WebGLCommand::BindAttribLocation(self.id, index, name.into()))
            .unwrap();
        Ok(())
    }

    pub fn get_active_uniform(&self, index: u32) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidValue);
        }
        let uniforms = self.active_uniforms.borrow();
        let data = uniforms.get(index as usize).ok_or(WebGLError::InvalidValue)?;
        Ok(WebGLActiveInfo::new(
            self.global().as_window(),
            data.size.unwrap_or(1),
            data.type_,
            data.name().into(),
        ))
    }

    /// glGetActiveAttrib
    pub fn get_active_attrib(&self, index: u32) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidValue);
        }
        let attribs = self.active_attribs.borrow();
        let data = attribs.get(index as usize).ok_or(WebGLError::InvalidValue)?;
        Ok(WebGLActiveInfo::new(
            self.global().as_window(),
            data.size,
            data.type_,
            data.name.clone().into(),
        ))
    }

    /// glGetAttribLocation
    pub fn get_attrib_location(&self, name: DOMString) -> WebGLResult<i32> {
        if !self.is_linked() || self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
            return Err(WebGLError::InvalidValue);
        }

        // Check if the name is reserved
        if name.starts_with("gl_") {
            return Ok(-1);
        }

        // https://www.khronos.org/registry/webgl/specs/latest/1.0/#GLSL_CONSTRUCTS
        if name.starts_with("webgl_") || name.starts_with("_webgl_") {
            return Ok(-1);
        }

        let location = self.active_attribs
            .borrow()
            .iter()
            .find(|attrib| attrib.name == &*name)
            .map_or(-1, |attrib| attrib.location);
        Ok(location)
    }

    /// glGetUniformLocation
    pub fn get_uniform_location(
        &self,
        name: DOMString,
    ) -> WebGLResult<Option<DomRoot<WebGLUniformLocation>>> {
        if !self.is_linked() || self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
            return Err(WebGLError::InvalidValue);
        }

        // Check if the name is reserved
        if name.starts_with("gl_") {
            return Ok(None);
        }

        // https://www.khronos.org/registry/webgl/specs/latest/1.0/#GLSL_CONSTRUCTS
        if name.starts_with("webgl_") || name.starts_with("_webgl_") {
            return Ok(None);
        }

        let (size, type_) = {
            let (base_name, array_index) = match parse_uniform_name(&name) {
                Some((name, index)) if index.map_or(true, |i| i >= 0) => (name, index),
                _ => return Ok(None),
            };

            let uniforms = self.active_uniforms.borrow();
            match uniforms.iter().find(|attrib| &*attrib.base_name == base_name) {
                Some(uniform) if array_index.is_none() || array_index < uniform.size => {
                    (uniform.size.map(|size| size - array_index.unwrap_or_default()), uniform.type_)
                },
                _ => return Ok(None),
            }
        };

        let (sender, receiver) = webgl_channel().unwrap();
        self.renderer
            .send(WebGLCommand::GetUniformLocation(self.id, name.into(), sender))
            .unwrap();
        let location = receiver.recv().unwrap();

        Ok(Some(WebGLUniformLocation::new(self.global().as_window(), location, self.id, size, type_)))
    }

    /// glGetProgramInfoLog
    pub fn get_info_log(&self) -> WebGLResult<String> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        if self.link_called.get() {
            let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) {
                (Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(),
                _ => false
            };
            if !shaders_compiled {
                return Ok("One or more shaders failed to compile".to_string());
            }
        }
        let (sender, receiver) = webgl_channel().unwrap();
        self.renderer.send(WebGLCommand::GetProgramInfoLog(self.id, sender)).unwrap();
        Ok(receiver.recv().unwrap())
    }

    pub fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> {
        if self.is_deleted.get() {
            return Err(WebGLError::InvalidValue);
        }
        Ok(match (self.vertex_shader.get(), self.fragment_shader.get()) {
            (Some(vertex_shader), Some(fragment_shader)) => {
                vec![vertex_shader, fragment_shader]
            }
            (Some(shader), None) | (None, Some(shader)) => vec![shader],
            (None, None) => vec![]
        })
    }
}

impl Drop for WebGLProgram {
    fn drop(&mut self) {
        self.delete();
    }
}

fn parse_uniform_name(name: &str) -> Option<(&str, Option<i32>)> {
    if !name.ends_with(']') {
        return Some((name, None));
    }
    let bracket_pos = name[..name.len() - 1].rfind('[')?;
    let index = name[(bracket_pos + 1)..(name.len() - 1)].parse::<i32>().ok()?;
    Some((&name[..bracket_pos], Some(index)))
}
<commit_msg>Correctly reset active uniforms on link<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/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::webgl::{ActiveAttribInfo, ActiveUniformInfo, WebGLCommand, WebGLError};
use canvas_traits::webgl::{WebGLMsgSender, WebGLProgramId, WebGLResult, webgl_channel};
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::WebGLProgramBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::root::{DomRoot, MutNullableDom};
use dom::bindings::str::DOMString;
use dom::webglactiveinfo::WebGLActiveInfo;
use dom::webglobject::WebGLObject;
use dom::webglrenderingcontext::MAX_UNIFORM_AND_ATTRIBUTE_LEN;
use dom::webglshader::WebGLShader;
use dom::webgluniformlocation::WebGLUniformLocation;
use dom::window::Window;
use dom_struct::dom_struct;
use fnv::FnvHashSet;
use std::cell::{Cell, Ref};

#[dom_struct]
pub struct WebGLProgram {
    webgl_object: WebGLObject,
    id: WebGLProgramId,
    is_deleted: Cell<bool>,
    link_called: Cell<bool>,
    linked: Cell<bool>,
    fragment_shader: MutNullableDom<WebGLShader>,
    vertex_shader: MutNullableDom<WebGLShader>,
    #[ignore_malloc_size_of = "Defined in ipc-channel"]
    renderer: WebGLMsgSender,
    active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>,
    active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>,
}

impl WebGLProgram {
    fn new_inherited(renderer: WebGLMsgSender,
                     id: WebGLProgramId)
                     -> WebGLProgram {
        WebGLProgram {
            webgl_object: WebGLObject::new_inherited(),
            id: id,
            is_deleted: Cell::new(false),
            link_called: Cell::new(false),
            linked: Cell::new(false),
            fragment_shader: Default::default(),
            vertex_shader: Default::default(),
            renderer: renderer,
            active_attribs: DomRefCell::new(vec![].into()),
            active_uniforms: DomRefCell::new(vec![].into()),
        }
    }

    pub fn maybe_new(window: &Window, renderer: WebGLMsgSender)
                     -> Option<DomRoot<WebGLProgram>> {
        let (sender, receiver) = webgl_channel().unwrap();
        renderer.send(WebGLCommand::CreateProgram(sender)).unwrap();

        let result = receiver.recv().unwrap();
        result.map(|program_id| WebGLProgram::new(window, renderer, program_id))
    }

    pub fn new(window: &Window,
               renderer: WebGLMsgSender,
               id: WebGLProgramId)
               -> DomRoot<WebGLProgram> {
        reflect_dom_object(Box::new(WebGLProgram::new_inherited(renderer, id)),
                           window,
                           WebGLProgramBinding::Wrap)
    }
}

impl WebGLProgram {
    pub fn id(&self) -> WebGLProgramId {
        self.id
    }

    /// glDeleteProgram
    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            let _ = self.renderer.send(WebGLCommand::DeleteProgram(self.id));

            if let Some(shader) = self.fragment_shader.get() {
                shader.decrement_attached_counter();
            }

            if let Some(shader) = self.vertex_shader.get() {
                shader.decrement_attached_counter();
            }
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    pub fn is_linked(&self) -> bool {
        self.linked.get()
    }

    /// glLinkProgram
    pub fn link(&self) -> WebGLResult<()>  {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        self.linked.set(false);
        *self.active_attribs.borrow_mut() = Box::new([]);
        *self.active_uniforms.borrow_mut() = Box::new([]);

        match self.fragment_shader.get() {
            Some(ref shader) if shader.successfully_compiled() => {},
            _ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
        }

        match self.vertex_shader.get() {
            Some(ref shader) if shader.successfully_compiled() => {},
            _ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
        }

        let (sender, receiver) = webgl_channel().unwrap();
        self.renderer.send(WebGLCommand::LinkProgram(self.id, sender)).unwrap();
        let link_info = receiver.recv().unwrap();

        {
            let mut used_locs = FnvHashSet::default();
            let mut used_names = FnvHashSet::default();
            for active_attrib in &*link_info.active_attribs {
                if active_attrib.location == -1 {
                    continue;
                }
                let columns = match active_attrib.type_ {
                    constants::FLOAT_MAT2 => 2,
                    constants::FLOAT_MAT3 => 3,
                    constants::FLOAT_MAT4 => 4,
                    _ => 1,
                };
                assert!(used_names.insert(&*active_attrib.name));
                for column in 0..columns {
                    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.31
                    if !used_locs.insert(active_attrib.location as u32 + column) {
                        return Ok(());
                    }
                }
            }
            for active_uniform in &*link_info.active_uniforms {
                // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.41
                if !used_names.insert(&*active_uniform.base_name) {
                    return Ok(());
                }
            }
        }

        self.linked.set(link_info.linked);
        *self.active_attribs.borrow_mut() = link_info.active_attribs;
        *self.active_uniforms.borrow_mut() = link_info.active_uniforms;
        Ok(())
    }

    pub fn active_attribs(&self) -> Ref<[ActiveAttribInfo]> {
        Ref::map(self.active_attribs.borrow(), |attribs| &**attribs)
    }

    pub fn active_uniforms(&self) -> Ref<[ActiveUniformInfo]> {
        Ref::map(self.active_uniforms.borrow(), |uniforms| &**uniforms)
    }

    /// glValidateProgram
    pub fn validate(&self) -> WebGLResult<()> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        self.renderer.send(WebGLCommand::ValidateProgram(self.id)).unwrap();
        Ok(())
    }

    /// glAttachShader
    pub fn attach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
        if self.is_deleted() || shader.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        let shader_slot = match shader.gl_type() {
            constants::FRAGMENT_SHADER => &self.fragment_shader,
            constants::VERTEX_SHADER => &self.vertex_shader,
            _ => {
                error!("detachShader: Unexpected shader type");
                return Err(WebGLError::InvalidValue);
            }
        };

        // TODO(emilio): Differentiate between same shader already assigned and other previous
        // shader.
        if shader_slot.get().is_some() {
            return Err(WebGLError::InvalidOperation);
        }

        shader_slot.set(Some(shader));
        shader.increment_attached_counter();

        self.renderer.send(WebGLCommand::AttachShader(self.id, shader.id())).unwrap();

        Ok(())
    }

    /// glDetachShader
    pub fn detach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        let shader_slot = match shader.gl_type() {
            constants::FRAGMENT_SHADER => &self.fragment_shader,
            constants::VERTEX_SHADER => &self.vertex_shader,
            _ => {
                error!("detachShader: Unexpected shader type");
                return Err(WebGLError::InvalidValue);
            }
        };

        match shader_slot.get() {
            Some(ref attached_shader) if attached_shader.id() != shader.id() =>
                return Err(WebGLError::InvalidOperation),
            None =>
                return Err(WebGLError::InvalidOperation),
            _ => {}
        }

        shader_slot.set(None);
        shader.decrement_attached_counter();

        self.renderer.send(WebGLCommand::DetachShader(self.id, shader.id())).unwrap();

        Ok(())
    }

    /// glBindAttribLocation
    pub fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
            return Err(WebGLError::InvalidValue);
        }

        // Check if the name is reserved
        if name.starts_with("gl_") || name.starts_with("webgl") || name.starts_with("_webgl_") {
            return Err(WebGLError::InvalidOperation);
        }

        self.renderer
            .send(WebGLCommand::BindAttribLocation(self.id, index, name.into()))
            .unwrap();
        Ok(())
    }

    pub fn get_active_uniform(&self, index: u32) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidValue);
        }
        let uniforms = self.active_uniforms.borrow();
        let data = uniforms.get(index as usize).ok_or(WebGLError::InvalidValue)?;
        Ok(WebGLActiveInfo::new(
            self.global().as_window(),
            data.size.unwrap_or(1),
            data.type_,
            data.name().into(),
        ))
    }

    /// glGetActiveAttrib
    pub fn get_active_attrib(&self, index: u32) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidValue);
        }
        let attribs = self.active_attribs.borrow();
        let data = attribs.get(index as usize).ok_or(WebGLError::InvalidValue)?;
        Ok(WebGLActiveInfo::new(
            self.global().as_window(),
            data.size,
            data.type_,
            data.name.clone().into(),
        ))
    }

    /// glGetAttribLocation
    pub fn get_attrib_location(&self, name: DOMString) -> WebGLResult<i32> {
        if !self.is_linked() || self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
            return Err(WebGLError::InvalidValue);
        }

        // Check if the name is reserved
        if name.starts_with("gl_") {
            return Ok(-1);
        }

        // https://www.khronos.org/registry/webgl/specs/latest/1.0/#GLSL_CONSTRUCTS
        if name.starts_with("webgl_") || name.starts_with("_webgl_") {
            return Ok(-1);
        }

        let location = self.active_attribs
            .borrow()
            .iter()
            .find(|attrib| attrib.name == &*name)
            .map_or(-1, |attrib| attrib.location);
        Ok(location)
    }

    /// glGetUniformLocation
    pub fn get_uniform_location(
        &self,
        name: DOMString,
    ) -> WebGLResult<Option<DomRoot<WebGLUniformLocation>>> {
        if !self.is_linked() || self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
            return Err(WebGLError::InvalidValue);
        }

        // Check if the name is reserved
        if name.starts_with("gl_") {
            return Ok(None);
        }

        // https://www.khronos.org/registry/webgl/specs/latest/1.0/#GLSL_CONSTRUCTS
        if name.starts_with("webgl_") || name.starts_with("_webgl_") {
            return Ok(None);
        }

        let (size, type_) = {
            let (base_name, array_index) = match parse_uniform_name(&name) {
                Some((name, index)) if index.map_or(true, |i| i >= 0) => (name, index),
                _ => return Ok(None),
            };

            let uniforms = self.active_uniforms.borrow();
            match uniforms.iter().find(|attrib| &*attrib.base_name == base_name) {
                Some(uniform) if array_index.is_none() || array_index < uniform.size => {
                    (uniform.size.map(|size| size - array_index.unwrap_or_default()), uniform.type_)
                },
                _ => return Ok(None),
            }
        };

        let (sender, receiver) = webgl_channel().unwrap();
        self.renderer
            .send(WebGLCommand::GetUniformLocation(self.id, name.into(), sender))
            .unwrap();
        let location = receiver.recv().unwrap();

        Ok(Some(WebGLUniformLocation::new(self.global().as_window(), location, self.id, size, type_)))
    }

    /// glGetProgramInfoLog
    pub fn get_info_log(&self) -> WebGLResult<String> {
        if self.is_deleted() {
            return Err(WebGLError::InvalidOperation);
        }
        if self.link_called.get() {
            let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) {
                (Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(),
                _ => false
            };
            if !shaders_compiled {
                return Ok("One or more shaders failed to compile".to_string());
            }
        }
        let (sender, receiver) = webgl_channel().unwrap();
        self.renderer.send(WebGLCommand::GetProgramInfoLog(self.id, sender)).unwrap();
        Ok(receiver.recv().unwrap())
    }

    pub fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> {
        if self.is_deleted.get() {
            return Err(WebGLError::InvalidValue);
        }
        Ok(match (self.vertex_shader.get(), self.fragment_shader.get()) {
            (Some(vertex_shader), Some(fragment_shader)) => {
                vec![vertex_shader, fragment_shader]
            }
            (Some(shader), None) | (None, Some(shader)) => vec![shader],
            (None, None) => vec![]
        })
    }
}

impl Drop for WebGLProgram {
    fn drop(&mut self) {
        self.delete();
    }
}

fn parse_uniform_name(name: &str) -> Option<(&str, Option<i32>)> {
    if !name.ends_with(']') {
        return Some((name, None));
    }
    let bracket_pos = name[..name.len() - 1].rfind('[')?;
    let index = name[(bracket_pos + 1)..(name.len() - 1)].parse::<i32>().ok()?;
    Some((&name[..bracket_pos], Some(index)))
}
<|endoftext|>


<commit_before>mod global;
mod handlers;
mod tree;
mod region;

pub use self::tree::RoleStatus;
use self::tree::SurfaceData;
use wayland_server::{Client, EventLoopHandle, Init, resource_is_registered};

use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_surface};

/// Description of which part of a surface
/// should be considered damaged and needs to be redrawn
pub enum Damage {
    /// The whole surface must be considered damaged (this is the default)
    Full,
    /// A rectangle containing the damaged zone, in surface coordinates
    Surface(Rectangle),
    /// A rectangle containing the smaazed zone, in buffer coordinates
    ///
    /// Note: Buffer scaling must be taken into consideration
    Buffer(Rectangle),
}

/// Data associated with a surface, aggreged by the handlers
///
/// Most of the fields of this struct represent a double-buffered state, which
/// should only be applied once a `commit` request is received from the surface.
///
/// You are responsible for setting those values as you see fit to avoid
/// processing them two times.
pub struct SurfaceAttributes<U> {
    /// Buffer defining the contents of the surface
    ///
    /// The tuple represent the coordinates of this buffer
    /// relative to the location of the current buffer.
    ///
    /// If set to `Some(None)`, it means the user specifically asked for the
    /// surface to be unmapped.
    ///
    /// You are free to set this field to `None` to avoid processing it several
    /// times. It'll be set to `Some(...)` if the user attaches a buffer (or NULL) to
    /// the surface.
    pub buffer: Option<Option<(wl_buffer::WlBuffer, (i32, i32))>>,
    /// Scale of the contents of the buffer, for higher-resolution contents.
    ///
    /// If it matches the one of the output displaying this surface, no change
    /// is necessary.
    pub buffer_scale: i32,
    /// Transform under which interpret the contents of the buffer
    ///
    /// If it matches the one of the output displaying this surface, no change
    /// is necessary.
    pub buffer_transform: wl_output::Transform,
    /// Region of the surface that is guaranteed to be opaque
    ///
    /// By default the whole surface is potentially transparent
    pub opaque_region: Option<RegionAttributes>,
    /// Region of the surface that is sensitive to user input
    ///
    /// By default the whole surface should be sensitive
    pub input_region: Option<RegionAttributes>,
    /// Damage rectangle
    ///
    /// Hint provided by the client to suggest that only this part
    /// of the surface was changed and needs to be redrawn
    pub damage: Damage,
    /// Subsurface-related attribute
    ///
    /// Is `Some` if this surface is a sub-surface
    ///
    /// **Warning:** Changing this field by yourself can cause panics.
    pub subsurface_attributes: Option<SubsurfaceAttributes>,
    /// User-controlled data
    ///
    /// This is your field to host whatever you need.
    pub user_data: U,
}

impl<U: Default> Default for SurfaceAttributes<U> {
    fn default() -> SurfaceAttributes<U> {
        SurfaceAttributes {
            buffer: None,
            buffer_scale: 1,
            buffer_transform: wl_output::Transform::Normal,
            opaque_region: None,
            input_region: None,
            damage: Damage::Full,
            subsurface_attributes: None,
            user_data: Default::default(),
        }
    }
}

/// Attributes defining the behaviour of a sub-surface relative to its parent
pub struct SubsurfaceAttributes {
    /// Horizontal location of the top-left corner of this sub-surface relative to
    /// the top-left corner of its parent
    pub x: i32,
    /// Vertical location of the top-left corner of this sub-surface relative to
    /// the top-left corner of its parent
    pub y: i32,
    /// Sync status of this sub-surface
    ///
    /// If `true`, this surface should be repainted synchronously with its parent
    /// if `false`, it should be considered independant of its parent regarding
    /// repaint timings.
    pub sync: bool,
}

impl Default for SubsurfaceAttributes {
    fn default() -> SubsurfaceAttributes {
        SubsurfaceAttributes {
            x: 0,
            y: 0,
            sync: true,
        }
    }
}

/// Kind of a rectangle part of a region
#[derive(Copy,Clone)]
pub enum RectangleKind {
    /// This rectangle should be added to the region
    Add,
    /// The intersection of this rectangle with the region should
    /// be removed from the region
    Subtract,
}

/// A rectangle defined by its top-left corner and dimensions
#[derive(Copy,Clone)]
pub struct Rectangle {
    /// horizontal position of the top-leftcorner of the rectangle, in surface coordinates
    pub x: i32,
    /// vertical position of the top-leftcorner of the rectangle, in surface coordinates
    pub y: i32,
    /// width of the rectangle
    pub width: i32,
    /// height of the rectangle
    pub height: i32,
}

/// Description of the contents of a region
///
/// A region is defined as an union and difference of rectangle.
///
/// This struct contains an ordered Vec containing the rectangles defining
/// a region. They should be added or substracted in this order to compute the
/// actual contents of the region.
#[derive(Clone)]
pub struct RegionAttributes {
    /// List of rectangle part of this region
    pub rects: Vec<(RectangleKind, Rectangle)>,
}

impl Default for RegionAttributes {
    fn default() -> RegionAttributes {
        RegionAttributes { rects: Vec::new() }
    }
}

/// A Compositor global token
///
/// This token can be cloned at will, and is the entry-point to
/// access data associated with the wl_surface and wl_region managed
/// by the `CompositorGlobal` that provided it.
#[derive(Copy,Clone)]
pub struct CompositorToken<U, H> {
    hid: usize,
    _data: ::std::marker::PhantomData<*mut U>,
    _handler: ::std::marker::PhantomData<*mut H>,
}

impl<U: Send + 'static, H: Handler + Send + 'static> CompositorToken<U, H> {
    /// Access the data of a surface
    ///
    /// The closure will be called with the contents of the data associated with this surface.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn with_surface_data<F>(&self, surface: &wl_surface::WlSurface, f: F)
        where F: FnOnce(&mut SurfaceAttributes<U>)
    {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe {
            SurfaceData::<U>::with_data(surface, f);
        }
    }

    /// Access the data of a surface tree
    ///
    /// The provided closure is called successively on the surface and all its child subsurfaces,
    /// in a depth-first order. This matches the order in which the surfaces are supposed to be
    /// drawn: top-most last.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn with_surface_tree<F>(&self, surface: &wl_surface::WlSurface, f: F) -> Result<(), ()>
        where F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>) -> bool
    {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe {
            SurfaceData::<U>::map_tree(surface, f);
        }
        Ok(())
    }

    /// Retrieve the parent of this surface
    ///
    /// Returns `None` is this surface is a root surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::get_parent(surface) }
    }

    /// Retrieve the children of this surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::get_children(surface) }
    }

    /// Retrieve the role status this surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn role_status(&self, surface: &wl_surface::WlSurface) -> RoleStatus {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::role_status(surface) }
    }

    /// Register that this surface has a role
    ///
    /// This makes this surface impossible to become a subsurface, as
    /// a surface can only have a single role at a time.
    ///
    /// Fails if the surface already has a role.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn give_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::give_role(surface) }
    }

    /// Register that this surface has no role
    ///
    /// It is a noop if this surface already didn't have one, but fails if
    /// the role was "subsurface". This role is automatically managed and as such
    /// cannot be removed manually.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn remove_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::remove_role(surface) }
    }

    /// Retrieve the metadata associated with a wl_region
    ///
    /// If the region is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(region, self.hid),
                "Accessing the data of foreign regions is not supported.");
        unsafe { RegionData::get_attributes(region) }
    }
}

pub struct CompositorHandler<U, H> {
    my_id: usize,
    log: ::slog::Logger,
    handler: H,
    _data: ::std::marker::PhantomData<U>,
}

impl<U, H> Init for CompositorHandler<U, H> {
    fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) {
        self.my_id = index;
        debug!(self.log, "Init finished")
    }
}

impl<U, H> CompositorHandler<U, H> {
    /// Create a new CompositorHandler
    pub fn new<L>(handler: H, logger: L) -> CompositorHandler<U, H>
        where L: Into<Option<::slog::Logger>>
    {
        let log = ::slog_or_stdlog(logger);
        CompositorHandler {
            my_id: ::std::usize::MAX,
            log: log.new(o!("smithay_module" => "compositor_handler")),
            handler: handler,
            _data: ::std::marker::PhantomData,
        }
    }

    /// Create a token to access the data associated to the objects managed by this handler.
    pub fn get_token(&self) -> CompositorToken<U, H> {
        assert!(self.my_id != ::std::usize::MAX,
                "CompositorHandler is not initialized yet.");
        CompositorToken {
            hid: self.my_id,
            _data: ::std::marker::PhantomData,
            _handler: ::std::marker::PhantomData,
        }
    }

    /// Access the underlying sub-handler
    pub fn get_handler(&mut self) -> &mut H {
        &mut self.handler
    }
}

/// Sub-handler trait for surface event handling
///
/// The global provided by Smithay cannot process these events for you, so they
/// are forwarded directly to a handler implementing this trait that you must provide
/// at creation of the `CompositorHandler`.
#[allow(unused_variables)]
pub trait Handler {
    /// See `wayland_server::protocol::wl_surface::Handler::commit`
    fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface) {}
    /// See `wayland_server::protocol::wl_surface::Handler::frame`
    fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface,
             callback: wl_callback::WlCallback) {
    }
}
<commit_msg>compositor: module documentation<commit_after>//! Utilities for handling surfaces, subsurfaces and regions
//!
//! This module provides the `CompositorHandler<U,H>` type, with implements
//! automatic handling of sufaces, subsurfaces and region wayland objects,
//! by being registered as a global handler for `wl_compositor` and
//! `wl_subcompositor`.
//!
//! ## Why use this handler
//!
//! This handler does a simple job: it stores in a coherent way the state of
//! surface trees with subsurfaces, to provide you a direct access to the tree
//! structure and all surface metadata.
//!
//! As such, you can, given a root surface with a role requiring it to be displayed,
//! you can iterate over the whole tree of subsurfaces to recover all the metadata you
//! need to display the subsurface tree.
//!
//! This handler will not do anything more than present you the metadata specified by the
//! client in a coherent and practical way. All the logic regarding to drawing itself, and
//! the positionning of windows (surface trees) one relative to another is out of its scope.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this handler, simply instanciate it and register it to the event loop
//! as a global handler for wl_compositor and wl_subcompositor:
//!
//! ```
//! # extern crate wayland_server;
//! # extern crate smithay;
//! use wayland_server::protocol::wl_compositor::WlCompositor;
//! use wayland_server::protocol::wl_subcompositor::WlSubcompositor;
//! use smithay::compositor;
//!
//! // Define some user data to be associated with the surfaces.
//! // It must implement the Default trait, which will represent the state of a surface which
//! // has just been created.
//! #[derive(Default)]
//! struct MyData {
//!     // whatever you need here
//! }
//!
//! // Define a sub-handler to take care of the events the CompositorHandler does not rack for you
//! struct MyHandler {
//!     // whatever you need
//! }
//!
//! // Implement the handler trait for this sub-handler
//! impl compositor::Handler for MyHandler {
//!     // See the trait documentation for its implementation
//!     // A default implementation for each method is provided, that does nothing
//! }
//!
//! // A type alias to shorten things:
//! type MyCompositorHandler = compositor::CompositorHandler<MyData,MyHandler>;
//!
//! # fn main() {
//! # let (_display, mut event_loop) = wayland_server::create_display();
//!
//! // Instanciate the CompositorHandler and give it to the event loop
//! let compositor_hid = event_loop.add_handler_with_init(
//!     MyCompositorHandler::new(Myhandler::new())
//! );
//!
//! // Register it as a handler for wl_compositor
//! event_loop.register_global::<WlCompositor, MyCompositorHandler>(compositor_hid);
//!
//! // Register it as a handler for wl_subcompositor
//! event_loop.register_global::<WlSubcompositor, MyCompositorHandler>(compositor_hid);
//!
//! // retrieve the token needed to access the surfaces' metadata
//! let compositor_token = {
//!     let state = event_loop.state();
//!     state.get_handler::<MyCompositorHandler>>(handler_id).get_token()
//! };
//!
//! // You're now ready to go!
//! # }
//! ```
//!
//! ### Use the surface metadata
//!
//! As you can see in the previous example, in the end we are retrieving a token from
//! the `CompositorHandler`. This token is necessary to retrieve the metadata associated with
//! a surface. It can be cloned, and is sendable accross threads. See `CompositorToken` for
//! the details of what it enables you.
//!
//! The surface metadata is held in the `SurfaceAttributes` struct. In contains double-buffered
//! state pending from the client as defined by the protocol for wl_surface, as well as your
//! user-defined type holding any data you need to have associated with a struct. See its
//! documentation for details.

mod global;
mod handlers;
mod tree;
mod region;

use self::region::RegionData;
pub use self::tree::RoleStatus;
use self::tree::SurfaceData;
use wayland_server::{Client, EventLoopHandle, Init, resource_is_registered};

use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_region, wl_surface};

/// Description of which part of a surface
/// should be considered damaged and needs to be redrawn
pub enum Damage {
    /// The whole surface must be considered damaged (this is the default)
    Full,
    /// A rectangle containing the damaged zone, in surface coordinates
    Surface(Rectangle),
    /// A rectangle containing the smaazed zone, in buffer coordinates
    ///
    /// Note: Buffer scaling must be taken into consideration
    Buffer(Rectangle),
}

/// Data associated with a surface, aggreged by the handlers
///
/// Most of the fields of this struct represent a double-buffered state, which
/// should only be applied once a `commit` request is received from the surface.
///
/// You are responsible for setting those values as you see fit to avoid
/// processing them two times.
pub struct SurfaceAttributes<U> {
    /// Buffer defining the contents of the surface
    ///
    /// The tuple represent the coordinates of this buffer
    /// relative to the location of the current buffer.
    ///
    /// If set to `Some(None)`, it means the user specifically asked for the
    /// surface to be unmapped.
    ///
    /// You are free to set this field to `None` to avoid processing it several
    /// times. It'll be set to `Some(...)` if the user attaches a buffer (or NULL) to
    /// the surface.
    pub buffer: Option<Option<(wl_buffer::WlBuffer, (i32, i32))>>,
    /// Scale of the contents of the buffer, for higher-resolution contents.
    ///
    /// If it matches the one of the output displaying this surface, no change
    /// is necessary.
    pub buffer_scale: i32,
    /// Transform under which interpret the contents of the buffer
    ///
    /// If it matches the one of the output displaying this surface, no change
    /// is necessary.
    pub buffer_transform: wl_output::Transform,
    /// Region of the surface that is guaranteed to be opaque
    ///
    /// By default the whole surface is potentially transparent
    pub opaque_region: Option<RegionAttributes>,
    /// Region of the surface that is sensitive to user input
    ///
    /// By default the whole surface should be sensitive
    pub input_region: Option<RegionAttributes>,
    /// Damage rectangle
    ///
    /// Hint provided by the client to suggest that only this part
    /// of the surface was changed and needs to be redrawn
    pub damage: Damage,
    /// Subsurface-related attribute
    ///
    /// Is `Some` if this surface is a sub-surface
    ///
    /// **Warning:** Changing this field by yourself can cause panics.
    pub subsurface_attributes: Option<SubsurfaceAttributes>,
    /// User-controlled data
    ///
    /// This is your field to host whatever you need.
    pub user_data: U,
}

impl<U: Default> Default for SurfaceAttributes<U> {
    fn default() -> SurfaceAttributes<U> {
        SurfaceAttributes {
            buffer: None,
            buffer_scale: 1,
            buffer_transform: wl_output::Transform::Normal,
            opaque_region: None,
            input_region: None,
            damage: Damage::Full,
            subsurface_attributes: None,
            user_data: Default::default(),
        }
    }
}

/// Attributes defining the behaviour of a sub-surface relative to its parent
pub struct SubsurfaceAttributes {
    /// Horizontal location of the top-left corner of this sub-surface relative to
    /// the top-left corner of its parent
    pub x: i32,
    /// Vertical location of the top-left corner of this sub-surface relative to
    /// the top-left corner of its parent
    pub y: i32,
    /// Sync status of this sub-surface
    ///
    /// If `true`, this surface should be repainted synchronously with its parent
    /// if `false`, it should be considered independant of its parent regarding
    /// repaint timings.
    pub sync: bool,
}

impl Default for SubsurfaceAttributes {
    fn default() -> SubsurfaceAttributes {
        SubsurfaceAttributes {
            x: 0,
            y: 0,
            sync: true,
        }
    }
}

/// Kind of a rectangle part of a region
#[derive(Copy,Clone)]
pub enum RectangleKind {
    /// This rectangle should be added to the region
    Add,
    /// The intersection of this rectangle with the region should
    /// be removed from the region
    Subtract,
}

/// A rectangle defined by its top-left corner and dimensions
#[derive(Copy,Clone)]
pub struct Rectangle {
    /// horizontal position of the top-leftcorner of the rectangle, in surface coordinates
    pub x: i32,
    /// vertical position of the top-leftcorner of the rectangle, in surface coordinates
    pub y: i32,
    /// width of the rectangle
    pub width: i32,
    /// height of the rectangle
    pub height: i32,
}

/// Description of the contents of a region
///
/// A region is defined as an union and difference of rectangle.
///
/// This struct contains an ordered Vec containing the rectangles defining
/// a region. They should be added or substracted in this order to compute the
/// actual contents of the region.
#[derive(Clone)]
pub struct RegionAttributes {
    /// List of rectangle part of this region
    pub rects: Vec<(RectangleKind, Rectangle)>,
}

impl Default for RegionAttributes {
    fn default() -> RegionAttributes {
        RegionAttributes { rects: Vec::new() }
    }
}

/// A Compositor global token
///
/// This token can be cloned at will, and is the entry-point to
/// access data associated with the wl_surface and wl_region managed
/// by the `CompositorGlobal` that provided it.
#[derive(Copy,Clone)]
pub struct CompositorToken<U, H> {
    hid: usize,
    _data: ::std::marker::PhantomData<*mut U>,
    _handler: ::std::marker::PhantomData<*mut H>,
}

impl<U: Send + 'static, H: Handler + Send + 'static> CompositorToken<U, H> {
    /// Access the data of a surface
    ///
    /// The closure will be called with the contents of the data associated with this surface.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn with_surface_data<F>(&self, surface: &wl_surface::WlSurface, f: F)
        where F: FnOnce(&mut SurfaceAttributes<U>)
    {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe {
            SurfaceData::<U>::with_data(surface, f);
        }
    }

    /// Access the data of a surface tree
    ///
    /// The provided closure is called successively on the surface and all its child subsurfaces,
    /// in a depth-first order. This matches the order in which the surfaces are supposed to be
    /// drawn: top-most last.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn with_surface_tree<F>(&self, surface: &wl_surface::WlSurface, f: F) -> Result<(), ()>
        where F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>) -> bool
    {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe {
            SurfaceData::<U>::map_tree(surface, f);
        }
        Ok(())
    }

    /// Retrieve the parent of this surface
    ///
    /// Returns `None` is this surface is a root surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::get_parent(surface) }
    }

    /// Retrieve the children of this surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::get_children(surface) }
    }

    /// Retrieve the role status this surface
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn role_status(&self, surface: &wl_surface::WlSurface) -> RoleStatus {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::role_status(surface) }
    }

    /// Register that this surface has a role
    ///
    /// This makes this surface impossible to become a subsurface, as
    /// a surface can only have a single role at a time.
    ///
    /// Fails if the surface already has a role.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn give_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::give_role(surface) }
    }

    /// Register that this surface has no role
    ///
    /// It is a noop if this surface already didn't have one, but fails if
    /// the role was "subsurface". This role is automatically managed and as such
    /// cannot be removed manually.
    ///
    /// If the surface is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn remove_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(surface, self.hid),
                "Accessing the data of foreign surfaces is not supported.");
        unsafe { SurfaceData::<U>::remove_role(surface) }
    }

    /// Retrieve the metadata associated with a wl_region
    ///
    /// If the region is not managed by the CompositorGlobal that provided this token, this
    /// will panic (having more than one compositor is not supported).
    pub fn get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes {
        assert!(resource_is_registered::<_, CompositorHandler<U, H>>(region, self.hid),
                "Accessing the data of foreign regions is not supported.");
        unsafe { RegionData::get_attributes(region) }
    }
}

/// A struct handling the `wl_compositor` and `wl_subcompositor` globals
///
/// It allows you to choose a custom `U` type to store data you want
/// associated with the surfaces in their metadata, as well a providing
/// a sub-handler to handle the events defined by the `Handler` trait
/// defined in this module.
///
/// See the module-level documentation for instructions and examples of use.
pub struct CompositorHandler<U, H> {
    my_id: usize,
    log: ::slog::Logger,
    handler: H,
    _data: ::std::marker::PhantomData<U>,
}

impl<U, H> Init for CompositorHandler<U, H> {
    fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) {
        self.my_id = index;
        debug!(self.log, "Init finished")
    }
}

impl<U, H> CompositorHandler<U, H> {
    /// Create a new CompositorHandler
    pub fn new<L>(handler: H, logger: L) -> CompositorHandler<U, H>
        where L: Into<Option<::slog::Logger>>
    {
        let log = ::slog_or_stdlog(logger);
        CompositorHandler {
            my_id: ::std::usize::MAX,
            log: log.new(o!("smithay_module" => "compositor_handler")),
            handler: handler,
            _data: ::std::marker::PhantomData,
        }
    }

    /// Create a token to access the data associated to the objects managed by this handler.
    pub fn get_token(&self) -> CompositorToken<U, H> {
        assert!(self.my_id != ::std::usize::MAX,
                "CompositorHandler is not initialized yet.");
        CompositorToken {
            hid: self.my_id,
            _data: ::std::marker::PhantomData,
            _handler: ::std::marker::PhantomData,
        }
    }

    /// Access the underlying sub-handler
    pub fn get_handler(&mut self) -> &mut H {
        &mut self.handler
    }
}

/// Sub-handler trait for surface event handling
///
/// The global provided by Smithay cannot process these events for you, so they
/// are forwarded directly to a handler implementing this trait that you must provide
/// at creation of the `CompositorHandler`.
#[allow(unused_variables)]
pub trait Handler {
    /// See `wayland_server::protocol::wl_surface::Handler::commit`
    fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface) {}
    /// See `wayland_server::protocol::wl_surface::Handler::frame`
    fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface,
             callback: wl_callback::WlCallback) {
    }
}
<|endoftext|>


<commit_before>//! Pool configuration.
use std::fmt;
use debug_builders::DebugStruct;

use {HandleError, NoopErrorHandler, CustomizeConnection, NoopConnectionCustomizer};

/// A builder for `Config`.
///
/// See the documentation of `Config` for more details about the default value
/// and meaning of the configuration parameters.
#[derive(Debug)]
pub struct Builder<C, E> {
    c: Config<C, E>,
}

impl<C, E> Builder<C, E> {
    /// Constructs a new `Builder`.
    ///
    /// Parameters are initialized with their default values.
    #[inline]
    pub fn new() -> Builder<C, E> {
        Builder {
            c: Config::default(),
        }
    }

    /// Sets `pool_size`.
    ///
    /// # Panics
    ///
    /// Panics if `pool_size` is 0.
    #[inline]
    pub fn pool_size(mut self, pool_size: u32) -> Builder<C, E> {
        assert!(pool_size > 0, "pool_size must be positive");
        self.c.pool_size = pool_size;
        self
    }

    /// Sets `helper_threads`.
    ///
    /// # Panics
    ///
    /// Panics if `helper_threads` is 0.
    #[inline]
    pub fn helper_threads(mut self, helper_threads: u32) -> Builder<C, E> {
        assert!(helper_threads > 0, "helper_threads must be positive");
        self.c.helper_threads = helper_threads;
        self
    }

    /// Sets `test_on_check_out`.
    #[inline]
    pub fn test_on_check_out(mut self, test_on_check_out: bool) -> Builder<C, E> {
        self.c.test_on_check_out = test_on_check_out;
        self
    }

    /// Sets `initialization_fail_fast`.
    #[inline]
    pub fn initialization_fail_fast(mut self, initialization_fail_fast: bool) -> Builder<C, E> {
        self.c.initialization_fail_fast = initialization_fail_fast;
        self
    }

    /// Sets `connection_timeout` to the specified number of milliseconds.
    ///
    /// # Panics
    ///
    /// Panics if `connection_timeout` is nonpositive.
    #[inline]
    pub fn connection_timeout_ms(mut self, connection_timeout_ms: u32) -> Builder<C, E> {
        assert!(connection_timeout_ms > 0, "connection_timeout_ms must be positive");
        self.c.connection_timeout_ms = connection_timeout_ms;
        self
    }

    /// Sets the `error_handler`.
    #[inline]
    pub fn error_handler(mut self, error_handler: Box<HandleError<E>>) -> Builder<C, E> {
        self.c.error_handler = error_handler;
        self
    }

    /// Sets the `connection_customizer`.
    #[inline]
    pub fn connection_customizer(mut self, connection_customizer: Box<CustomizeConnection<C, E>>)
                                 -> Builder<C, E> {
        self.c.connection_customizer = connection_customizer;
        self
    }

    /// Consumes the `Builder`, turning it into a `Config`.
    #[inline]
    pub fn build(self) -> Config<C, E> {
        self.c
    }
}

/// A struct specifying the runtime configuration of a pool.
///
/// `Config` implements `Default`, which provides a set of reasonable default
/// values. It can be constructed using a `Builder`.
pub struct Config<C, E> {
    pool_size: u32,
    helper_threads: u32,
    test_on_check_out: bool,
    initialization_fail_fast: bool,
    connection_timeout_ms: u32,
    error_handler: Box<HandleError<E>>,
    connection_customizer: Box<CustomizeConnection<C, E>>,
}

impl<C, E> fmt::Debug for Config<C, E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        DebugStruct::new(fmt, "Config")
            .field("pool_size", &self.pool_size)
            .field("helper_threads", &self.helper_threads)
            .field("test_on_check_out", &self.test_on_check_out)
            .field("initialization_fail_fast", &self.initialization_fail_fast)
            .field("connection_timeout_ms", &self.connection_timeout_ms)
            .finish()
    }
}

impl<C, E> Default for Config<C, E> {
    #[inline]
    fn default() -> Config<C, E> {
        Config {
            pool_size: 10,
            helper_threads: 3,
            test_on_check_out: true,
            initialization_fail_fast: true,
            connection_timeout_ms: 30 * 1000,
            error_handler: Box::new(NoopErrorHandler),
            connection_customizer: Box::new(NoopConnectionCustomizer),
        }
    }
}

impl<C, E> Config<C, E> {
    /// Creates a new `Builder` which can be used to construct a customized
    /// `Config`.
    ///
    /// All parameters are initialized to their default values.
    #[inline]
    pub fn builder() -> Builder<C, E> {
        Builder::new()
    }

    /// The number of connections managed by the pool.
    ///
    /// Defaults to 10.
    #[inline]
    pub fn pool_size(&self) -> u32 {
        self.pool_size
    }

    /// The number of threads that the pool will use for asynchronous
    /// operations such as connection creation and health checks.
    ///
    /// Defaults to 3.
    #[inline]
    pub fn helper_threads(&self) -> u32 {
        self.helper_threads
    }

    /// If true, the health of a connection will be verified via a call to
    /// `ConnectionManager::is_valid` before it is checked out of the pool.
    ///
    /// Defaults to true.
    #[inline]
    pub fn test_on_check_out(&self) -> bool {
        self.test_on_check_out
    }

    /// If true, `Pool::new` will synchronously initialize its connections,
    /// returning an error if they could not be created.
    ///
    /// Defaults to true.
    #[inline]
    pub fn initialization_fail_fast(&self) -> bool {
        self.initialization_fail_fast
    }

    /// Calls to `Pool::get` will wait for this many milliseconds for a
    /// connection to become available before returning an error.
    ///
    /// Defaults to 30 seconds.
    #[inline]
    pub fn connection_timeout_ms(&self) -> u32 {
        self.connection_timeout_ms
    }

    /// The handler for error reported in the pool.
    ///
    /// Defaults to `r2d2::NoopErrorHandler`.
    #[inline]
    pub fn error_handler(&self) -> &HandleError<E> {
        &*self.error_handler
    }

    /// The connection customizer used by the pool.
    ///
    /// Defaults to `r2d2::NoopConnectionCustomizer`.
    #[inline]
    pub fn connection_customizer(&self) -> &CustomizeConnection<C, E> {
        &*self.connection_customizer
    }
}
<commit_msg>Doc tweak<commit_after>//! Pool configuration.
use std::fmt;
use debug_builders::DebugStruct;

use {HandleError, NoopErrorHandler, CustomizeConnection, NoopConnectionCustomizer};

/// A builder for `Config`.
///
/// See the documentation of `Config` for more details about the default value
/// and meaning of the configuration parameters.
#[derive(Debug)]
pub struct Builder<C, E> {
    c: Config<C, E>,
}

impl<C, E> Builder<C, E> {
    /// Constructs a new `Builder`.
    ///
    /// Parameters are initialized with their default values.
    #[inline]
    pub fn new() -> Builder<C, E> {
        Builder {
            c: Config::default(),
        }
    }

    /// Sets `pool_size`.
    ///
    /// # Panics
    ///
    /// Panics if `pool_size` is 0.
    #[inline]
    pub fn pool_size(mut self, pool_size: u32) -> Builder<C, E> {
        assert!(pool_size > 0, "pool_size must be positive");
        self.c.pool_size = pool_size;
        self
    }

    /// Sets `helper_threads`.
    ///
    /// # Panics
    ///
    /// Panics if `helper_threads` is 0.
    #[inline]
    pub fn helper_threads(mut self, helper_threads: u32) -> Builder<C, E> {
        assert!(helper_threads > 0, "helper_threads must be positive");
        self.c.helper_threads = helper_threads;
        self
    }

    /// Sets `test_on_check_out`.
    #[inline]
    pub fn test_on_check_out(mut self, test_on_check_out: bool) -> Builder<C, E> {
        self.c.test_on_check_out = test_on_check_out;
        self
    }

    /// Sets `initialization_fail_fast`.
    #[inline]
    pub fn initialization_fail_fast(mut self, initialization_fail_fast: bool) -> Builder<C, E> {
        self.c.initialization_fail_fast = initialization_fail_fast;
        self
    }

    /// Sets `connection_timeout_ms` to the specified number of milliseconds.
    ///
    /// # Panics
    ///
    /// Panics if `connection_timeout_ms` is 0.
    #[inline]
    pub fn connection_timeout_ms(mut self, connection_timeout_ms: u32) -> Builder<C, E> {
        assert!(connection_timeout_ms > 0, "connection_timeout_ms must be positive");
        self.c.connection_timeout_ms = connection_timeout_ms;
        self
    }

    /// Sets the `error_handler`.
    #[inline]
    pub fn error_handler(mut self, error_handler: Box<HandleError<E>>) -> Builder<C, E> {
        self.c.error_handler = error_handler;
        self
    }

    /// Sets the `connection_customizer`.
    #[inline]
    pub fn connection_customizer(mut self, connection_customizer: Box<CustomizeConnection<C, E>>)
                                 -> Builder<C, E> {
        self.c.connection_customizer = connection_customizer;
        self
    }

    /// Consumes the `Builder`, turning it into a `Config`.
    #[inline]
    pub fn build(self) -> Config<C, E> {
        self.c
    }
}

/// A struct specifying the runtime configuration of a pool.
///
/// `Config` implements `Default`, which provides a set of reasonable default
/// values. It can be constructed using a `Builder`.
pub struct Config<C, E> {
    pool_size: u32,
    helper_threads: u32,
    test_on_check_out: bool,
    initialization_fail_fast: bool,
    connection_timeout_ms: u32,
    error_handler: Box<HandleError<E>>,
    connection_customizer: Box<CustomizeConnection<C, E>>,
}

impl<C, E> fmt::Debug for Config<C, E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        DebugStruct::new(fmt, "Config")
            .field("pool_size", &self.pool_size)
            .field("helper_threads", &self.helper_threads)
            .field("test_on_check_out", &self.test_on_check_out)
            .field("initialization_fail_fast", &self.initialization_fail_fast)
            .field("connection_timeout_ms", &self.connection_timeout_ms)
            .finish()
    }
}

impl<C, E> Default for Config<C, E> {
    #[inline]
    fn default() -> Config<C, E> {
        Config {
            pool_size: 10,
            helper_threads: 3,
            test_on_check_out: true,
            initialization_fail_fast: true,
            connection_timeout_ms: 30 * 1000,
            error_handler: Box::new(NoopErrorHandler),
            connection_customizer: Box::new(NoopConnectionCustomizer),
        }
    }
}

impl<C, E> Config<C, E> {
    /// Creates a new `Builder` which can be used to construct a customized
    /// `Config`.
    ///
    /// All parameters are initialized to their default values.
    #[inline]
    pub fn builder() -> Builder<C, E> {
        Builder::new()
    }

    /// The number of connections managed by the pool.
    ///
    /// Defaults to 10.
    #[inline]
    pub fn pool_size(&self) -> u32 {
        self.pool_size
    }

    /// The number of threads that the pool will use for asynchronous
    /// operations such as connection creation and health checks.
    ///
    /// Defaults to 3.
    #[inline]
    pub fn helper_threads(&self) -> u32 {
        self.helper_threads
    }

    /// If true, the health of a connection will be verified via a call to
    /// `ConnectionManager::is_valid` before it is checked out of the pool.
    ///
    /// Defaults to true.
    #[inline]
    pub fn test_on_check_out(&self) -> bool {
        self.test_on_check_out
    }

    /// If true, `Pool::new` will synchronously initialize its connections,
    /// returning an error if they could not be created.
    ///
    /// Defaults to true.
    #[inline]
    pub fn initialization_fail_fast(&self) -> bool {
        self.initialization_fail_fast
    }

    /// Calls to `Pool::get` will wait for this many milliseconds for a
    /// connection to become available before returning an error.
    ///
    /// Defaults to 30 seconds.
    #[inline]
    pub fn connection_timeout_ms(&self) -> u32 {
        self.connection_timeout_ms
    }

    /// The handler for error reported in the pool.
    ///
    /// Defaults to `r2d2::NoopErrorHandler`.
    #[inline]
    pub fn error_handler(&self) -> &HandleError<E> {
        &*self.error_handler
    }

    /// The connection customizer used by the pool.
    ///
    /// Defaults to `r2d2::NoopConnectionCustomizer`.
    #[inline]
    pub fn connection_customizer(&self) -> &CustomizeConnection<C, E> {
        &*self.connection_customizer
    }
}
<|endoftext|>


<commit_before>use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Deref;
use std::str::FromStr;

use error::*;
use ser::ConfigSerializer;
use source::Source;

use path;
use value::{Table, Value, ValueKind, ValueWithKey};

#[derive(Clone, Debug)]
enum ConfigKind {
    // A mutable configuration. This is the default.
    Mutable {
        defaults: HashMap<path::Expression, Value>,
        overrides: HashMap<path::Expression, Value>,
        sources: Vec<Box<Source + Send + Sync>>,
    },

    // A frozen configuration.
    // Configuration can no longer be mutated.
    Frozen,
}

impl Default for ConfigKind {
    fn default() -> Self {
        ConfigKind::Mutable {
            defaults: HashMap::new(),
            overrides: HashMap::new(),
            sources: Vec::new(),
        }
    }
}

/// A prioritized configuration repository. It maintains a set of
/// configuration sources, fetches values to populate those, and provides
/// them according to the source's priority.
#[derive(Default, Clone, Debug)]
pub struct Config {
    kind: ConfigKind,

    /// Root of the cached configuration.
    pub cache: Value,
}

impl Config {
    pub fn new() -> Self {
        Self {
            kind: ConfigKind::default(),
            // Config root should be instantiated as an empty table
            // to avoid deserialization errors.
            cache: Value::new(None, Table::new()),
        }
    }

    /// Merge in a configuration property source.
    pub fn merge<T>(&mut self, source: T) -> Result<&mut Config>
    where
        T: 'static,
        T: Source + Send + Sync,
    {
        match self.kind {
            ConfigKind::Mutable {
                ref mut sources, ..
            } => {
                sources.push(Box::new(source));
            }

            ConfigKind::Frozen => {
                return Err(ConfigError::Frozen);
            }
        }

        self.refresh()
    }

    /// Refresh the configuration cache with fresh
    /// data from added sources.
    ///
    /// Configuration is automatically refreshed after a mutation
    /// operation (`set`, `merge`, `set_default`, etc.).
    pub fn refresh(&mut self) -> Result<&mut Config> {
        self.cache = match self.kind {
            // TODO: We need to actually merge in all the stuff
            ConfigKind::Mutable {
                ref overrides,
                ref sources,
                ref defaults,
            } => {
                let mut cache: Value = HashMap::<String, Value>::new().into();

                // Add defaults
                for (key, val) in defaults {
                    key.set(&mut cache, val.clone());
                }

                // Add sources
                sources.collect_to(&mut cache)?;

                // Add overrides
                for (key, val) in overrides {
                    key.set(&mut cache, val.clone());
                }

                cache
            }

            ConfigKind::Frozen => {
                return Err(ConfigError::Frozen);
            }
        };

        Ok(self)
    }

    pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
    where
        T: Into<Value>,
    {
        match self.kind {
            ConfigKind::Mutable {
                ref mut defaults, ..
            } => {
                defaults.insert(key.to_lowercase().parse()?, value.into());
            }

            ConfigKind::Frozen => return Err(ConfigError::Frozen),
        };

        self.refresh()
    }

    pub fn set_defaults<T>(&mut self, value: &T) -> Result<&mut Config>
    where
        T: Serialize,
    {
        match self.kind {
            ConfigKind::Mutable {
                ref mut defaults, ..
            } => {
                for (key, val) in Self::try_from(&value)?.collect()? {
                    defaults.insert(key.parse()?, val);
                }
            }

            ConfigKind::Frozen => return Err(ConfigError::Frozen),
        }

        self.refresh()
    }

    pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
    where
        T: Into<Value>,
    {
        match self.kind {
            ConfigKind::Mutable {
                ref mut overrides, ..
            } => {
                overrides.insert(key.to_lowercase().parse()?, value.into());
            }

            ConfigKind::Frozen => return Err(ConfigError::Frozen),
        };

        self.refresh()
    }

    pub fn get<'de, T: Deserialize<'de>>(&self, key: &'de str) -> Result<T> {
        // Parse the key into a path expression
        let expr: path::Expression = key.to_lowercase().parse()?;

        // Traverse the cache using the path to (possibly) retrieve a value
        let value = expr.get(&self.cache).cloned();

        match value {
            Some(value) => {
                // Deserialize the received value into the requested type
                T::deserialize(ValueWithKey::new(value, key))
            }

            None => Err(ConfigError::NotFound(key.into())),
        }
    }

    pub fn get_str(&self, key: &str) -> Result<String> {
        self.get(key).and_then(Value::into_str)
    }

    pub fn get_int(&self, key: &str) -> Result<i64> {
        self.get(key).and_then(Value::into_int)
    }

    pub fn get_float(&self, key: &str) -> Result<f64> {
        self.get(key).and_then(Value::into_float)
    }

    pub fn get_bool(&self, key: &str) -> Result<bool> {
        self.get(key).and_then(Value::into_bool)
    }

    pub fn get_table(&self, key: &str) -> Result<HashMap<String, Value>> {
        self.get(key).and_then(Value::into_table)
    }

    pub fn get_array(&self, key: &str) -> Result<Vec<Value>> {
        self.get(key).and_then(Value::into_array)
    }

    /// Attempt to deserialize the entire configuration into the requested type.
    pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> {
        T::deserialize(self)
    }

    /// Attempt to serialize the entire configuration from the given type.
    pub fn try_from<T: Serialize>(from: &T) -> Result<Self> {
        let mut serializer = ConfigSerializer::default();
        from.serialize(&mut serializer)?;
        Ok(serializer.output)
    }

    /// Attempt to serialize the entire configuration from the given type
    /// as default values.
    pub fn try_defaults_from<T: Serialize>(from: &T) -> Result<Self> {
        let mut c = Self::new();
        c.set_defaults(from)?;
        Ok(c)
    }

    #[deprecated(since = "0.7.0", note = "please use 'try_into' instead")]
    pub fn deserialize<'de, T: Deserialize<'de>>(self) -> Result<T> {
        self.try_into()
    }
}

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

    fn collect(&self) -> Result<HashMap<String, Value>> {
        self.cache.clone().into_table()
    }
}
<commit_msg>Add missing doc<commit_after>use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Deref;
use std::str::FromStr;

use error::*;
use ser::ConfigSerializer;
use source::Source;

use path;
use value::{Table, Value, ValueKind, ValueWithKey};

#[derive(Clone, Debug)]
enum ConfigKind {
    // A mutable configuration. This is the default.
    Mutable {
        defaults: HashMap<path::Expression, Value>,
        overrides: HashMap<path::Expression, Value>,
        sources: Vec<Box<Source + Send + Sync>>,
    },

    // A frozen configuration.
    // Configuration can no longer be mutated.
    Frozen,
}

impl Default for ConfigKind {
    fn default() -> Self {
        ConfigKind::Mutable {
            defaults: HashMap::new(),
            overrides: HashMap::new(),
            sources: Vec::new(),
        }
    }
}

/// A prioritized configuration repository. It maintains a set of
/// configuration sources, fetches values to populate those, and provides
/// them according to the source's priority.
#[derive(Default, Clone, Debug)]
pub struct Config {
    kind: ConfigKind,

    /// Root of the cached configuration.
    pub cache: Value,
}

impl Config {
    pub fn new() -> Self {
        Self {
            kind: ConfigKind::default(),
            // Config root should be instantiated as an empty table
            // to avoid deserialization errors.
            cache: Value::new(None, Table::new()),
        }
    }

    /// Merge in a configuration property source.
    pub fn merge<T>(&mut self, source: T) -> Result<&mut Config>
    where
        T: 'static,
        T: Source + Send + Sync,
    {
        match self.kind {
            ConfigKind::Mutable {
                ref mut sources, ..
            } => {
                sources.push(Box::new(source));
            }

            ConfigKind::Frozen => {
                return Err(ConfigError::Frozen);
            }
        }

        self.refresh()
    }

    /// Refresh the configuration cache with fresh
    /// data from added sources.
    ///
    /// Configuration is automatically refreshed after a mutation
    /// operation (`set`, `merge`, `set_default`, etc.).
    pub fn refresh(&mut self) -> Result<&mut Config> {
        self.cache = match self.kind {
            // TODO: We need to actually merge in all the stuff
            ConfigKind::Mutable {
                ref overrides,
                ref sources,
                ref defaults,
            } => {
                let mut cache: Value = HashMap::<String, Value>::new().into();

                // Add defaults
                for (key, val) in defaults {
                    key.set(&mut cache, val.clone());
                }

                // Add sources
                sources.collect_to(&mut cache)?;

                // Add overrides
                for (key, val) in overrides {
                    key.set(&mut cache, val.clone());
                }

                cache
            }

            ConfigKind::Frozen => {
                return Err(ConfigError::Frozen);
            }
        };

        Ok(self)
    }

    pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
    where
        T: Into<Value>,
    {
        match self.kind {
            ConfigKind::Mutable {
                ref mut defaults, ..
            } => {
                defaults.insert(key.to_lowercase().parse()?, value.into());
            }

            ConfigKind::Frozen => return Err(ConfigError::Frozen),
        };

        self.refresh()
    }

    /// Set the configuration defaults by serializing them from given value.
    pub fn set_defaults<T>(&mut self, value: &T) -> Result<&mut Config>
    where
        T: Serialize,
    {
        match self.kind {
            ConfigKind::Mutable {
                ref mut defaults, ..
            } => {
                for (key, val) in Self::try_from(&value)?.collect()? {
                    defaults.insert(key.parse()?, val);
                }
            }

            ConfigKind::Frozen => return Err(ConfigError::Frozen),
        }

        self.refresh()
    }

    pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut Config>
    where
        T: Into<Value>,
    {
        match self.kind {
            ConfigKind::Mutable {
                ref mut overrides, ..
            } => {
                overrides.insert(key.to_lowercase().parse()?, value.into());
            }

            ConfigKind::Frozen => return Err(ConfigError::Frozen),
        };

        self.refresh()
    }

    pub fn get<'de, T: Deserialize<'de>>(&self, key: &'de str) -> Result<T> {
        // Parse the key into a path expression
        let expr: path::Expression = key.to_lowercase().parse()?;

        // Traverse the cache using the path to (possibly) retrieve a value
        let value = expr.get(&self.cache).cloned();

        match value {
            Some(value) => {
                // Deserialize the received value into the requested type
                T::deserialize(ValueWithKey::new(value, key))
            }

            None => Err(ConfigError::NotFound(key.into())),
        }
    }

    pub fn get_str(&self, key: &str) -> Result<String> {
        self.get(key).and_then(Value::into_str)
    }

    pub fn get_int(&self, key: &str) -> Result<i64> {
        self.get(key).and_then(Value::into_int)
    }

    pub fn get_float(&self, key: &str) -> Result<f64> {
        self.get(key).and_then(Value::into_float)
    }

    pub fn get_bool(&self, key: &str) -> Result<bool> {
        self.get(key).and_then(Value::into_bool)
    }

    pub fn get_table(&self, key: &str) -> Result<HashMap<String, Value>> {
        self.get(key).and_then(Value::into_table)
    }

    pub fn get_array(&self, key: &str) -> Result<Vec<Value>> {
        self.get(key).and_then(Value::into_array)
    }

    /// Attempt to deserialize the entire configuration into the requested type.
    pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> {
        T::deserialize(self)
    }

    /// Attempt to serialize the entire configuration from the given type.
    pub fn try_from<T: Serialize>(from: &T) -> Result<Self> {
        let mut serializer = ConfigSerializer::default();
        from.serialize(&mut serializer)?;
        Ok(serializer.output)
    }

    /// Attempt to serialize the entire configuration from the given type
    /// as default values.
    pub fn try_defaults_from<T: Serialize>(from: &T) -> Result<Self> {
        let mut c = Self::new();
        c.set_defaults(from)?;
        Ok(c)
    }

    #[deprecated(since = "0.7.0", note = "please use 'try_into' instead")]
    pub fn deserialize<'de, T: Deserialize<'de>>(self) -> Result<T> {
        self.try_into()
    }
}

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

    fn collect(&self) -> Result<HashMap<String, Value>> {
        self.cache.clone().into_table()
    }
}
<|endoftext|>


<commit_before>use shell::Shell;
use shell::status::*;

macro_rules! string_function {
    ($method:tt) => (
        pub fn $method(args: &[&str], _: &mut Shell) -> i32 {
            match args.len() {
                0...2 => {
                    eprintln!("ion: $method: two arguments must be supplied");
                    return BAD_ARG
                }
                3 => if args[1].$method(&args[2]) { SUCCESS } else { FAILURE }
                _ => {
                    for arg in args[2..].iter() {
                        if args[1].$method(arg) { return SUCCESS }
                    }
                    FAILURE
                }
            }
        }
    )
}

string_function!(starts_with);
string_function!(ends_with);
string_function!(contains);<commit_msg>Fix Stable Builds<commit_after>use shell::Shell;
use shell::status::*;

macro_rules! string_function {
    ($method:tt) => (
        pub fn $method(args: &[&str], _: &mut Shell) -> i32 {
            match args.len() {
                0...2 => {
                    eprintln!("ion: $method: two arguments must be supplied");
                    return BAD_ARG
                },
                3 => if args[1].$method(&args[2]) { SUCCESS } else { FAILURE },
                _ => {
                    for arg in args[2..].iter() {
                        if args[1].$method(arg) { return SUCCESS }
                    }
                    FAILURE
                }
            }
        }
    )
}

string_function!(starts_with);
string_function!(ends_with);
string_function!(contains);
<|endoftext|>