<commit_before>// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Server management via Compute API.

use std::collections::HashMap;
use std::fmt::Debug;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::time::Duration;

use chrono::{DateTime, FixedOffset};
use fallible_iterator::{IntoFallibleIterator, FallibleIterator};
use serde::Serialize;

use super::super::{Error, ErrorKind, Result, Sort, Waiter};
use super::super::service::{ListResources, ResourceId, ResourceIterator};
use super::super::session::Session;
use super::super::types;
use super::super::utils::{self, Query};
use super::base::V2API;
use super::protocol;

/// A query to server list.
#[derive(Clone, Debug)]
pub struct ServerQuery<'session> {
    session: &'session Session,
    query: Query,
    can_paginate: bool,
}

/// Structure representing a single server.
#[derive(Clone, Debug)]
pub struct Server<'session> {
    session: &'session Session,
    inner: protocol::Server
}

/// Structure representing a summary of a single server.
#[derive(Clone, Debug)]
pub struct ServerSummary<'session> {
    session: &'session Session,
    inner: protocol::ServerSummary
}

/// Waiter for server status to change.
#[derive(Debug)]
pub struct ServerStatusWaiter<'server> {
    server: &'server mut Server<'server>,
    target: protocol::ServerStatus
}

impl<'session> Server<'session> {
    /// Load a Server object.
    pub(crate) fn new<Id: AsRef<str>>(session: &'session Session, id: Id)
            -> Result<Server<'session>> {
        let inner = session.get_server(id)?;
        Ok(Server {
            session: session,
            inner: inner
        })
    }

    /// Refresh the server.
    pub fn refresh(&mut self) -> Result<()> {
        self.inner = self.session.get_server(&self.inner.id)?;
        Ok(())
    }

    /// Get a reference to IPv4 address.
    ///
    /// If not None, this address should be used to access the server instead
    /// of one from the `addresses` method.
    pub fn access_ipv4(&self) -> &Option<Ipv4Addr> {
        &self.inner.accessIPv4
    }

    /// Get a reference to IPv6 address.
    ///
    /// If not None, this address should be used to access the server instead
    /// of one from the `addresses` method.
    pub fn access_ipv6(&self) -> &Option<Ipv6Addr> {
        &self.inner.accessIPv6
    }

    /// Get a reference to associated addresses.
    pub fn addresses(&self) -> &HashMap<String, Vec<protocol::ServerAddress>> {
        &self.inner.addresses
    }

    /// Get a reference to the availability zone.
    pub fn availability_zone(&self) -> &String {
        &self.inner.availability_zone
    }

    /// Get a reference to creation date and time.
    pub fn created_at(&self) -> &DateTime<FixedOffset> {
        &self.inner.created
    }

    /// Get a reference to server unique ID.
    pub fn id(&self) -> &String {
        &self.inner.id
    }

    /// Get a reference to the image.
    ///
    /// May be None if the server was created from a volume.
    pub fn image_id(&self) -> Option<&String> {
        match self.inner.image {
            Some(ref image) => Some(&image.id),
            None => None
        }
    }

    /// Whether the server has an image.
    ///
    /// May return `false` if the server was created from a volume.
    pub fn has_image(&self) -> bool {
        self.inner.image.is_some()
    }

    /// Get a reference to server name.
    pub fn name(&self) -> &String {
        &self.inner.name
    }

    /// Get server power state.
    pub fn power_state(&self) -> protocol::ServerPowerState {
        self.inner.power_state
    }

    /// Get server status.
    pub fn status(&self) -> protocol::ServerStatus {
        self.inner.status
    }

    /// Get a reference to last update date and time.
    pub fn updated_at(&self) -> &DateTime<FixedOffset> {
        &self.inner.updated
    }

    /// Start the server, optionally wait for it to be active.
    pub fn start(&'session mut self) -> Result<ServerStatusWaiter<'session>> {
        self.session.server_simple_action(&self.inner.id, "os-start")?;
        Ok(ServerStatusWaiter {
            server: self,
            target: protocol::ServerStatus::Active
        })
    }

    /// Stop the server, optionally wait for it to be powered off.
    pub fn stop(&'session mut self) -> Result<ServerStatusWaiter<'session>> {
        self.session.server_simple_action(&self.inner.id, "os-stop")?;
        Ok(ServerStatusWaiter {
            server: self,
            target: protocol::ServerStatus::ShutOff
        })
    }

    /// Delete the server.
    pub fn delete(self) -> Result<()> {
        // TODO(dtantsur): implement wait
        self.session.delete_server(&self.inner.id)
    }
}

impl<'server> ServerStatusWaiter<'server> {
    /// Current state of the server.
    ///
    /// Valid as of the last poll.
    pub fn current(&self) -> &Server<'server> {
        self.server
    }
}

impl<'server> Waiter<()> for ServerStatusWaiter<'server> {
    fn default_wait_timeout(&self) -> Option<Duration> {
        // TODO(dtantsur): vary depending on target?
        Some(Duration::new(600, 0))
    }

    fn default_delay(&self) -> Duration {
        Duration::new(1, 0)
    }

    fn timeout_error_message(&self) -> String {
        format!("Timeout waiting for server {} to reach state {}",
                self.server.id(), self.target)
    }

    fn poll(&mut self) -> Result<Option<()>> {
        self.server.refresh()?;
        if self.server.status() == self.target {
            debug!("Server {} reached state {}", self.server.id(), self.target);
            Ok(Some(()))
        } else if self.server.status() == protocol::ServerStatus::Error {
            debug!("Failed to move server {} to {} - status is ERROR",
                   self.server.id(), self.target);
            Err(Error::new(ErrorKind::OperationFailed,
                           format!("Server {} got into ERROR state",
                                   self.server.id())))
        } else {
            trace!("Still waiting for server {} to get to state {}, current is {}",
                   self.server.id(), self.server.status(), self.target);
            Ok(None)
        }
    }
}

impl<'session> ServerSummary<'session> {
    /// Get a reference to server unique ID.
    pub fn id(&self) -> &String {
        &self.inner.id
    }

    /// Get a reference to server name.
    pub fn name(&self) -> &String {
        &self.inner.name
    }

    /// Get details.
    pub fn details(&self) -> Result<Server<'session>> {
        Server::new(self.session, &self.inner.id)
    }

    /// Delete the server.
    pub fn delete(self) -> Result<()> {
        // TODO(dtantsur): implement wait
        self.session.delete_server(&self.inner.id)
    }
}

impl<'session> ServerQuery<'session> {
    pub(crate) fn new(session: &'session Session) -> ServerQuery<'session> {
        ServerQuery {
            session: session,
            query: Query::new(),
            can_paginate: true,
        }
    }

    /// Add marker to the request.
    ///
    /// Using this disables automatic pagination.
    pub fn with_marker<T: Into<String>>(mut self, marker: T) -> Self {
        self.can_paginate = false;
        self.query.push_str("marker", marker);
        self
    }

    /// Add limit to the request.
    ///
    /// Using this disables automatic pagination.
    pub fn with_limit(mut self, limit: usize) -> Self {
        self.can_paginate = false;
        self.query.push("limit", limit);
        self
    }

    /// Add sorting to the request.
    pub fn sort_by(mut self, sort: Sort<protocol::ServerSortKey>) -> Self {
        let (field, direction) = sort.into();
        self.query.push_str("sort_key", field);
        self.query.push("sort_dir", direction);
        self
    }

    /// Filter by IPv4 address that should be used to access the server.
    pub fn with_access_ip_v4(mut self, value: Ipv4Addr) -> Self {
        self.query.push("access_ip_v4", value);
        self
    }

    /// Filter by IPv6 address that should be used to access the server.
    pub fn with_access_ip_v6(mut self, value: Ipv6Addr) -> Self {
        self.query.push("access_ipv6", value);
        self
    }

    /// Filter by availability zone.
    pub fn with_availability_zone<T: Into<String>>(mut self, value: T) -> Self {
        self.query.push_str("availability_zone", value);
        self
    }

    /// Filter by flavor.
    pub fn with_flavor<T: Into<types::FlavorId>>(mut self, value: T) -> Self {
        self.query.push_str("flavor", value.into());
        self
    }

    /// Filter by host name.
    pub fn with_hostname<T: Into<String>>(mut self, value: T) -> Self {
        self.query.push_str("hostname", value);
        self
    }

    /// Filter by image ID.
    pub fn with_image<T: Into<types::ImageId>>(mut self, value: T) -> Self {
        self.query.push_str("image", value.into());
        self
    }

    /// Filter by an IPv4 address.
    pub fn with_ip_v4(mut self, value: Ipv4Addr) -> Self {
        self.query.push("ip", value);
        self
    }

    /// Filter by an IPv6 address.
    pub fn with_ip_v6(mut self, value: Ipv6Addr) -> Self {
        self.query.push("ip6", value);
        self
    }

    /// Filter by server name (a database regular expression).
    pub fn with_name<T: Into<String>>(mut self, value: T) -> Self {
        self.query.push_str("name", value);
        self
    }

    /// Filter by project ID (also commonly known as tenant ID).
    pub fn with_project<T: Into<types::ProjectId>>(mut self, value: T) -> Self {
        self.query.push_str("project_id", value.into());
        self
    }

    /// Filter by server status.
    pub fn with_status(mut self, value: protocol::ServerStatus) -> Self {
        self.query.push_str("status", value.to_string());
        self
    }

    /// Filter by user ID.
    pub fn with_user<T: Into<types::UserId>>(mut self, value: T) -> Self {
        self.query.push_str("user_id", value.into());
        self
    }

    /// Convert this query into an iterator executing the request.
    ///
    /// This iterator yields only `ServerSummary` objects, containing
    /// IDs and names. Use `into_iter_detailed` for full `Server` objects.
    ///
    /// Returns a `FallibleIterator`, which is an iterator with each `next`
    /// call returning a `Result`.
    ///
    /// Note that no requests are done until you start iterating.
    pub fn into_iter(self) -> ResourceIterator<'session, ServerSummary<'session>> {
        ResourceIterator::new(self.session, self.query)
    }

    /// Convert this query into an iterator executing the request.
    ///
    /// This iterator yields full `Server` objects. If you only need IDs
    /// and/or names, use `into_iter` to save bandwidth.
    ///
    /// Returns a `FallibleIterator`, which is an iterator with each `next`
    /// call returning a `Result`.
    ///
    /// Note that no requests are done until you start iterating.
    pub fn into_iter_detailed(self) -> ResourceIterator<'session, Server<'session>> {
        ResourceIterator::new(self.session, self.query)
    }

    /// Execute this request and return all results.
    ///
    /// A convenience shortcut for `self.into_iter().collect()`.
    pub fn all(self) -> Result<Vec<ServerSummary<'session>>> {
        self.into_iter().collect()
    }

    /// Return one and exactly one result.
    ///
    /// Fails with `ResourceNotFound` if the query produces no results and
    /// with `TooManyItems` if the query produces more than one result.
    pub fn one(mut self) -> Result<ServerSummary<'session>> {
        if self.can_paginate {
            // We need only one result. We fetch maximum two to be able
            // to check if the query yieled more than one result.
            self.query.push("limit", 2);
        }

        utils::fetch_one(self)
    }
}

impl<'session> ResourceId for ServerSummary<'session> {
    fn resource_id(&self) -> String {
        self.id().clone()
    }
}

impl<'session> ListResources<'session> for ServerSummary<'session> {
    const DEFAULT_LIMIT: usize = 50;

    fn list_resources<Q: Serialize + Debug>(session: &'session Session, query: Q)
            -> Result<Vec<ServerSummary<'session>>> {
        Ok(session.list_servers(&query)?.into_iter().map(|srv| ServerSummary {
            session: session,
            inner: srv
        }).collect())
    }
}

impl<'session> ResourceId for Server<'session> {
    fn resource_id(&self) -> String {
        self.id().clone()
    }
}

impl<'session> ListResources<'session> for Server<'session> {
    const DEFAULT_LIMIT: usize = 50;

    fn list_resources<Q: Serialize + Debug>(session: &'session Session, query: Q)
            -> Result<Vec<Server<'session>>> {
        Ok(session.list_servers_detail(&query)?.into_iter().map(|srv| Server {
            session: session,
            inner: srv
        }).collect())
    }
}

impl<'session> IntoFallibleIterator for ServerQuery<'session> {
    type Item = ServerSummary<'session>;

    type Error = Error;

    type IntoIter = ResourceIterator<'session, ServerSummary<'session>>;

    fn into_fallible_iterator(self) -> ResourceIterator<'session, ServerSummary<'session>> {
        self.into_iter()
    }
}
<commit_msg>feat(compute): use Into<Ipv4Addr> and Into<Ipv6Addr><commit_after>// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Server management via Compute API.

use std::collections::HashMap;
use std::fmt::Debug;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::time::Duration;

use chrono::{DateTime, FixedOffset};
use fallible_iterator::{IntoFallibleIterator, FallibleIterator};
use serde::Serialize;

use super::super::{Error, ErrorKind, Result, Sort, Waiter};
use super::super::service::{ListResources, ResourceId, ResourceIterator};
use super::super::session::Session;
use super::super::types;
use super::super::utils::{self, Query};
use super::base::V2API;
use super::protocol;

/// A query to server list.
#[derive(Clone, Debug)]
pub struct ServerQuery<'session> {
    session: &'session Session,
    query: Query,
    can_paginate: bool,
}

/// Structure representing a single server.
#[derive(Clone, Debug)]
pub struct Server<'session> {
    session: &'session Session,
    inner: protocol::Server
}

/// Structure representing a summary of a single server.
#[derive(Clone, Debug)]
pub struct ServerSummary<'session> {
    session: &'session Session,
    inner: protocol::ServerSummary
}

/// Waiter for server status to change.
#[derive(Debug)]
pub struct ServerStatusWaiter<'server> {
    server: &'server mut Server<'server>,
    target: protocol::ServerStatus
}

impl<'session> Server<'session> {
    /// Load a Server object.
    pub(crate) fn new<Id: AsRef<str>>(session: &'session Session, id: Id)
            -> Result<Server<'session>> {
        let inner = session.get_server(id)?;
        Ok(Server {
            session: session,
            inner: inner
        })
    }

    /// Refresh the server.
    pub fn refresh(&mut self) -> Result<()> {
        self.inner = self.session.get_server(&self.inner.id)?;
        Ok(())
    }

    /// Get a reference to IPv4 address.
    ///
    /// If not None, this address should be used to access the server instead
    /// of one from the `addresses` method.
    pub fn access_ipv4(&self) -> &Option<Ipv4Addr> {
        &self.inner.accessIPv4
    }

    /// Get a reference to IPv6 address.
    ///
    /// If not None, this address should be used to access the server instead
    /// of one from the `addresses` method.
    pub fn access_ipv6(&self) -> &Option<Ipv6Addr> {
        &self.inner.accessIPv6
    }

    /// Get a reference to associated addresses.
    pub fn addresses(&self) -> &HashMap<String, Vec<protocol::ServerAddress>> {
        &self.inner.addresses
    }

    /// Get a reference to the availability zone.
    pub fn availability_zone(&self) -> &String {
        &self.inner.availability_zone
    }

    /// Get a reference to creation date and time.
    pub fn created_at(&self) -> &DateTime<FixedOffset> {
        &self.inner.created
    }

    /// Get a reference to server unique ID.
    pub fn id(&self) -> &String {
        &self.inner.id
    }

    /// Get a reference to the image.
    ///
    /// May be None if the server was created from a volume.
    pub fn image_id(&self) -> Option<&String> {
        match self.inner.image {
            Some(ref image) => Some(&image.id),
            None => None
        }
    }

    /// Whether the server has an image.
    ///
    /// May return `false` if the server was created from a volume.
    pub fn has_image(&self) -> bool {
        self.inner.image.is_some()
    }

    /// Get a reference to server name.
    pub fn name(&self) -> &String {
        &self.inner.name
    }

    /// Get server power state.
    pub fn power_state(&self) -> protocol::ServerPowerState {
        self.inner.power_state
    }

    /// Get server status.
    pub fn status(&self) -> protocol::ServerStatus {
        self.inner.status
    }

    /// Get a reference to last update date and time.
    pub fn updated_at(&self) -> &DateTime<FixedOffset> {
        &self.inner.updated
    }

    /// Start the server, optionally wait for it to be active.
    pub fn start(&'session mut self) -> Result<ServerStatusWaiter<'session>> {
        self.session.server_simple_action(&self.inner.id, "os-start")?;
        Ok(ServerStatusWaiter {
            server: self,
            target: protocol::ServerStatus::Active
        })
    }

    /// Stop the server, optionally wait for it to be powered off.
    pub fn stop(&'session mut self) -> Result<ServerStatusWaiter<'session>> {
        self.session.server_simple_action(&self.inner.id, "os-stop")?;
        Ok(ServerStatusWaiter {
            server: self,
            target: protocol::ServerStatus::ShutOff
        })
    }

    /// Delete the server.
    pub fn delete(self) -> Result<()> {
        // TODO(dtantsur): implement wait
        self.session.delete_server(&self.inner.id)
    }
}

impl<'server> ServerStatusWaiter<'server> {
    /// Current state of the server.
    ///
    /// Valid as of the last poll.
    pub fn current(&self) -> &Server<'server> {
        self.server
    }
}

impl<'server> Waiter<()> for ServerStatusWaiter<'server> {
    fn default_wait_timeout(&self) -> Option<Duration> {
        // TODO(dtantsur): vary depending on target?
        Some(Duration::new(600, 0))
    }

    fn default_delay(&self) -> Duration {
        Duration::new(1, 0)
    }

    fn timeout_error_message(&self) -> String {
        format!("Timeout waiting for server {} to reach state {}",
                self.server.id(), self.target)
    }

    fn poll(&mut self) -> Result<Option<()>> {
        self.server.refresh()?;
        if self.server.status() == self.target {
            debug!("Server {} reached state {}", self.server.id(), self.target);
            Ok(Some(()))
        } else if self.server.status() == protocol::ServerStatus::Error {
            debug!("Failed to move server {} to {} - status is ERROR",
                   self.server.id(), self.target);
            Err(Error::new(ErrorKind::OperationFailed,
                           format!("Server {} got into ERROR state",
                                   self.server.id())))
        } else {
            trace!("Still waiting for server {} to get to state {}, current is {}",
                   self.server.id(), self.server.status(), self.target);
            Ok(None)
        }
    }
}

impl<'session> ServerSummary<'session> {
    /// Get a reference to server unique ID.
    pub fn id(&self) -> &String {
        &self.inner.id
    }

    /// Get a reference to server name.
    pub fn name(&self) -> &String {
        &self.inner.name
    }

    /// Get details.
    pub fn details(&self) -> Result<Server<'session>> {
        Server::new(self.session, &self.inner.id)
    }

    /// Delete the server.
    pub fn delete(self) -> Result<()> {
        // TODO(dtantsur): implement wait
        self.session.delete_server(&self.inner.id)
    }
}

impl<'session> ServerQuery<'session> {
    pub(crate) fn new(session: &'session Session) -> ServerQuery<'session> {
        ServerQuery {
            session: session,
            query: Query::new(),
            can_paginate: true,
        }
    }

    /// Add marker to the request.
    ///
    /// Using this disables automatic pagination.
    pub fn with_marker<T: Into<String>>(mut self, marker: T) -> Self {
        self.can_paginate = false;
        self.query.push_str("marker", marker);
        self
    }

    /// Add limit to the request.
    ///
    /// Using this disables automatic pagination.
    pub fn with_limit(mut self, limit: usize) -> Self {
        self.can_paginate = false;
        self.query.push("limit", limit);
        self
    }

    /// Add sorting to the request.
    pub fn sort_by(mut self, sort: Sort<protocol::ServerSortKey>) -> Self {
        let (field, direction) = sort.into();
        self.query.push_str("sort_key", field);
        self.query.push("sort_dir", direction);
        self
    }

    /// Filter by IPv4 address that should be used to access the server.
    pub fn with_access_ip_v4<T: Into<Ipv4Addr>>(mut self, value: T) -> Self {
        self.query.push("access_ip_v4", value.into());
        self
    }

    /// Filter by IPv6 address that should be used to access the server.
    pub fn with_access_ip_v6<T: Into<Ipv6Addr>>(mut self, value: T) -> Self {
        self.query.push("access_ipv6", value.into());
        self
    }

    /// Filter by availability zone.
    pub fn with_availability_zone<T: Into<String>>(mut self, value: T) -> Self {
        self.query.push_str("availability_zone", value);
        self
    }

    /// Filter by flavor.
    pub fn with_flavor<T: Into<types::FlavorId>>(mut self, value: T) -> Self {
        self.query.push_str("flavor", value.into());
        self
    }

    /// Filter by host name.
    pub fn with_hostname<T: Into<String>>(mut self, value: T) -> Self {
        self.query.push_str("hostname", value);
        self
    }

    /// Filter by image ID.
    pub fn with_image<T: Into<types::ImageId>>(mut self, value: T) -> Self {
        self.query.push_str("image", value.into());
        self
    }

    /// Filter by an IPv4 address.
    pub fn with_ip_v4<T: Into<Ipv4Addr>>(mut self, value: T) -> Self {
        self.query.push("ip", value.into());
        self
    }

    /// Filter by an IPv6 address.
    pub fn with_ip_v6<T: Into<Ipv6Addr>>(mut self, value: T) -> Self {
        self.query.push("ip6", value.into());
        self
    }

    /// Filter by server name (a database regular expression).
    pub fn with_name<T: Into<String>>(mut self, value: T) -> Self {
        self.query.push_str("name", value);
        self
    }

    /// Filter by project ID (also commonly known as tenant ID).
    pub fn with_project<T: Into<types::ProjectId>>(mut self, value: T) -> Self {
        self.query.push_str("project_id", value.into());
        self
    }

    /// Filter by server status.
    pub fn with_status(mut self, value: protocol::ServerStatus) -> Self {
        self.query.push_str("status", value.to_string());
        self
    }

    /// Filter by user ID.
    pub fn with_user<T: Into<types::UserId>>(mut self, value: T) -> Self {
        self.query.push_str("user_id", value.into());
        self
    }

    /// Convert this query into an iterator executing the request.
    ///
    /// This iterator yields only `ServerSummary` objects, containing
    /// IDs and names. Use `into_iter_detailed` for full `Server` objects.
    ///
    /// Returns a `FallibleIterator`, which is an iterator with each `next`
    /// call returning a `Result`.
    ///
    /// Note that no requests are done until you start iterating.
    pub fn into_iter(self) -> ResourceIterator<'session, ServerSummary<'session>> {
        ResourceIterator::new(self.session, self.query)
    }

    /// Convert this query into an iterator executing the request.
    ///
    /// This iterator yields full `Server` objects. If you only need IDs
    /// and/or names, use `into_iter` to save bandwidth.
    ///
    /// Returns a `FallibleIterator`, which is an iterator with each `next`
    /// call returning a `Result`.
    ///
    /// Note that no requests are done until you start iterating.
    pub fn into_iter_detailed(self) -> ResourceIterator<'session, Server<'session>> {
        ResourceIterator::new(self.session, self.query)
    }

    /// Execute this request and return all results.
    ///
    /// A convenience shortcut for `self.into_iter().collect()`.
    pub fn all(self) -> Result<Vec<ServerSummary<'session>>> {
        self.into_iter().collect()
    }

    /// Return one and exactly one result.
    ///
    /// Fails with `ResourceNotFound` if the query produces no results and
    /// with `TooManyItems` if the query produces more than one result.
    pub fn one(mut self) -> Result<ServerSummary<'session>> {
        if self.can_paginate {
            // We need only one result. We fetch maximum two to be able
            // to check if the query yieled more than one result.
            self.query.push("limit", 2);
        }

        utils::fetch_one(self)
    }
}

impl<'session> ResourceId for ServerSummary<'session> {
    fn resource_id(&self) -> String {
        self.id().clone()
    }
}

impl<'session> ListResources<'session> for ServerSummary<'session> {
    const DEFAULT_LIMIT: usize = 50;

    fn list_resources<Q: Serialize + Debug>(session: &'session Session, query: Q)
            -> Result<Vec<ServerSummary<'session>>> {
        Ok(session.list_servers(&query)?.into_iter().map(|srv| ServerSummary {
            session: session,
            inner: srv
        }).collect())
    }
}

impl<'session> ResourceId for Server<'session> {
    fn resource_id(&self) -> String {
        self.id().clone()
    }
}

impl<'session> ListResources<'session> for Server<'session> {
    const DEFAULT_LIMIT: usize = 50;

    fn list_resources<Q: Serialize + Debug>(session: &'session Session, query: Q)
            -> Result<Vec<Server<'session>>> {
        Ok(session.list_servers_detail(&query)?.into_iter().map(|srv| Server {
            session: session,
            inner: srv
        }).collect())
    }
}

impl<'session> IntoFallibleIterator for ServerQuery<'session> {
    type Item = ServerSummary<'session>;

    type Error = Error;

    type IntoIter = ResourceIterator<'session, ServerSummary<'session>>;

    fn into_fallible_iterator(self) -> ResourceIterator<'session, ServerSummary<'session>> {
        self.into_iter()
    }
}
<|endoftext|>


<commit_before>//! Sets up the dbus interface for Awesome.

use std::{cell::RefCell, os::unix::io::RawFd, thread::LocalKey};

use dbus_rs::{BusType, Connection, Message, MessageItem, MessageType, MsgHandler,
              MsgHandlerType, MsgHandlerResult};
use rlua::{self, Lua, Table, Value, ToLua, ToLuaMulti, MultiValue,
           Error::RuntimeError};

use ::lua::LUA;
use ::common::signal;

type GlobalConnection = RefCell<Option<DBusConnection>>;

const SIGNALS_NAME: &'static str = "signals";

thread_local! {
    static SESSION_BUS: GlobalConnection = RefCell::new(None);
    static SYSTEM_BUS:  GlobalConnection = RefCell::new(None);
}

/// Called from `wayland_glib_interface.c` whenever a request is sent to the
/// current session dbus file descriptor.
///
/// This will kick off the special handling code in `dbus.rs`.
#[no_mangle]
pub extern "C" fn dbus_session_refresh(_: libc::c_void) -> bool {
    SESSION_BUS.with(|session_bus| {
        let session_bus = session_bus.borrow_mut();
        let session_bus = session_bus.as_ref().unwrap();
        let _ = session_bus.incoming(0).collect::<Vec<_>>();
    });
    true
}

/// Called from `wayland_glib_interface.c` whenever a request is sent to the
/// system dbus file descriptor.
///
/// This will kick off the special handling code in `dbus.rs`.
#[no_mangle]
pub extern "C" fn dbus_system_refresh(_: libc::c_void) -> bool {
    SYSTEM_BUS.with(|session_bus| {
        let session_bus = session_bus.borrow_mut();
        let session_bus = session_bus.as_ref().unwrap();
        let _ = session_bus.incoming(0).collect::<Vec<_>>();
    });
    true
}

struct DBusConnection {
    connection: Connection
}

struct DBusHandler {
    global_connection: &'static LocalKey<GlobalConnection>
}

impl std::ops::Deref for DBusConnection {
    type Target = Connection;

    fn deref(&self) -> &Self::Target {
        &self.connection
    }
}

impl Drop for DBusConnection {
    fn drop(&mut self) {
        unsafe {
            ::remove_dbus_from_glib();
        }
        // Need to close both of them, no idea which one was just destroyed,
        // so try to destroy both of them.
        SESSION_BUS.try_with(|session_bus| {
            *session_bus.borrow_mut() = None
        }).ok();
        SYSTEM_BUS.try_with(|session_bus| {
            *session_bus.borrow_mut() = None
        }).ok();
    }
}

impl MsgHandler for DBusHandler {
    fn handler_type(&self) -> MsgHandlerType {
        MsgHandlerType::All
    }

    fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> {
        if msg.msg_type() == MessageType::Signal {
            let (_, _, interface, member) = msg.headers();
            match (interface.unwrap().as_str(), member.unwrap().as_str()) {
                ("org.freedesktop.DBus.Local", "Disconnected") => {
                    self.global_connection.with(|bus| {
                        *bus.borrow_mut() = None;
                    });
                    // TODO not none
                    return Some(MsgHandlerResult {
                        handled: true,
                        done: true,
                        reply: vec![]
                    });
                },
                _ => {}
            }
        }
        let reply = LUA.with(|lua| {
            let lua = lua.borrow();
            self.process_request(&*lua, msg)
        }).unwrap_or_else(|err| {
            ::lua::log_error(err);
            vec![]
        });

        return Some(MsgHandlerResult {
            handled: true,
            done: false,
            reply
        })
    }
}

impl DBusHandler {
    /// Gives the D-Bus message to Lua for processing the reply.
    fn process_request(&mut self, lua: &Lua, msg: &Message) -> rlua::Result<Vec<Message>> {
        let message_metadata = lua.create_table()?;
        let msg_type = match msg.msg_type() {
            MessageType::Signal => "signal",
            MessageType::MethodCall => "method_call",
            MessageType::MethodReturn => "method_return",
            MessageType::Error => "error",
            MessageType::Invalid => "unknown"
        };
        message_metadata.set("type", msg_type)?;
        let (type_, path, interface, member) = msg.headers();
        message_metadata.set("interface", interface.unwrap_or_else(|| "".into()))?;
        message_metadata.set("path", path.unwrap_or_else(|| "".into()))?;
        message_metadata.set("member", member.unwrap_or_else(|| "".into()))?;
        if let Some(sender) = msg.sender() {
            message_metadata.set("sender", sender.to_string())?;
        }
        if self.global_connection as *const _ == &SYSTEM_BUS as *const _ {
            message_metadata.set("bus", "system")?;
        } else {
            message_metadata.set("bus", "session")?;
        };
        let lua_message = dbus_to_lua_value(lua, msg)?;
        unimplemented!()
    }
}

/// Set up the connections to the session bus and system bus.
///
/// The message handling is all done within the `dbus` module,
/// it's up to the caller of this function to register the DBus
/// file descriptors with glib so that we can awaken and deal
/// with dbus events when necessary.
pub fn connect() -> Result<(RawFd, RawFd), dbus::Error> {
    let session_con = Connection::get_private(BusType::Session)?;
    let system_con = Connection::get_private(BusType::System)?;
    let session_fds = session_con.watch_fds();
    let system_fds = system_con.watch_fds();
    assert_eq!(session_fds.len(), 1, "Only one fd per dbus connection");
    assert_eq!(system_fds.len(), 1, "Only one fd per dbus connection");
    session_con.add_handler(DBusHandler { global_connection: &SESSION_BUS });
    system_con.add_handler(DBusHandler { global_connection: &SYSTEM_BUS });

    SESSION_BUS.with(|session_bus| {
        let mut session_bus = session_bus.borrow_mut();
        *session_bus = Some(DBusConnection { connection: session_con });
    });
    SYSTEM_BUS.with(|system_bus| {
        let mut system_bus = system_bus.borrow_mut();
        *system_bus = Some(DBusConnection{ connection: system_con });
    });

    Ok((session_fds[0].fd(), system_fds[0].fd()))
}

/// Set up the DBus object in Lua so that the user libs can interact with
/// Awesome via DBus.
pub fn lua_init(lua: &Lua) -> rlua::Result<()> {
    let dbus_table = lua.create_table()?;
    dbus_table.set(SIGNALS_NAME, lua.create_table()?)?;
    dbus_table.set("request_name", lua.create_function(request_name)?)?;
    dbus_table.set("release_name", lua.create_function(release_name)?)?;
    dbus_table.set("add_match", lua.create_function(add_match)?)?;
    dbus_table.set("remove_match", lua.create_function(remove_match)?)?;
    dbus_table.set("connect_signal", lua.create_function(connect_signal)?)?;
    dbus_table.set("disconnect_signal", lua.create_function(disconnect_signal)?)?;
    dbus_table.set("emit_signal", lua.create_function(emit_signal)?)?;
    lua.globals().set("dbus", dbus_table)?;
    Ok(())
}

fn get_bus_by_name<'bus>(bus_name: &str)
                         -> rlua::Result<&'bus LocalKey<GlobalConnection>> {
    match bus_name {
        "session" => {
            Ok(&SESSION_BUS)
        },
        "system" => {
            Ok(&SYSTEM_BUS)
        },
        v => Err(RuntimeError(format!("Unknown bus type {}", v)))
    }
}

fn dbus_to_lua_value<'lua>(lua: &'lua Lua, msg: &Message)
                           -> rlua::Result<MultiValue<'lua>> {
    unimplemented!();
}

/// Converts an `rlua::Value` into a `dbus_rs::MessageItem`.
fn lua_value_to_dbus(lua: &Lua, type_: Value, value: Value)
                     -> rlua::Result<MessageItem> {
    use rlua::Value;
    use ::dbus_rs::arg::ArgType;
    let type_ = match type_ {
        Value::String(s) => s.to_str()?.to_string(),
        _ => return Err(RuntimeError("D-Bus type name was not a string".into()))
    };
    let is_ascii = type_.chars().next()
        .map(|c| !char::is_ascii(&c))
        .unwrap_or(false);
    if type_.len() > 1 ||  !is_ascii {
        return Err(RuntimeError(format!("{} is an invalid type name", type_)))
    }
    let type_ = ArgType::from_i32(type_.chars().next().unwrap() as i32)
        .map_err(|_| RuntimeError(format!(
            "{} is an invalid type name", type_)))?;
    match (type_, value) {
        (ArgType::Array, Value::Table(value)) => {
            let size = value.len()?;
            if size % 2 != 0 {
                return Err(RuntimeError(
                    "your D-Bus signal handling method returned \
                     wrong number of arguments".into()))
            }
            let types = value.clone().sequence_values().step_by(2);
            let values = value.clone().sequence_values().skip(1).step_by(2);
            let mut list = Vec::with_capacity(size as usize);
            for (type_, value) in types.zip(values) {
                list.push(lua_value_to_dbus(lua, type_?, value?)?)
            }
            MessageItem::new_array(list)
               .map_err(|_| RuntimeError("Empty list is invalid".into()))
        },
        (ArgType::Boolean, Value::Boolean(value)) => {
            Ok(value.into())
        },
        (ArgType::String, Value::String(value)) => {
            Ok(value.to_str()?.into())
        },
        (ArgType::Byte, Value::Integer(value)) |
        (ArgType::Int16, Value::Integer(value)) |
        (ArgType::UInt16, Value::Integer(value)) |
        (ArgType::Int32, Value::Integer(value)) |
        (ArgType::UInt32, Value::Integer(value)) |
        (ArgType::Int64, Value::Integer(value)) |
        (ArgType::UInt64, Value::Integer(value))  => {
            Ok(value.into())
        },
        (ArgType::Byte, Value::Number(value)) |
        (ArgType::Int16, Value::Number(value)) |
        (ArgType::UInt16, Value::Number(value)) |
        (ArgType::Int32, Value::Number(value)) |
        (ArgType::UInt32, Value::Number(value)) |
        (ArgType::Int64, Value::Number(value)) |
        (ArgType::UInt64, Value::Number(value)) |
        (ArgType::Double, Value::Number(value)) => {
            Ok(value.into())
        }
        (type_, value) => {
            Err(RuntimeError(format!("Invalid type {:?} or value {:?}",
                                            type_, value)))
        }
    }
}

fn request_name(_: &Lua, (bus, name): (String, String)) -> rlua::Result<bool> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.register_name(name.as_str(), 0)
            .expect(&format!("Could not register name {}", name.as_str()));
    });
    Ok(true)
}

fn release_name(_: &Lua, (bus, name): (String, String)) -> rlua::Result<bool> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.release_name(name.as_str())
            .expect(&format!("Could not release name {}", name.as_str()));
    });
    Ok(true)
}

fn add_match(_: &Lua, (bus, name): (String, String)) -> rlua::Result<()> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.add_match(name.as_str())
            .map_err(|err| RuntimeError(format!("{}", err)))
    })?;
    Ok(())
}

fn remove_match(_: &Lua, (bus, name): (String, String)) -> rlua::Result<()> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.remove_match(name.as_str())
            .map_err(|err| RuntimeError(format!("{}", err)))
    })?;
    Ok(())
}

fn connect_signal<'lua>(lua: &'lua Lua, (name, func): (String, rlua::Function))
                  -> rlua::Result<MultiValue<'lua>> {
    let signals: Table = lua.globals()
        .get::<_, Table>("dbus")
        .unwrap().get(SIGNALS_NAME).unwrap();
    if signals.get(name.as_str())? {
        let error_msg = format!(
            "Cannot add signal {} on D-Bus, already existing", name.as_str());
        warn!("{}", error_msg);
        (rlua::Nil, error_msg).to_lua_multi(lua)
    } else {
        signal::connect_signals(lua, signals, name, &[func])?;
        (true.to_lua_multi(lua))
    }
}

fn disconnect_signal(lua: &Lua, (name, func): (String, rlua::Function))
                     -> rlua::Result<()> {
    let signals: Table = lua.globals()
        .get::<_, Table>("dbus").unwrap()
        .get(SIGNALS_NAME).unwrap();
    signal::disconnect_signals(lua, signals, name)
}

fn emit_signal<'lua>(lua: &'lua Lua, (bus, path, interface, name, args):
                     (String, String, String, String, MultiValue))
                     -> rlua::Result<Value<'lua>> {
    if args.len() % 2 != 0 {
        let error_msg =
            "your D-Bus signal emitting metod has wrong number of arguments";
        warn!("{}", error_msg);
        return false.to_lua(lua)
    }
    let types = args.iter().step_by(2);
    let values = args.iter().skip(1).step_by(2);
    let args = types.zip(values)
        .map(|v| lua_value_to_dbus(lua, v.0.clone(), v.1.clone()))
        .collect::<rlua::Result<Vec<MessageItem>>>()?;
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        // TODO use new_signal, convert error
        let mut msg = Message::signal(&path.into(), &interface.into(), &name.into());
        msg.append_items(&args);
        bus.send(msg)
    }).map_err(|_| RuntimeError("Could not send D-Bus message".into()))?;
    true.to_lua(lua)
}
<commit_msg>Fix destructor<commit_after>//! Sets up the dbus interface for Awesome.

use std::{cell::RefCell, os::unix::io::RawFd, thread::LocalKey};

use dbus_rs::{BusType, Connection, Message, MessageItem, MessageType, MsgHandler,
              MsgHandlerType, MsgHandlerResult};
use rlua::{self, Lua, Table, Value, ToLua, ToLuaMulti, MultiValue,
           Error::RuntimeError};

use ::lua::LUA;
use ::common::signal;

type GlobalConnection = RefCell<Option<DBusConnection>>;

const SIGNALS_NAME: &'static str = "signals";

thread_local! {
    static SESSION_BUS: GlobalConnection = RefCell::new(None);
    static SYSTEM_BUS:  GlobalConnection = RefCell::new(None);
}

/// Called from `wayland_glib_interface.c` whenever a request is sent to the
/// current session dbus file descriptor.
///
/// This will kick off the special handling code in `dbus.rs`.
#[no_mangle]
pub extern "C" fn dbus_session_refresh(_: libc::c_void) -> bool {
    SESSION_BUS.with(|session_bus| {
        let session_bus = session_bus.borrow_mut();
        let session_bus = session_bus.as_ref().unwrap();
        let _ = session_bus.incoming(0).collect::<Vec<_>>();
    });
    true
}

/// Called from `wayland_glib_interface.c` whenever a request is sent to the
/// system dbus file descriptor.
///
/// This will kick off the special handling code in `dbus.rs`.
#[no_mangle]
pub extern "C" fn dbus_system_refresh(_: libc::c_void) -> bool {
    SYSTEM_BUS.with(|session_bus| {
        let session_bus = session_bus.borrow_mut();
        let session_bus = session_bus.as_ref().unwrap();
        let _ = session_bus.incoming(0).collect::<Vec<_>>();
    });
    true
}

struct DBusConnection {
    connection: Connection
}

struct DBusHandler {
    global_connection: &'static LocalKey<GlobalConnection>
}

impl std::ops::Deref for DBusConnection {
    type Target = Connection;

    fn deref(&self) -> &Self::Target {
        &self.connection
    }
}

impl Drop for DBusConnection {
    fn drop(&mut self) {
        unsafe {
            ::remove_dbus_from_glib();
        }
        // Need to close both of them, no idea which one was just destroyed,
        // so try to destroy both of them.
        SESSION_BUS.try_with(|session_bus| {
            if let Ok(mut bus) = session_bus.try_borrow_mut()  {
                *bus = None
            }
        }).ok();
        SYSTEM_BUS.try_with(|system_bus| {
            if let Ok(mut bus) = system_bus.try_borrow_mut()  {
                *bus = None
            }
        }).ok();
    }
}

impl MsgHandler for DBusHandler {
    fn handler_type(&self) -> MsgHandlerType {
        MsgHandlerType::All
    }

    fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> {
        if msg.msg_type() == MessageType::Signal {
            let (_, _, interface, member) = msg.headers();
            match (interface.unwrap().as_str(), member.unwrap().as_str()) {
                ("org.freedesktop.DBus.Local", "Disconnected") => {
                    self.global_connection.with(|bus| {
                        *bus.borrow_mut() = None;
                    });
                    // TODO not none
                    return Some(MsgHandlerResult {
                        handled: true,
                        done: true,
                        reply: vec![]
                    });
                },
                _ => {}
            }
        }
        let reply = LUA.with(|lua| {
            let lua = lua.borrow();
            self.process_request(&*lua, msg)
        }).unwrap_or_else(|err| {
            ::lua::log_error(err);
            vec![]
        });

        return Some(MsgHandlerResult {
            handled: true,
            done: false,
            reply
        })
    }
}

impl DBusHandler {
    /// Gives the D-Bus message to Lua for processing the reply.
    fn process_request(&mut self, lua: &Lua, msg: &Message) -> rlua::Result<Vec<Message>> {
        let message_metadata = lua.create_table()?;
        let msg_type = match msg.msg_type() {
            MessageType::Signal => "signal",
            MessageType::MethodCall => "method_call",
            MessageType::MethodReturn => "method_return",
            MessageType::Error => "error",
            MessageType::Invalid => "unknown"
        };
        message_metadata.set("type", msg_type)?;
        let (type_, path, interface, member) = msg.headers();
        message_metadata.set("interface", interface.unwrap_or_else(|| "".into()))?;
        message_metadata.set("path", path.unwrap_or_else(|| "".into()))?;
        message_metadata.set("member", member.unwrap_or_else(|| "".into()))?;
        if let Some(sender) = msg.sender() {
            message_metadata.set("sender", sender.to_string())?;
        }
        if self.global_connection as *const _ == &SYSTEM_BUS as *const _ {
            message_metadata.set("bus", "system")?;
        } else {
            message_metadata.set("bus", "session")?;
        };
        let lua_message = dbus_to_lua_value(lua, msg)?;
        unimplemented!()
    }
}

/// Set up the connections to the session bus and system bus.
///
/// The message handling is all done within the `dbus` module,
/// it's up to the caller of this function to register the DBus
/// file descriptors with glib so that we can awaken and deal
/// with dbus events when necessary.
pub fn connect() -> Result<(RawFd, RawFd), dbus::Error> {
    let session_con = Connection::get_private(BusType::Session)?;
    let system_con = Connection::get_private(BusType::System)?;
    let session_fds = session_con.watch_fds();
    let system_fds = system_con.watch_fds();
    assert_eq!(session_fds.len(), 1, "Only one fd per dbus connection");
    assert_eq!(system_fds.len(), 1, "Only one fd per dbus connection");
    session_con.add_handler(DBusHandler { global_connection: &SESSION_BUS });
    system_con.add_handler(DBusHandler { global_connection: &SYSTEM_BUS });

    SESSION_BUS.with(|session_bus| {
        let mut session_bus = session_bus.borrow_mut();
        *session_bus = Some(DBusConnection { connection: session_con });
    });
    SYSTEM_BUS.with(|system_bus| {
        let mut system_bus = system_bus.borrow_mut();
        *system_bus = Some(DBusConnection{ connection: system_con });
    });

    Ok((session_fds[0].fd(), system_fds[0].fd()))
}

/// Set up the DBus object in Lua so that the user libs can interact with
/// Awesome via DBus.
pub fn lua_init(lua: &Lua) -> rlua::Result<()> {
    let dbus_table = lua.create_table()?;
    dbus_table.set(SIGNALS_NAME, lua.create_table()?)?;
    dbus_table.set("request_name", lua.create_function(request_name)?)?;
    dbus_table.set("release_name", lua.create_function(release_name)?)?;
    dbus_table.set("add_match", lua.create_function(add_match)?)?;
    dbus_table.set("remove_match", lua.create_function(remove_match)?)?;
    dbus_table.set("connect_signal", lua.create_function(connect_signal)?)?;
    dbus_table.set("disconnect_signal", lua.create_function(disconnect_signal)?)?;
    dbus_table.set("emit_signal", lua.create_function(emit_signal)?)?;
    lua.globals().set("dbus", dbus_table)?;
    Ok(())
}

fn get_bus_by_name<'bus>(bus_name: &str)
                         -> rlua::Result<&'bus LocalKey<GlobalConnection>> {
    match bus_name {
        "session" => {
            Ok(&SESSION_BUS)
        },
        "system" => {
            Ok(&SYSTEM_BUS)
        },
        v => Err(RuntimeError(format!("Unknown bus type {}", v)))
    }
}

fn dbus_to_lua_value<'lua>(lua: &'lua Lua, msg: &Message)
                           -> rlua::Result<MultiValue<'lua>> {
    unimplemented!();
}

/// Converts an `rlua::Value` into a `dbus_rs::MessageItem`.
fn lua_value_to_dbus(lua: &Lua, type_: Value, value: Value)
                     -> rlua::Result<MessageItem> {
    use rlua::Value;
    use ::dbus_rs::arg::ArgType;
    let type_ = match type_ {
        Value::String(s) => s.to_str()?.to_string(),
        _ => return Err(RuntimeError("D-Bus type name was not a string".into()))
    };
    let is_ascii = type_.chars().next()
        .map(|c| !char::is_ascii(&c))
        .unwrap_or(false);
    if type_.len() > 1 ||  !is_ascii {
        return Err(RuntimeError(format!("{} is an invalid type name", type_)))
    }
    let type_ = ArgType::from_i32(type_.chars().next().unwrap() as i32)
        .map_err(|_| RuntimeError(format!(
            "{} is an invalid type name", type_)))?;
    match (type_, value) {
        (ArgType::Array, Value::Table(value)) => {
            let size = value.len()?;
            if size % 2 != 0 {
                return Err(RuntimeError(
                    "your D-Bus signal handling method returned \
                     wrong number of arguments".into()))
            }
            let types = value.clone().sequence_values().step_by(2);
            let values = value.clone().sequence_values().skip(1).step_by(2);
            let mut list = Vec::with_capacity(size as usize);
            for (type_, value) in types.zip(values) {
                list.push(lua_value_to_dbus(lua, type_?, value?)?)
            }
            MessageItem::new_array(list)
               .map_err(|_| RuntimeError("Empty list is invalid".into()))
        },
        (ArgType::Boolean, Value::Boolean(value)) => {
            Ok(value.into())
        },
        (ArgType::String, Value::String(value)) => {
            Ok(value.to_str()?.into())
        },
        (ArgType::Byte, Value::Integer(value)) |
        (ArgType::Int16, Value::Integer(value)) |
        (ArgType::UInt16, Value::Integer(value)) |
        (ArgType::Int32, Value::Integer(value)) |
        (ArgType::UInt32, Value::Integer(value)) |
        (ArgType::Int64, Value::Integer(value)) |
        (ArgType::UInt64, Value::Integer(value))  => {
            Ok(value.into())
        },
        (ArgType::Byte, Value::Number(value)) |
        (ArgType::Int16, Value::Number(value)) |
        (ArgType::UInt16, Value::Number(value)) |
        (ArgType::Int32, Value::Number(value)) |
        (ArgType::UInt32, Value::Number(value)) |
        (ArgType::Int64, Value::Number(value)) |
        (ArgType::UInt64, Value::Number(value)) |
        (ArgType::Double, Value::Number(value)) => {
            Ok(value.into())
        }
        (type_, value) => {
            Err(RuntimeError(format!("Invalid type {:?} or value {:?}",
                                            type_, value)))
        }
    }
}

fn request_name(_: &Lua, (bus, name): (String, String)) -> rlua::Result<bool> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.register_name(name.as_str(), 0)
            .expect(&format!("Could not register name {}", name.as_str()));
    });
    Ok(true)
}

fn release_name(_: &Lua, (bus, name): (String, String)) -> rlua::Result<bool> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.release_name(name.as_str())
            .expect(&format!("Could not release name {}", name.as_str()));
    });
    Ok(true)
}

fn add_match(_: &Lua, (bus, name): (String, String)) -> rlua::Result<()> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.add_match(name.as_str())
            .map_err(|err| RuntimeError(format!("{}", err)))
    })?;
    Ok(())
}

fn remove_match(_: &Lua, (bus, name): (String, String)) -> rlua::Result<()> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.remove_match(name.as_str())
            .map_err(|err| RuntimeError(format!("{}", err)))
    })?;
    Ok(())
}

fn connect_signal<'lua>(lua: &'lua Lua, (name, func): (String, rlua::Function))
                  -> rlua::Result<MultiValue<'lua>> {
    let signals: Table = lua.globals()
        .get::<_, Table>("dbus")
        .unwrap().get(SIGNALS_NAME).unwrap();
    if signals.get(name.as_str())? {
        let error_msg = format!(
            "Cannot add signal {} on D-Bus, already existing", name.as_str());
        warn!("{}", error_msg);
        (rlua::Nil, error_msg).to_lua_multi(lua)
    } else {
        signal::connect_signals(lua, signals, name, &[func])?;
        (true.to_lua_multi(lua))
    }
}

fn disconnect_signal(lua: &Lua, (name, func): (String, rlua::Function))
                     -> rlua::Result<()> {
    let signals: Table = lua.globals()
        .get::<_, Table>("dbus").unwrap()
        .get(SIGNALS_NAME).unwrap();
    signal::disconnect_signals(lua, signals, name)
}

fn emit_signal<'lua>(lua: &'lua Lua, (bus, path, interface, name, args):
                     (String, String, String, String, MultiValue))
                     -> rlua::Result<Value<'lua>> {
    if args.len() % 2 != 0 {
        let error_msg =
            "your D-Bus signal emitting metod has wrong number of arguments";
        warn!("{}", error_msg);
        return false.to_lua(lua)
    }
    let types = args.iter().step_by(2);
    let values = args.iter().skip(1).step_by(2);
    let args = types.zip(values)
        .map(|v| lua_value_to_dbus(lua, v.0.clone(), v.1.clone()))
        .collect::<rlua::Result<Vec<MessageItem>>>()?;
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        // TODO use new_signal, convert error
        let mut msg = Message::signal(&path.into(), &interface.into(), &name.into());
        msg.append_items(&args);
        bus.send(msg)
    }).map_err(|_| RuntimeError("Could not send D-Bus message".into()))?;
    true.to_lua(lua)
}
<|endoftext|>


<commit_before>// This file is part of Twig (ported to Rust).
//
// For the copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Typisation of parser errors.
///
/// @author Colin Kiegel <kiegel@gmx.de>

/////////////
// imports //
/////////////

use std::fmt::{self, Display};
use error::Error;
use error::api::{GeneralizeTo, ErrorCode, Dump};

use lexer::error::TokenErrorCode;
use parser::job::{self, cursor};
use lexer::token;

/////////////
// exports //
/////////////

pub type ParserError = Error<ParserErrorCode>;
pub type NodeError = Error<NodeErrorCode>; // todo move somewhere else??

impl GeneralizeTo<ParserErrorCode> for TokenErrorCode {
    fn generalize(&self) -> ParserErrorCode { ParserErrorCode::TokenError }
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum ParserErrorCode {
    Unreachable {
        reason: String,
        job: job::JobDump,
    },
    MissingExtensions,
    InvalidState {
        item: token::stream::Item,
        job: job::JobDump,
    },
    TokenError,
    TokenParserError {
        tag: &'static str, // known at compile-time
        error: String,
        job: <job::Job<'static, 'static> as Dump>::Data,
    },
    SemanticError,
    NoTagHandler {
        tag: String, // only known at runtime
        position: token::stream::Position,
        job: <job::Job<'static, 'static> as Dump>::Data,
    },
    UnexpectedBinaryOperator {
        name: String,
        job: <job::Job<'static, 'static> as Dump>::Data,
    },
    UnexpectedToken {
        reason: Option<&'static str>,
        expected: token::PatternDump,
        found: token::stream::Item,
    },
    UnexpectedEof {
        reason: Option<&'static str>,
        expected: Option<token::PatternDump>,
        cursor: cursor::CursorDump,
    },
}

impl ErrorCode for ParserErrorCode {
    fn description(&self) -> &str {
        match *self {
            ParserErrorCode::Unreachable{..} => "Unexptected parser error (please report as bug with details).",
            ParserErrorCode::MissingExtensions => "Could not initialize parser due to missing compiler extensions.",
            ParserErrorCode::InvalidState{..} => "Parser ended up in unsupported state.",
            ParserErrorCode::TokenError => "Token error.",
            ParserErrorCode::TokenParserError{..} => "Token parser error.",
            ParserErrorCode::SemanticError => "Semantic error.",
            ParserErrorCode::NoTagHandler{..} => "There is no registered tag handler for named block.",
            ParserErrorCode::UnexpectedBinaryOperator{..} => "Unexpected Binary Operator.",
            ParserErrorCode::UnexpectedToken{..} => "Unexpected Token.",
            ParserErrorCode::UnexpectedEof{..} => "Unexpected end of token stream.",
        }
    }
}

impl Display for ParserErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));

        match *self {
            ParserErrorCode::Unreachable {
                ref reason, ref job
            } => {
                write!(f, " {reason} for {job}",
                    reason = reason,
                    job = job)
            }
            ParserErrorCode::MissingExtensions => Ok(()),
            ParserErrorCode::InvalidState {
                item: ref i, job: ref j
            } => {
                write!(f, " Found token {token:?} at {pos} in {job}",
                    token = i.token(),
                    pos = i.position(),
                    job = j)
            },
            ParserErrorCode::TokenError => Ok(()),
            ParserErrorCode::TokenParserError {
                tag, ref error, ref job
            } => {
                write!(f, " {tag:?}-block: {error} for job {job}.",
                    tag = tag, error = error, job = job)
            },
            ParserErrorCode::SemanticError => Ok(()),
            ParserErrorCode::NoTagHandler {
                tag: ref t, position: ref p, job: ref j
            } => {
                write!(f, " Found block {tag} at {pos} for job {job}.",
                    tag = t, pos = p, job = j)
            },
            ParserErrorCode::UnexpectedBinaryOperator {
                name: ref n, job: ref j
            } => {
                write!(f, " The binary operator {name:?} is unknown to the compiler for job {job}",
                    name = n,
                    job = j)
            },
            ParserErrorCode::UnexpectedToken {
                reason: r, expected: ref x, found: ref i
            } => {
                try!(write!(f, " Expected token {x:?} but found {t:?} at {p:?}.",
                    x = x, t = i.token(), p = i.position()));

                if let Some(reason) = r {
                    try!(write!(f, " {}", reason));
                }

                Ok(())
            },
            ParserErrorCode::UnexpectedEof {
                reason, ref expected, ref cursor
            } => {
                if let Some(ref expected) = *expected {
                    try!(write!(f, " Expected token to match {:?}.", expected));
                }

                if let Some(reason) = reason {
                    try!(write!(f, " {}", reason))
                }

                write!(f, " For {}.", cursor)
            }
        }
    }
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum NodeErrorCode {
    Unreachable {
        reason: String
    },
    AttributeNotFound {
        key: String,
        node_tag: String
    }
}

impl ErrorCode for NodeErrorCode {
    fn description(&self) -> &str {
        match *self {
            NodeErrorCode::Unreachable{..} => "Unexptected node error (please report as bug with details).",
            NodeErrorCode::AttributeNotFound{..} => "Attribute not found.",
        }
    }
}

impl Display for NodeErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));

        match *self {
            NodeErrorCode::Unreachable {
                ref reason
            } => {
                write!(f, " {}", reason)
            }
            NodeErrorCode::AttributeNotFound{
                ref key, ref node_tag
            } => {
                write!(f, " Attribute {key:?} does not exist for Node {node:?}.",
                    key = key, node = node_tag)
            },
        }
    }
}
<commit_msg>cleanup<commit_after>// This file is part of Twig (ported to Rust).
//
// For the copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Typisation of parser errors.
///
/// @author Colin Kiegel <kiegel@gmx.de>

/////////////
// imports //
/////////////

use std::fmt::{self, Display};
use error::Error;
use error::api::{GeneralizeTo, ErrorCode};

use lexer::error::TokenErrorCode;
use parser::job::{self, cursor};
use lexer::token;

/////////////
// exports //
/////////////

pub type ParserError = Error<ParserErrorCode>;
pub type NodeError = Error<NodeErrorCode>; // todo move somewhere else??

impl GeneralizeTo<ParserErrorCode> for TokenErrorCode {
    fn generalize(&self) -> ParserErrorCode { ParserErrorCode::TokenError }
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum ParserErrorCode {
    Unreachable {
        reason: String,
        job: job::JobDump,
    },
    MissingExtensions,
    InvalidState {
        item: token::stream::Item,
        job: job::JobDump,
    },
    TokenError,
    TokenParserError {
        tag: &'static str, // known at compile-time
        error: String,
        job: job::JobDump,
    },
    SemanticError,
    NoTagHandler {
        tag: String, // only known at runtime
        position: token::stream::Position,
        job: job::JobDump,
    },
    UnexpectedBinaryOperator {
        name: String,
        job: job::JobDump,
    },
    UnexpectedToken {
        reason: Option<&'static str>,
        expected: token::PatternDump,
        found: token::stream::Item,
    },
    UnexpectedEof {
        reason: Option<&'static str>,
        expected: Option<token::PatternDump>,
        cursor: cursor::CursorDump,
    },
}

impl ErrorCode for ParserErrorCode {
    fn description(&self) -> &str {
        match *self {
            ParserErrorCode::Unreachable{..} => "Unexptected parser error (please report as bug with details).",
            ParserErrorCode::MissingExtensions => "Could not initialize parser due to missing compiler extensions.",
            ParserErrorCode::InvalidState{..} => "Parser ended up in unsupported state.",
            ParserErrorCode::TokenError => "Token error.",
            ParserErrorCode::TokenParserError{..} => "Token parser error.",
            ParserErrorCode::SemanticError => "Semantic error.",
            ParserErrorCode::NoTagHandler{..} => "There is no registered tag handler for named block.",
            ParserErrorCode::UnexpectedBinaryOperator{..} => "Unexpected Binary Operator.",
            ParserErrorCode::UnexpectedToken{..} => "Unexpected Token.",
            ParserErrorCode::UnexpectedEof{..} => "Unexpected end of token stream.",
        }
    }
}

impl Display for ParserErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));

        match *self {
            ParserErrorCode::Unreachable {
                ref reason, ref job
            } => {
                write!(f, " {reason} for {job}",
                    reason = reason,
                    job = job)
            }
            ParserErrorCode::MissingExtensions => Ok(()),
            ParserErrorCode::InvalidState {
                item: ref i, job: ref j
            } => {
                write!(f, " Found token {token:?} at {pos} in {job}",
                    token = i.token(),
                    pos = i.position(),
                    job = j)
            },
            ParserErrorCode::TokenError => Ok(()),
            ParserErrorCode::TokenParserError {
                tag, ref error, ref job
            } => {
                write!(f, " {tag:?}-block: {error} for job {job}.",
                    tag = tag, error = error, job = job)
            },
            ParserErrorCode::SemanticError => Ok(()),
            ParserErrorCode::NoTagHandler {
                tag: ref t, position: ref p, job: ref j
            } => {
                write!(f, " Found block {tag} at {pos} for job {job}.",
                    tag = t, pos = p, job = j)
            },
            ParserErrorCode::UnexpectedBinaryOperator {
                name: ref n, job: ref j
            } => {
                write!(f, " The binary operator {name:?} is unknown to the compiler for job {job}",
                    name = n,
                    job = j)
            },
            ParserErrorCode::UnexpectedToken {
                reason: r, expected: ref x, found: ref i
            } => {
                try!(write!(f, " Expected token {x:?} but found {t:?} at {p:?}.",
                    x = x, t = i.token(), p = i.position()));

                if let Some(reason) = r {
                    try!(write!(f, " {}", reason));
                }

                Ok(())
            },
            ParserErrorCode::UnexpectedEof {
                reason, ref expected, ref cursor
            } => {
                if let Some(ref expected) = *expected {
                    try!(write!(f, " Expected token to match {:?}.", expected));
                }

                if let Some(reason) = reason {
                    try!(write!(f, " {}", reason))
                }

                write!(f, " For {}.", cursor)
            }
        }
    }
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum NodeErrorCode {
    Unreachable {
        reason: String
    },
    AttributeNotFound {
        key: String,
        node_tag: String
    }
}

impl ErrorCode for NodeErrorCode {
    fn description(&self) -> &str {
        match *self {
            NodeErrorCode::Unreachable{..} => "Unexptected node error (please report as bug with details).",
            NodeErrorCode::AttributeNotFound{..} => "Attribute not found.",
        }
    }
}

impl Display for NodeErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));

        match *self {
            NodeErrorCode::Unreachable {
                ref reason
            } => {
                write!(f, " {}", reason)
            }
            NodeErrorCode::AttributeNotFound{
                ref key, ref node_tag
            } => {
                write!(f, " Attribute {key:?} does not exist for Node {node:?}.",
                    key = key, node = node_tag)
            },
        }
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::borrow::BorrowMut;
use gl;
use gl::types::*;
use context::ContextOf;
use types::GLError;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl<AB, EAB, P, FB, RB, TU> ContextOf<AB, EAB, P, FB, RB, TU> {
    pub fn gen_renderbuffer(&self) -> Renderbuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenRenderbuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Renderbuffer {
                gl_id: id
            }
        }
    }

    pub fn bind_renderbuffer<'a>(self, renderbuffer: &'a mut Renderbuffer)
        -> (
            RenderbufferBinding<'a>,
            ContextOf<AB, EAB, P, FB, (), TU>
        )
        where RB: BorrowMut<RenderbufferBinder>
    {
        let (mut renderbuffer_binder, gl) = self.split_renderbuffer();
        (renderbuffer_binder.borrow_mut().bind(renderbuffer), gl)
    }
}

gl_enum! {
    pub gl_enum RenderbufferTarget {
        Renderbuffer as RENDERBUFFER = gl::RENDERBUFFER
    }
}

pub struct RenderbufferBinding<'a> {
    phantom: PhantomData<&'a mut Renderbuffer>
}

impl<'a> RenderbufferBinding<'a> {
    fn target(&self) -> RenderbufferTarget {
        RenderbufferTarget::Renderbuffer
    }
}

pub struct RenderbufferBinder;
impl RenderbufferBinder {
    pub fn bind<'a>(&mut self, renderbuffer: &'a mut Renderbuffer)
        -> RenderbufferBinding<'a>
    {
        let binding = RenderbufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindRenderbuffer(binding.target().gl_enum(),
                                 renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<commit_msg>Add `RenderbufferBinding.storage()` method<commit_after>use std::marker::PhantomData;
use std::borrow::BorrowMut;
use gl;
use gl::types::*;
use context::ContextOf;
use image_data::{RenderbufferFormat};
use types::GLError;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl<AB, EAB, P, FB, RB, TU> ContextOf<AB, EAB, P, FB, RB, TU> {
    pub fn gen_renderbuffer(&self) -> Renderbuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenRenderbuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Renderbuffer {
                gl_id: id
            }
        }
    }

    pub fn bind_renderbuffer<'a>(self, renderbuffer: &'a mut Renderbuffer)
        -> (
            RenderbufferBinding<'a>,
            ContextOf<AB, EAB, P, FB, (), TU>
        )
        where RB: BorrowMut<RenderbufferBinder>
    {
        let (mut renderbuffer_binder, gl) = self.split_renderbuffer();
        (renderbuffer_binder.borrow_mut().bind(renderbuffer), gl)
    }
}

gl_enum! {
    pub gl_enum RenderbufferTarget {
        Renderbuffer as RENDERBUFFER = gl::RENDERBUFFER
    }
}

pub struct RenderbufferBinding<'a> {
    phantom: PhantomData<&'a mut Renderbuffer>
}

impl<'a> RenderbufferBinding<'a> {
    fn target(&self) -> RenderbufferTarget {
        RenderbufferTarget::Renderbuffer
    }

    pub fn storage(&mut self,
                   format: RenderbufferFormat,
                   width: u32,
                   height: u32)
    {
        unsafe {
            gl::RenderbufferStorage(self.target().gl_enum(),
                                    format.gl_enum(),
                                    width as GLint,
                                    height as GLint);
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER` or `internalformat` is not an accepted format",
                GLError::InvalidValue => "`width` or `height` is less than zero or greater than `GL_MAX_RENDERBUFFER_SIZE`",
                GLError::OutOfMemory => "Unable to allocate enough memory for requested size",
                GLError::InvalidOperation => "Renderbuffer object 0 is bound",
                _ => "Unknown error"
            }
        }
    }
}

pub struct RenderbufferBinder;
impl RenderbufferBinder {
    pub fn bind<'a>(&mut self, renderbuffer: &'a mut Renderbuffer)
        -> RenderbufferBinding<'a>
    {
        let binding = RenderbufferBinding { phantom: PhantomData };
        unsafe {
            gl::BindRenderbuffer(binding.target().gl_enum(),
                                 renderbuffer.gl_id());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`target` is not `GL_RENDERBUFFER`",
                _ => "Unknown error"
            }
        }
        binding
    }
}
<|endoftext|>


<commit_before>#![feature(integer_atomics)]

extern crate abra;
extern crate rocksdb;
extern crate rustc_serialize;

use std::sync::{Arc, RwLock};
use std::sync::atomic::{AtomicU32, Ordering};
use std::collections::BTreeMap;

use rocksdb::{DB, Writable, Options};
use abra::{Term, Document};
use abra::schema::{Schema, FieldType, FieldRef, AddFieldError};
use rustc_serialize::{json, Encodable};

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
struct TermRef(u32);

pub struct RocksDBIndexStore {
    schema: Arc<Schema>,
    db: DB,
    next_term_ref: AtomicU32,
    term_dictionary: RwLock<BTreeMap<Vec<u8>, TermRef>>,
}

impl RocksDBIndexStore {
    pub fn create(path: &str) -> Result<RocksDBIndexStore, String> {
        let mut opts = Options::default();
        opts.create_if_missing(true);
        let db = try!(DB::open(&opts, path));

        // Schema
        let schema = Schema::new();
        db.put(b"schema", json::encode(&schema).unwrap().as_bytes());

        // Next term ref
        db.put(b"next_term_ref", b"1");

        Ok(RocksDBIndexStore {
            schema: Arc::new(schema),
            db: db,
            next_term_ref: AtomicU32::new(1),
            term_dictionary: RwLock::new(BTreeMap::new()),
        })
    }

    pub fn open(path: &str) -> Result<RocksDBIndexStore, String> {
        let mut opts = Options::default();
        let db = try!(DB::open(&opts, path));

        let schema = match db.get(b"schema") {
            Ok(Some(schema)) => {
                let schema = schema.to_utf8().unwrap().to_string();
                json::decode(&schema).unwrap()
            }
            Ok(None) => Schema::new(),  // TODO: error
            Err(_) => Schema::new(),  // TODO: error
        };

        let next_term_ref = match db.get(b"next_term_ref") {
            Ok(Some(next_term_ref)) => {
                next_term_ref.to_utf8().unwrap().parse::<u32>().unwrap()
            }
            Ok(None) => 1,  // TODO: error
            Err(_) => 1,  // TODO: error
        };

        Ok(RocksDBIndexStore {
            schema: Arc::new(schema),
            db: db,
            next_term_ref: AtomicU32::new(next_term_ref),
            term_dictionary: RwLock::new(BTreeMap::new()),
        })
    }

    pub fn add_field(&mut self, name: String, field_type: FieldType) -> Result<FieldRef, AddFieldError> {
        let mut schema_copy = (*self.schema).clone();
        let field_ref = try!(schema_copy.add_field(name, field_type));
        self.schema = Arc::new(schema_copy);

        self.db.put(b"schema", json::encode(&self.schema).unwrap().as_bytes());

        Ok(field_ref)
    }

    pub fn remove_field(&mut self, field_ref: &FieldRef) -> bool {
        let mut schema_copy = (*self.schema).clone();
        let field_removed = schema_copy.remove_field(field_ref);

        if field_removed {
            self.schema = Arc::new(schema_copy);
            self.db.put(b"schema", json::encode(&self.schema).unwrap().as_bytes());
        }

        field_removed
    }

    fn get_or_create_term(&mut self, term: &Term) -> TermRef {
        let term_bytes = term.to_bytes();

        if let Some(term_ref) = self.term_dictionary.read().unwrap().get(&term_bytes) {
            return *term_ref;
        }

        // Term doesn't exist in the term dictionary

        // Increment next_term_ref
        let next_term_ref = self.next_term_ref.fetch_add(1, Ordering::SeqCst);
        self.db.put(b"next_term_ref", (next_term_ref + 1).to_string().as_bytes());

        // Create term ref
        let term_ref = TermRef(next_term_ref);

        // Get exclusive lock to term dictionary
        let mut term_dictionary = self.term_dictionary.write().unwrap();

        // It's possible that another thread has written the term to the dictionary
        // since we checked earlier. If this is the case, We should forget about
        // writing our TermRef and use the one that has been inserted already.
        if let Some(term_ref) = term_dictionary.get(&term_bytes) {
            return *term_ref;
        }

        // Write it to the on-disk term dictionary
        let mut key = Vec::with_capacity(3 + term_bytes.len());
        key.push(b't');
        key.push(b'd');
        key.push(b'/');
        for byte in term_bytes.iter() {
            key.push(*byte);
        }
        self.db.put(&key, next_term_ref.to_string().as_bytes());

        // Write it to the term dictionary
        term_dictionary.insert(term_bytes, term_ref);

        term_ref
    }

    pub fn insert_or_update_document(&mut self, doc: Document) {
        // Put field contents in inverted index
        for (field_ref, tokens) in doc.fields.iter() {
            for token in tokens.iter() {
                let term_ref = self.get_or_create_term(&token.term);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use rocksdb::{DB, Options};

    use super::RocksDBIndexStore;

    fn clean_test_indices() {
        use std::fs::remove_dir_all;

        remove_dir_all("test_indices");
    }

    #[test]
    fn test_create() {
        clean_test_indices();

        let store = RocksDBIndexStore::create("test_indices/test_create");
        assert!(store.is_ok());
    }

    #[test]
    fn test_open() {
        clean_test_indices();

        let store = RocksDBIndexStore::open("test_indices/test_open");
        assert!(store.is_err());

        // Create DB
        let mut opts = Options::default();
        opts.create_if_missing(true);
        DB::open(&opts, "test_indices/test_open").unwrap();

        let store = RocksDBIndexStore::open("test_indices/test_open");
        assert!(store.is_ok());
    }
}
<commit_msg>First character of each key denotes the type<commit_after>#![feature(integer_atomics)]

extern crate abra;
extern crate rocksdb;
extern crate rustc_serialize;

use std::sync::{Arc, RwLock};
use std::sync::atomic::{AtomicU32, Ordering};
use std::collections::BTreeMap;

use rocksdb::{DB, Writable, Options};
use abra::{Term, Document};
use abra::schema::{Schema, FieldType, FieldRef, AddFieldError};
use rustc_serialize::{json, Encodable};

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
struct TermRef(u32);

pub struct RocksDBIndexStore {
    schema: Arc<Schema>,
    db: DB,
    next_term_ref: AtomicU32,
    term_dictionary: RwLock<BTreeMap<Vec<u8>, TermRef>>,
}

impl RocksDBIndexStore {
    pub fn create(path: &str) -> Result<RocksDBIndexStore, String> {
        let mut opts = Options::default();
        opts.create_if_missing(true);
        let db = try!(DB::open(&opts, path));

        // Schema
        let schema = Schema::new();
        db.put(b".schema", json::encode(&schema).unwrap().as_bytes());

        // Next term ref
        db.put(b".next_term_ref", b"1");

        Ok(RocksDBIndexStore {
            schema: Arc::new(schema),
            db: db,
            next_term_ref: AtomicU32::new(1),
            term_dictionary: RwLock::new(BTreeMap::new()),
        })
    }

    pub fn open(path: &str) -> Result<RocksDBIndexStore, String> {
        let mut opts = Options::default();
        let db = try!(DB::open(&opts, path));

        let schema = match db.get(b".schema") {
            Ok(Some(schema)) => {
                let schema = schema.to_utf8().unwrap().to_string();
                json::decode(&schema).unwrap()
            }
            Ok(None) => Schema::new(),  // TODO: error
            Err(_) => Schema::new(),  // TODO: error
        };

        let next_term_ref = match db.get(b".next_term_ref") {
            Ok(Some(next_term_ref)) => {
                next_term_ref.to_utf8().unwrap().parse::<u32>().unwrap()
            }
            Ok(None) => 1,  // TODO: error
            Err(_) => 1,  // TODO: error
        };

        Ok(RocksDBIndexStore {
            schema: Arc::new(schema),
            db: db,
            next_term_ref: AtomicU32::new(next_term_ref),
            term_dictionary: RwLock::new(BTreeMap::new()),
        })
    }

    pub fn add_field(&mut self, name: String, field_type: FieldType) -> Result<FieldRef, AddFieldError> {
        let mut schema_copy = (*self.schema).clone();
        let field_ref = try!(schema_copy.add_field(name, field_type));
        self.schema = Arc::new(schema_copy);

        self.db.put(b".schema", json::encode(&self.schema).unwrap().as_bytes());

        Ok(field_ref)
    }

    pub fn remove_field(&mut self, field_ref: &FieldRef) -> bool {
        let mut schema_copy = (*self.schema).clone();
        let field_removed = schema_copy.remove_field(field_ref);

        if field_removed {
            self.schema = Arc::new(schema_copy);
            self.db.put(b".schema", json::encode(&self.schema).unwrap().as_bytes());
        }

        field_removed
    }

    fn get_or_create_term(&mut self, term: &Term) -> TermRef {
        let term_bytes = term.to_bytes();

        if let Some(term_ref) = self.term_dictionary.read().unwrap().get(&term_bytes) {
            return *term_ref;
        }

        // Term doesn't exist in the term dictionary

        // Increment next_term_ref
        let next_term_ref = self.next_term_ref.fetch_add(1, Ordering::SeqCst);
        self.db.put(b".next_term_ref", (next_term_ref + 1).to_string().as_bytes());

        // Create term ref
        let term_ref = TermRef(next_term_ref);

        // Get exclusive lock to term dictionary
        let mut term_dictionary = self.term_dictionary.write().unwrap();

        // It's possible that another thread has written the term to the dictionary
        // since we checked earlier. If this is the case, We should forget about
        // writing our TermRef and use the one that has been inserted already.
        if let Some(term_ref) = term_dictionary.get(&term_bytes) {
            return *term_ref;
        }

        // Write it to the on-disk term dictionary
        let mut key = Vec::with_capacity(1 + term_bytes.len());
        key.push(b't');
        for byte in term_bytes.iter() {
            key.push(*byte);
        }
        self.db.put(&key, next_term_ref.to_string().as_bytes());

        // Write it to the term dictionary
        term_dictionary.insert(term_bytes, term_ref);

        term_ref
    }

    pub fn insert_or_update_document(&mut self, doc: Document) {
        // Put field contents in inverted index
        for (field_ref, tokens) in doc.fields.iter() {
            for token in tokens.iter() {
                let term_ref = self.get_or_create_term(&token.term);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use rocksdb::{DB, Options};

    use super::RocksDBIndexStore;

    fn clean_test_indices() {
        use std::fs::remove_dir_all;

        remove_dir_all("test_indices");
    }

    #[test]
    fn test_create() {
        clean_test_indices();

        let store = RocksDBIndexStore::create("test_indices/test_create");
        assert!(store.is_ok());
    }

    #[test]
    fn test_open() {
        clean_test_indices();

        let store = RocksDBIndexStore::open("test_indices/test_open");
        assert!(store.is_err());

        // Create DB
        let mut opts = Options::default();
        opts.create_if_missing(true);
        DB::open(&opts, "test_indices/test_open").unwrap();

        let store = RocksDBIndexStore::open("test_indices/test_open");
        assert!(store.is_ok());
    }
}
<|endoftext|>