<commit_before>use std::fmt;
use clap::{Arg, App};

// This struct represents the parsed command line arguments, not the raw ones.
// For example, if no value is supplied for an argument then the default is applied
// and set for the corresponding field in this struct.
pub struct CommandLineArguments {
    load_config: bool,
    xdg_profile: String
}

impl CommandLineArguments {
    pub fn new() -> CommandLineArguments {
        let matches = App::new("Qork")
                        .version(crate_version!())
                        .author("Philip Daniels philip.daniels1971@gmail.com")
                        .about("A text editor/IDE written in Rust and Python.")
                        .arg(Arg::with_name("xdg-profile")
                                .short("p")
                                .long("xdg-profile")
                                .value_name("DIRECTORY")
                                .help("Sets the XDG profile directory.")
                                .default_value("default")
                                .takes_value(true)
                        )
                        .arg(Arg::with_name("no-config")
                                .short("q")
                                .long("no-config")
                                .help("Prevent the loading of the configuration directory")
                        )
                        .get_matches();

        CommandLineArguments {
            // Flip this so that we express what we want to do as a positive boolean (double
            // negatives are hard to reason about). The code will read 'if args.load_config()...'
            load_config: !matches.is_present("no-config"),
            xdg_profile: String::from(matches.value_of("xdg-profile").unwrap())
        }
    }

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

    pub fn xdg_profile(&self) -> &String {
        &self.xdg_profile
    }
}

impl fmt::Debug for CommandLineArguments {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, r#"CommandLineArguments {{ load_config: {}, xdg_profile: "{}" }}"#,
            self.load_config, self.xdg_profile
        )
    }
}

impl fmt::Display for CommandLineArguments {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}<commit_msg>Get the author(s) from Cargo.toml using an env!() macro.<commit_after>use std::fmt;
use clap::{Arg, App};

// This struct represents the parsed command line arguments, not the raw ones.
// For example, if no value is supplied for an argument then the default is applied
// and set for the corresponding field in this struct.
pub struct CommandLineArguments {
    load_config: bool,
    xdg_profile: String
}

impl CommandLineArguments {
    pub fn new() -> CommandLineArguments {
        let matches = App::new("Qork")
                        .version(crate_version!())
                        .author(env!("CARGO_PKG_AUTHORS"))
                        .about("A text editor/IDE written in Rust and Python.")
                        .arg(Arg::with_name("xdg-profile")
                                .short("p")
                                .long("xdg-profile")
                                .value_name("DIRECTORY")
                                .help("Sets the XDG profile directory.")
                                .default_value("default")
                                .takes_value(true)
                        )
                        .arg(Arg::with_name("no-config")
                                .short("q")
                                .long("no-config")
                                .help("Prevent the loading of the configuration directory")
                        )
                        .get_matches();

        CommandLineArguments {
            // Flip this so that we express what we want to do as a positive boolean (double
            // negatives are hard to reason about). The code will read 'if args.load_config()...'
            load_config: !matches.is_present("no-config"),
            xdg_profile: String::from(matches.value_of("xdg-profile").unwrap())
        }
    }

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

    pub fn xdg_profile(&self) -> &String {
        &self.xdg_profile
    }
}

impl fmt::Debug for CommandLineArguments {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, r#"CommandLineArguments {{ load_config: {}, xdg_profile: "{}" }}"#,
            self.load_config, self.xdg_profile
        )
    }
}

impl fmt::Display for CommandLineArguments {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::sync::{Arc, Mutex};
use std::{fmt, net};

use futures::future::TryFutureExt;
use thiserror::Error;
use tracing::{debug, error, info, warn};

use xds::istio::workload::Workload as XdsWorkload;

use crate::identity::Identity;
use crate::workload::WorkloadError::ProtocolParse;
use crate::xds::{HandlerContext, XdsUpdate};
use crate::{config, xds};

#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum Protocol {
    Tcp,
    Hbone,
}

impl Default for Protocol {
    fn default() -> Self {
        Protocol::Tcp
    }
}

impl TryFrom<Option<xds::istio::workload::Protocol>> for Protocol {
    type Error = WorkloadError;

    fn try_from(value: Option<xds::istio::workload::Protocol>) -> Result<Self, Self::Error> {
        match value {
            Some(xds::istio::workload::Protocol::Http) => Ok(Protocol::Hbone),
            Some(xds::istio::workload::Protocol::Direct) => Ok(Protocol::Hbone),
            None => Err(ProtocolParse("unknown type".into())),
        }
    }
}

#[derive(Debug, Hash, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Workload {
    pub workload_ip: IpAddr,
    #[serde(default)]
    pub waypoint_address: Option<IpAddr>,
    #[serde(default)]
    pub gateway_ip: Option<SocketAddr>,
    #[serde(default)]
    pub protocol: Protocol,

    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub namespace: String,
    #[serde(default)]
    pub service_account: String,

    #[serde(default)]
    pub node: String,

    #[serde(default)]
    pub native_hbone: bool,
}

impl Workload {
    pub fn identity(&self) -> Identity {
        Identity::Spiffe {
            /// TODO: don't hardcode
            trust_domain: "cluster.local".to_string(),
            namespace: self.namespace.clone(),
            service_account: self.service_account.clone(),
        }
    }
}

impl Workload {
    fn resource_name(&self) -> String {
        self.name.to_owned() + "/" + self.namespace.as_str()
    }
}

impl fmt::Display for Workload {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Workload{{{} at {} via {} ({:?})}}",
            self.name,
            self.workload_ip,
            self.gateway_ip
                .map(|x| format!("{}", x))
                .unwrap_or_else(|| "None".into()),
            self.protocol
        )
    }
}

#[derive(Debug, Hash, Eq, PartialEq, Clone, serde::Serialize)]
pub struct Upstream {
    pub workload: Workload,
    pub port: u16,
}

impl fmt::Display for Upstream {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Upstream{{{} at {}:{} via {} ({:?})}}",
            self.workload.name,
            self.workload.workload_ip,
            self.port,
            self.workload
                .gateway_ip
                .map(|x| format!("{}", x))
                .unwrap_or_else(|| "None".into()),
            self.workload.protocol
        )
    }
}

fn byte_to_ip(b: bytes::Bytes) -> Result<Option<IpAddr>, WorkloadError> {
    Ok(match b.len() {
        0 => None,
        4 => {
            let v: [u8; 4] = (&*b).try_into().expect("size already proven");
            Some(IpAddr::V4(Ipv4Addr::from(v)))
        }
        16 => {
            let v: [u8; 16] = (&*b).try_into().expect("size already proven");
            Some(IpAddr::V6(Ipv6Addr::from(v)))
        }
        n => return Err(WorkloadError::ByteAddressParse(n)),
    })
}

impl TryFrom<&XdsWorkload> for Workload {
    type Error = WorkloadError;
    fn try_from(resource: &XdsWorkload) -> Result<Self, Self::Error> {
        let waypoint = byte_to_ip(resource.waypoint_address.clone())?;
        let address =
            byte_to_ip(resource.address.clone())?.ok_or(WorkloadError::ByteAddressParse(0))?;
        // TODO can we borrow instead of clone everywhere?
        Ok(Workload {
            workload_ip: address,
            waypoint_address: waypoint,
            gateway_ip: None,

            protocol: Protocol::try_from(xds::istio::workload::Protocol::from_i32(
                resource.protocol,
            ))?,

            name: resource.name.clone(),
            namespace: resource.namespace.clone(),
            service_account: {
                let result = resource.service_account.clone();
                if result.is_empty() {
                    "default".into()
                } else {
                    result
                }
            },
            node: resource.node.clone(),

            native_hbone: resource.native_hbone,
        })
    }
}

pub struct WorkloadManager {
    workloads: Arc<Mutex<WorkloadInformation>>,
    xds_client: xds::AdsClient,
    local_client: LocalClient,
}

fn handle_xds<F: FnOnce() -> anyhow::Result<()>>(ctx: &mut HandlerContext, name: String, f: F) {
    debug!("handling update {}", name);
    let result: anyhow::Result<()> = f();
    if let Err(e) = result {
        warn!("rejecting workload {name}: {e}");
        ctx.reject(name, e)
    }
}

impl xds::Handler<XdsWorkload> for Arc<Mutex<WorkloadInformation>> {
    fn handle(&self, ctx: &mut HandlerContext, updates: Vec<XdsUpdate<XdsWorkload>>) {
        let mut wli = self.lock().unwrap();
        for res in updates {
            let name = res.name();
            handle_xds(ctx, name, || {
                match res {
                    XdsUpdate::Update(w) => {
                        let workload = Workload::try_from(&w.resource)?;
                        // TODO: we process each item on its own, this may lead to heavy lock contention.
                        // Need batch updates?
                        wli.insert(workload.clone());
                        for (vip, pl) in &w.resource.virtual_ips {
                            let ip = vip.parse::<IpAddr>()?;
                            for port in &pl.ports {
                                let addr = SocketAddr::from((ip, port.service_port as u16));
                                let us = Upstream {
                                    workload: workload.clone(), // TODO avoid clones
                                    port: port.target_port as u16,
                                };
                                wli.vips.entry(addr).or_default().insert(us);
                            }
                        }
                    }
                    XdsUpdate::Remove(name) => {
                        info!("handling delete {}", name);
                        wli.remove(name);
                    }
                }
                Ok(())
            });
        }
    }
}

impl WorkloadManager {
    pub fn new(config: config::Config) -> WorkloadManager {
        let workloads: Arc<Mutex<WorkloadInformation>> = Default::default();
        let xds_workloads = workloads.clone();
        let xds_client = xds::Config::new()
            .with_workload_handler(xds_workloads)
            .watch(xds::WORKLOAD_TYPE.into())
            .build();
        let local_workloads = workloads.clone();
        let local_client = LocalClient {
            path: config.local_xds_path,
            workloads: local_workloads,
        };
        WorkloadManager {
            xds_client,
            workloads,
            local_client,
        }
    }

    pub async fn run(self) -> anyhow::Result<()> {
        tokio::try_join!(
            self.xds_client.run().map_err(|e| anyhow::anyhow!(e)),
            self.local_client.run()
        )?;
        Ok(())
    }

    pub fn workloads(&self) -> Arc<Mutex<WorkloadInformation>> {
        self.workloads.clone()
    }
}

/// LocalClient serves as a local file reader alternative for XDS. This is intended for testing.
struct LocalClient {
    path: Option<String>,
    workloads: Arc<Mutex<WorkloadInformation>>,
}

impl LocalClient {
    async fn run(self) -> Result<(), anyhow::Error> {
        let path = match self.path {
            Some(p) => p,
            None => return Ok(()),
        };
        info!("running local client");
        // Currently, we just load the file once. In the future, we could dynamically reload.
        let data = tokio::fs::read_to_string(path).await?;
        let r: Vec<Workload> = serde_yaml::from_str(&data)?;
        let mut wli = self.workloads.lock().unwrap();
        for wl in r {
            info!("inserting local workloads {wl}");
            wli.insert(wl);
        }
        Ok(())
    }
}

#[derive(serde::Serialize, Default)]
/// A WorkloadInformation encapsulates all information about workloads in the mesh
pub struct WorkloadInformation {
    workloads: HashMap<IpAddr, Workload>,
    name_index: HashMap<String, IpAddr>,
    vips: HashMap<SocketAddr, HashSet<Upstream>>,
}

impl WorkloadInformation {
    fn insert(&mut self, w: Workload) {
        let wip = w.workload_ip;
        let wname = w.resource_name();
        self.workloads.insert(wip, w);
        self.name_index.insert(wname, wip);
    }

    fn remove(&mut self, name: String) {
        if let Some(ip) = self.name_index.remove(&name) {
            if let Some(_workload) = self.workloads.remove(&ip) {
                // TODO: store VIPs in workload so we can remove them
            } else {
                panic!(
                    "removed name {} with ip {}, but was not found in workload map",
                    name, ip
                )
            }
        }
    }

    pub fn find_workload(&self, addr: &IpAddr) -> Option<&Workload> {
        self.workloads.get(addr)
    }

    pub fn find_upstream(&self, addr: SocketAddr) -> (Upstream, bool) {
        if let Some(upstream) = self.vips.get(&addr) {
            let us: &Upstream = upstream.iter().next().unwrap();
            // TODO: avoid clone
            let mut us: Upstream = us.clone();
            Self::set_gateway_ip(&mut us);
            debug!("found upstream from VIP: {}", us);
            return (us, true);
        }
        if let Some(wl) = self.workloads.get(&addr.ip()) {
            let mut us = Upstream {
                workload: wl.clone(),
                port: addr.port(),
            };
            Self::set_gateway_ip(&mut us);
            debug!("found upstream: {}", us);
            return (us, false);
        }
        (
            Upstream {
                port: addr.port(),
                workload: Workload {
                    workload_ip: addr.ip(),
                    waypoint_address: None,
                    gateway_ip: Some(addr),
                    protocol: Protocol::Tcp,

                    name: "".to_string(),
                    namespace: "".to_string(),
                    node: "".to_string(),
                    service_account: "".to_string(),

                    native_hbone: false,
                },
            },
            false,
        )
    }

    fn set_gateway_ip(us: &mut Upstream) {
        let mut ip = us.workload.workload_ip;
        if us.workload.waypoint_address.is_some() {
            ip = us.workload.waypoint_address.unwrap();
        }
        if us.workload.gateway_ip.is_none() {
            us.workload.gateway_ip = Some(match us.workload.protocol {
                Protocol::Hbone => SocketAddr::from((ip, 15008)),
                Protocol::Tcp => SocketAddr::from((us.workload.workload_ip, us.port)),
            });
        }
    }
}

#[allow(clippy::enum_variant_names)]
#[derive(Error, Debug)]
pub enum WorkloadError {
    #[error("failed to parse address: {0}")]
    AddressParse(#[from] net::AddrParseError),
    #[error("failed to parse address, had {0} bytes")]
    ByteAddressParse(usize),
    #[error("unknown protocol {0}")]
    ProtocolParse(String),
}
<commit_msg>Audit clone usages (#36)<commit_after>use std::collections::{HashMap, HashSet};
use std::convert::Into;
use std::net::{IpAddr, SocketAddr};
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use std::{fmt, net};

use futures::future::TryFutureExt;
use thiserror::Error;
use tracing::{debug, error, info, warn};

use xds::istio::workload::Workload as XdsWorkload;

use crate::identity::Identity;
use crate::workload::WorkloadError::ProtocolParse;
use crate::xds::{HandlerContext, XdsUpdate};
use crate::{config, xds};

#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum Protocol {
    Tcp,
    Hbone,
}

impl Default for Protocol {
    fn default() -> Self {
        Protocol::Tcp
    }
}

impl TryFrom<Option<xds::istio::workload::Protocol>> for Protocol {
    type Error = WorkloadError;

    fn try_from(value: Option<xds::istio::workload::Protocol>) -> Result<Self, Self::Error> {
        match value {
            Some(xds::istio::workload::Protocol::Http) => Ok(Protocol::Hbone),
            Some(xds::istio::workload::Protocol::Direct) => Ok(Protocol::Hbone),
            None => Err(ProtocolParse("unknown type".into())),
        }
    }
}

#[derive(Debug, Hash, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Workload {
    pub workload_ip: IpAddr,
    #[serde(default)]
    pub waypoint_address: Option<IpAddr>,
    #[serde(default)]
    pub gateway_ip: Option<SocketAddr>,
    #[serde(default)]
    pub protocol: Protocol,

    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub namespace: String,
    #[serde(default)]
    pub service_account: String,

    #[serde(default)]
    pub node: String,

    #[serde(default)]
    pub native_hbone: bool,
}

impl Workload {
    pub fn identity(&self) -> Identity {
        Identity::Spiffe {
            /// TODO: don't hardcode
            trust_domain: "cluster.local".to_string(),
            namespace: self.namespace.clone(),
            service_account: self.service_account.clone(),
        }
    }
}

impl Workload {
    fn resource_name(&self) -> String {
        self.name.to_owned() + "/" + self.namespace.as_str()
    }
}

impl fmt::Display for Workload {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Workload{{{} at {} via {} ({:?})}}",
            self.name,
            self.workload_ip,
            self.gateway_ip
                .map(|x| format!("{}", x))
                .unwrap_or_else(|| "None".into()),
            self.protocol
        )
    }
}

#[derive(Debug, Hash, Eq, PartialEq, Clone, serde::Serialize)]
pub struct Upstream {
    pub workload: Workload,
    pub port: u16,
}

impl fmt::Display for Upstream {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Upstream{{{} at {}:{} via {} ({:?})}}",
            self.workload.name,
            self.workload.workload_ip,
            self.port,
            self.workload
                .gateway_ip
                .map(|x| format!("{}", x))
                .unwrap_or_else(|| "None".into()),
            self.workload.protocol
        )
    }
}

fn byte_to_ip(b: &bytes::Bytes) -> Result<Option<IpAddr>, WorkloadError> {
    Ok(match b.len() {
        0 => None,
        4 => {
            let v: [u8; 4] = b.deref().try_into().expect("size already proven");
            Some(IpAddr::from(v))
        }
        16 => {
            let v: [u8; 16] = b.deref().try_into().expect("size already proven");
            Some(IpAddr::from(v))
        }
        n => return Err(WorkloadError::ByteAddressParse(n)),
    })
}

impl TryFrom<&XdsWorkload> for Workload {
    type Error = WorkloadError;
    fn try_from(resource: &XdsWorkload) -> Result<Self, Self::Error> {
        let resource: XdsWorkload = resource.to_owned();
        let waypoint = byte_to_ip(&resource.waypoint_address)?;
        let address = byte_to_ip(&resource.address)?.ok_or(WorkloadError::ByteAddressParse(0))?;
        Ok(Workload {
            workload_ip: address,
            waypoint_address: waypoint,
            gateway_ip: None,

            protocol: Protocol::try_from(xds::istio::workload::Protocol::from_i32(
                resource.protocol,
            ))?,

            name: resource.name,
            namespace: resource.namespace,
            service_account: {
                let result = resource.service_account;
                if result.is_empty() {
                    "default".into()
                } else {
                    result
                }
            },
            node: resource.node,

            native_hbone: resource.native_hbone,
        })
    }
}

pub struct WorkloadManager {
    workloads: Arc<Mutex<WorkloadInformation>>,
    xds_client: xds::AdsClient,
    local_client: LocalClient,
}

fn handle_xds<F: FnOnce() -> anyhow::Result<()>>(ctx: &mut HandlerContext, name: String, f: F) {
    debug!("handling update {}", name);
    let result: anyhow::Result<()> = f();
    if let Err(e) = result {
        warn!("rejecting workload {name}: {e}");
        ctx.reject(name, e)
    }
}

impl xds::Handler<XdsWorkload> for Arc<Mutex<WorkloadInformation>> {
    fn handle(&self, ctx: &mut HandlerContext, updates: Vec<XdsUpdate<XdsWorkload>>) {
        let mut wli = self.lock().unwrap();
        for res in updates {
            let name = res.name();
            handle_xds(ctx, name, || {
                match res {
                    XdsUpdate::Update(w) => {
                        let workload = Workload::try_from(&w.resource)?;
                        // TODO: we process each item on its own, this may lead to heavy lock contention.
                        // Need batch updates?
                        wli.insert(workload.clone());
                        for (vip, pl) in &w.resource.virtual_ips {
                            let ip = vip.parse::<IpAddr>()?;
                            for port in &pl.ports {
                                let addr = SocketAddr::from((ip, port.service_port as u16));
                                let us = Upstream {
                                    workload: workload.clone(), // TODO avoid clones
                                    port: port.target_port as u16,
                                };
                                wli.vips.entry(addr).or_default().insert(us);
                            }
                        }
                    }
                    XdsUpdate::Remove(name) => {
                        info!("handling delete {}", name);
                        wli.remove(name);
                    }
                }
                Ok(())
            });
        }
    }
}

impl WorkloadManager {
    pub fn new(config: config::Config) -> WorkloadManager {
        let workloads: Arc<Mutex<WorkloadInformation>> = Default::default();
        let xds_workloads = workloads.clone();
        let xds_client = xds::Config::new()
            .with_workload_handler(xds_workloads)
            .watch(xds::WORKLOAD_TYPE.into())
            .build();
        let local_workloads = workloads.clone();
        let local_client = LocalClient {
            path: config.local_xds_path,
            workloads: local_workloads,
        };
        WorkloadManager {
            xds_client,
            workloads,
            local_client,
        }
    }

    pub async fn run(self) -> anyhow::Result<()> {
        tokio::try_join!(
            self.xds_client.run().map_err(|e| anyhow::anyhow!(e)),
            self.local_client.run()
        )?;
        Ok(())
    }

    pub fn workloads(&self) -> Arc<Mutex<WorkloadInformation>> {
        self.workloads.clone()
    }
}

/// LocalClient serves as a local file reader alternative for XDS. This is intended for testing.
struct LocalClient {
    path: Option<String>,
    workloads: Arc<Mutex<WorkloadInformation>>,
}

impl LocalClient {
    async fn run(self) -> Result<(), anyhow::Error> {
        let path = match self.path {
            Some(p) => p,
            None => return Ok(()),
        };
        info!("running local client");
        // Currently, we just load the file once. In the future, we could dynamically reload.
        let data = tokio::fs::read_to_string(path).await?;
        let r: Vec<Workload> = serde_yaml::from_str(&data)?;
        let mut wli = self.workloads.lock().unwrap();
        for wl in r {
            info!("inserting local workloads {wl}");
            wli.insert(wl);
        }
        Ok(())
    }
}

#[derive(serde::Serialize, Default)]
/// A WorkloadInformation encapsulates all information about workloads in the mesh
pub struct WorkloadInformation {
    workloads: HashMap<IpAddr, Workload>,
    name_index: HashMap<String, IpAddr>,
    vips: HashMap<SocketAddr, HashSet<Upstream>>,
}

impl WorkloadInformation {
    fn insert(&mut self, w: Workload) {
        let wip = w.workload_ip;
        let wname = w.resource_name();
        self.workloads.insert(wip, w);
        self.name_index.insert(wname, wip);
    }

    fn remove(&mut self, name: String) {
        if let Some(ip) = self.name_index.remove(&name) {
            if let Some(_workload) = self.workloads.remove(&ip) {
                // TODO: store VIPs in workload so we can remove them
            } else {
                panic!(
                    "removed name {} with ip {}, but was not found in workload map",
                    name, ip
                )
            }
        }
    }

    pub fn find_workload(&self, addr: &IpAddr) -> Option<&Workload> {
        self.workloads.get(addr)
    }

    pub fn find_upstream(&self, addr: SocketAddr) -> (Upstream, bool) {
        if let Some(upstream) = self.vips.get(&addr) {
            let us: &Upstream = upstream.iter().next().unwrap();
            // TODO: avoid clone
            let mut us: Upstream = us.clone();
            Self::set_gateway_ip(&mut us);
            debug!("found upstream from VIP: {}", us);
            return (us, true);
        }
        if let Some(wl) = self.workloads.get(&addr.ip()) {
            let mut us = Upstream {
                workload: wl.clone(),
                port: addr.port(),
            };
            Self::set_gateway_ip(&mut us);
            debug!("found upstream: {}", us);
            return (us, false);
        }
        (
            Upstream {
                port: addr.port(),
                workload: Workload {
                    workload_ip: addr.ip(),
                    waypoint_address: None,
                    gateway_ip: Some(addr),
                    protocol: Protocol::Tcp,

                    name: "".to_string(),
                    namespace: "".to_string(),
                    node: "".to_string(),
                    service_account: "".to_string(),

                    native_hbone: false,
                },
            },
            false,
        )
    }

    fn set_gateway_ip(us: &mut Upstream) {
        let mut ip = us.workload.workload_ip;
        if us.workload.waypoint_address.is_some() {
            ip = us.workload.waypoint_address.unwrap();
        }
        if us.workload.gateway_ip.is_none() {
            us.workload.gateway_ip = Some(match us.workload.protocol {
                Protocol::Hbone => SocketAddr::from((ip, 15008)),
                Protocol::Tcp => SocketAddr::from((us.workload.workload_ip, us.port)),
            });
        }
    }
}

#[allow(clippy::enum_variant_names)]
#[derive(Error, Debug, PartialEq, Eq)]
pub enum WorkloadError {
    #[error("failed to parse address: {0}")]
    AddressParse(#[from] net::AddrParseError),
    #[error("failed to parse address, had {0} bytes")]
    ByteAddressParse(usize),
    #[error("unknown protocol {0}")]
    ProtocolParse(String),
}

#[cfg(test)]
mod tests {
    use bytes::Bytes;

    use super::*;

    #[test]
    fn byte_to_ipaddr_garbage() {
        let garbage = "not_an_ip";
        let bytes = Bytes::from(garbage);
        let result = byte_to_ip(&bytes);
        assert!(result.is_err());
        let actual_error: WorkloadError = result.unwrap_err();
        let expected_error = WorkloadError::ByteAddressParse(garbage.len());
        assert_eq!(actual_error, expected_error);
    }

    #[test]
    fn byte_to_ipaddr_empty() {
        let empty = "";
        let bytes = Bytes::from(empty);
        let result = byte_to_ip(&bytes);
        assert!(result.is_ok());
        let maybe_ip_addr = result.unwrap();
        assert!(maybe_ip_addr.is_none());
    }

    #[test]
    fn byte_to_ipaddr_unspecified() {
        let unspecified: Vec<u8> = vec![0, 0, 0, 0];
        let bytes = Bytes::from(unspecified);
        let result = byte_to_ip(&bytes);
        assert!(result.is_ok());
        let maybe_ip_addr = result.unwrap();
        assert!(maybe_ip_addr.is_some());
        let ip_addr = maybe_ip_addr.unwrap();
        assert!(ip_addr.is_unspecified(), "was not unspecified")
    }

    #[test]
    fn byte_to_ipaddr_v4_loopback() {
        let loopback: Vec<u8> = vec![127, 0, 0, 1];
        let bytes = Bytes::from(loopback);
        let result = byte_to_ip(&bytes);
        assert!(result.is_ok());
        let maybe_loopback_ip = result.unwrap();
        assert!(maybe_loopback_ip.is_some());
        assert_eq!(maybe_loopback_ip.unwrap().to_string(), "127.0.0.1");
    }

    #[test]
    fn byte_to_ipaddr_v4_happy() {
        let addr_vec: Vec<u8> = Vec::from([1, 1, 1, 1]);
        let bytes = &Bytes::from(addr_vec);
        let result = byte_to_ip(bytes);
        assert!(result.is_ok());
        let maybe_ip_addr = result.unwrap();
        assert!(maybe_ip_addr.is_some());
        let ip_addr = maybe_ip_addr.unwrap();
        assert!(ip_addr.is_ipv4(), "was not ipv4");
        assert!(!ip_addr.is_ipv6(), "was ipv6");
        assert!(!ip_addr.is_unspecified(), "was unspecified");
        assert_eq!(ip_addr.to_string(), "1.1.1.1");
    }

    #[test]
    fn byte_to_ipaddr_v6_happy() {
        let addr: Vec<u8> = Vec::from([
            32, 1, 13, 184, 133, 163, 0, 0, 0, 0, 138, 46, 3, 112, 115, 52,
        ]);
        let bytes = &Bytes::from(addr);
        let result = byte_to_ip(bytes);
        assert!(result.is_ok());
        let maybe_ip_addr = result.unwrap();
        assert!(maybe_ip_addr.is_some());
        let ip_addr = maybe_ip_addr.unwrap();
        assert!(ip_addr.is_ipv6(), "was not ipv6");
        assert!(!ip_addr.is_ipv4(), "was ipv4");
        assert!(!ip_addr.is_unspecified());
        assert_eq!(ip_addr.to_string(), "2001:db8:85a3::8a2e:370:7334");
    }

    #[test]
    fn byte_to_ipaddr_v6_loopback() {
        let addr_vec: Vec<u8> = Vec::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
        let bytes = &Bytes::from(addr_vec);
        let result = byte_to_ip(bytes);
        assert!(result.is_ok());
        let maybe_loopback_ip = result.unwrap();
        assert!(maybe_loopback_ip.is_some());
        assert_eq!(maybe_loopback_ip.unwrap().to_string(), "::1");
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#![feature(unboxed_closures)]

#[phase(plugin)]
extern crate glium_macros;

extern crate glutin;
extern crate glium;

use glium::{Texture, Surface};

mod support;

#[test]
fn texture_1d_creation() {
    // ignoring test on travis
    // TODO: find out why they are failing
    if ::std::os::getenv("TRAVIS").is_some() {
        return;
    }

    let display = support::build_display();

    let texture = glium::texture::Texture1d::new(&display, vec![
        (0.0, 0.0, 0.0, 0.0),
        (0.0, 0.0, 0.0, 0.0),
        (0.0, 0.0, 0.0, 0.0),
    ]);

    assert_eq!(texture.get_width(), 3);
    assert_eq!(texture.get_height(), None);
    assert_eq!(texture.get_depth(), None);
    assert_eq!(texture.get_array_size(), None);

    display.assert_no_error();
}

#[test]
fn texture_2d_creation() {
    // ignoring test on travis
    // TODO: find out why they are failing
    if ::std::os::getenv("TRAVIS").is_some() {
        return;
    }

    let display = support::build_display();

    let texture = glium::texture::Texture2d::new(&display, vec![
        vec![(0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0)],
        vec![(0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0)],
        vec![(0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0f32)],
    ]);

    assert_eq!(texture.get_width(), 2);
    assert_eq!(texture.get_height(), Some(3));
    assert_eq!(texture.get_depth(), None);
    assert_eq!(texture.get_array_size(), None);

    display.assert_no_error();
}

#[test]
fn texture_3d_creation() {
    // ignoring test on travis
    // TODO: find out why they are failing
    if ::std::os::getenv("TRAVIS").is_some() {
        return;
    }

    let display = support::build_display();

    let texture = glium::texture::Texture3d::new(&display, vec![
        vec![
            vec![(0.0, 0.0, 0.0, 0.0)],
            vec![(0.0, 0.0, 0.0, 0.0)],
        ],
        vec![
            vec![(0.0, 0.0, 0.0, 0.0)],
            vec![(0.0, 0.0, 0.0, 0.0)],
        ],
        vec![
            vec![(0.0, 0.0, 0.0, 0.0)],
            vec![(0.0, 0.0, 0.0, 0.0f32)],
        ],
    ]);

    assert_eq!(texture.get_width(), 1);
    assert_eq!(texture.get_height(), Some(2));
    assert_eq!(texture.get_depth(), Some(3));
    assert_eq!(texture.get_array_size(), None);

    display.assert_no_error();
}

#[test]
#[cfg(feature = "gl_extensions")]
fn texture_2d_read() {
    let display = support::build_display();

    // we use only powers of two in order to avoid float rounding errors
    let texture = glium::texture::Texture2d::new(&display, vec![
        vec![(0u8, 1u8, 2u8), (4u8, 8u8, 16u8)],
        vec![(32u8, 64u8, 128u8), (32u8, 16u8, 4u8)],
    ]);

    let read_back: Vec<Vec<(u8, u8, u8)>> = texture.read();

    assert_eq!(read_back[0][0], (0, 1, 2));
    assert_eq!(read_back[0][1], (4, 8, 16));
    assert_eq!(read_back[1][0], (32, 64, 128));
    assert_eq!(read_back[1][1], (32, 16, 4));

    display.assert_no_error();
}

#[test]
fn compressed_texture_2d_creation() {
    let display = support::build_display();

    let texture = glium::texture::CompressedTexture2d::new(&display, vec![
        vec![(0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0)],
        vec![(0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0)],
        vec![(0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0f32)],
    ]);

    assert_eq!(texture.get_width(), 2);
    assert_eq!(texture.get_height(), Some(3));
    assert_eq!(texture.get_depth(), None);
    assert_eq!(texture.get_array_size(), None);

    display.assert_no_error();
}

#[test]
fn empty_texture2d() {
    let display = support::build_display();

    let texture = glium::texture::Texture2d::new_empty(&display,
                                                       glium::texture::UncompressedFloatFormat::
                                                           U8U8U8U8,
                                                       128, 128);

    display.assert_no_error();

    drop(texture);

    display.assert_no_error();
}

#[test]
#[ignore]  // FIXME: FAILING TEST
#[cfg(feature = "gl_extensions")]
fn render_to_texture2d() {
    use std::default::Default;

    let display = support::build_display();
    let (vb, ib, program) = support::build_fullscreen_red_pipeline(&display);

    let texture = glium::Texture2d::new_empty(&display,
                                              glium::texture::UncompressedFloatFormat::U8U8U8U8,
                                              1024, 1024);
    let params = Default::default();
    texture.as_surface().draw(&vb, &ib, &program, &glium::uniforms::EmptyUniforms, &params);

    let read_back: Vec<Vec<(f32, f32, f32, f32)>> = texture.read();

    assert_eq!(read_back[0][0], (1.0, 0.0, 0.0, 1.0));
    assert_eq!(read_back[512][512], (1.0, 0.0, 0.0, 1.0));
    assert_eq!(read_back[1023][1023], (1.0, 0.0, 0.0, 1.0));

    display.assert_no_error();
}
<commit_msg>Remove travis filtering in tests/texture.rs<commit_after>#![feature(phase)]
#![feature(unboxed_closures)]

#[phase(plugin)]
extern crate glium_macros;

extern crate glutin;
extern crate glium;

use glium::{Texture, Surface};

mod support;

#[test]
fn texture_1d_creation() {
    let display = support::build_display();

    let texture = glium::texture::Texture1d::new(&display, vec![
        (0, 0, 0, 0),
        (0, 0, 0, 0),
        (0, 0, 0, 0u8),
    ]);

    assert_eq!(texture.get_width(), 3);
    assert_eq!(texture.get_height(), None);
    assert_eq!(texture.get_depth(), None);
    assert_eq!(texture.get_array_size(), None);

    display.assert_no_error();
}

#[test]
fn texture_2d_creation() {
    let display = support::build_display();

    let texture = glium::texture::Texture2d::new(&display, vec![
        vec![(0, 0, 0, 0), (0, 0, 0, 0)],
        vec![(0, 0, 0, 0), (0, 0, 0, 0)],
        vec![(0, 0, 0, 0), (0, 0, 0, 0u8)],
    ]);

    assert_eq!(texture.get_width(), 2);
    assert_eq!(texture.get_height(), Some(3));
    assert_eq!(texture.get_depth(), None);
    assert_eq!(texture.get_array_size(), None);

    display.assert_no_error();
}

#[test]
fn texture_3d_creation() {
    let display = support::build_display();

    let texture = glium::texture::Texture3d::new(&display, vec![
        vec![
            vec![(0, 0, 0, 0)],
            vec![(0, 0, 0, 0)],
        ],
        vec![
            vec![(0, 0, 0, 0)],
            vec![(0, 0, 0, 0)],
        ],
        vec![
            vec![(0, 0, 0, 0)],
            vec![(0, 0, 0, 0u8)],
        ],
    ]);

    assert_eq!(texture.get_width(), 1);
    assert_eq!(texture.get_height(), Some(2));
    assert_eq!(texture.get_depth(), Some(3));
    assert_eq!(texture.get_array_size(), None);

    display.assert_no_error();
}

#[test]
#[cfg(feature = "gl_extensions")]
fn texture_2d_read() {
    let display = support::build_display();

    // we use only powers of two in order to avoid float rounding errors
    let texture = glium::texture::Texture2d::new(&display, vec![
        vec![(0u8, 1u8, 2u8), (4u8, 8u8, 16u8)],
        vec![(32u8, 64u8, 128u8), (32u8, 16u8, 4u8)],
    ]);

    let read_back: Vec<Vec<(u8, u8, u8)>> = texture.read();

    assert_eq!(read_back[0][0], (0, 1, 2));
    assert_eq!(read_back[0][1], (4, 8, 16));
    assert_eq!(read_back[1][0], (32, 64, 128));
    assert_eq!(read_back[1][1], (32, 16, 4));

    display.assert_no_error();
}

#[test]
fn compressed_texture_2d_creation() {
    let display = support::build_display();

    let texture = glium::texture::CompressedTexture2d::new(&display, vec![
        vec![(0, 0, 0, 0), (0, 0, 0, 0)],
        vec![(0, 0, 0, 0), (0, 0, 0, 0)],
        vec![(0, 0, 0, 0), (0, 0, 0, 0u8)],
    ]);

    assert_eq!(texture.get_width(), 2);
    assert_eq!(texture.get_height(), Some(3));
    assert_eq!(texture.get_depth(), None);
    assert_eq!(texture.get_array_size(), None);

    display.assert_no_error();
}

#[test]
fn empty_texture2d() {
    let display = support::build_display();

    let texture = glium::texture::Texture2d::new_empty(&display,
                                                       glium::texture::UncompressedFloatFormat::
                                                           U8U8U8U8,
                                                       128, 128);

    display.assert_no_error();

    drop(texture);

    display.assert_no_error();
}

#[test]
#[ignore]  // FIXME: FAILING TEST
#[cfg(feature = "gl_extensions")]
fn render_to_texture2d() {
    use std::default::Default;

    let display = support::build_display();
    let (vb, ib, program) = support::build_fullscreen_red_pipeline(&display);

    let texture = glium::Texture2d::new_empty(&display,
                                              glium::texture::UncompressedFloatFormat::U8U8U8U8,
                                              1024, 1024);
    let params = Default::default();
    texture.as_surface().draw(&vb, &ib, &program, &glium::uniforms::EmptyUniforms, &params);

    let read_back: Vec<Vec<(u8, u8, u8, u8)>> = texture.read();

    assert_eq!(read_back[0][0], (255, 0, 0, 255));
    assert_eq!(read_back[512][512], (255, 0, 0, 255));
    assert_eq!(read_back[1023][1023], (255, 0, 0, 255));

    display.assert_no_error();
}
<|endoftext|>


<commit_before>use super::{
    flow_control::{ExportAction, LocalAction},
    Shell,
};
use crate::{
    assignments::*,
    builtins::Status,
    parser::lexers::assignments::{Key, Operator, Primitive},
    shell::{flow_control::Function, Value, Variables},
};
use std::{
    env,
    io::{self, BufWriter, Write},
    rc::Rc,
    result::Result,
};
use types_rs::{EuclDiv, Modifications, OpError, Pow};

fn list_vars(shell: &Shell<'_>) -> Result<(), io::Error> {
    let stdout = io::stdout();
    let mut buffer = BufWriter::new(stdout.lock());

    // Write all the string variables to the buffer.
    buffer.write_all(b"# String Variables\n")?;
    for (key, val) in shell.variables.string_vars() {
        writeln!(buffer, "{} = {}", key, val)?;
    }

    // Then immediately follow that with a list of array variables.
    buffer.write_all(b"\n# Array Variables\n")?;
    for (key, val) in shell.variables.arrays() {
        write!(buffer, "{} = [ ", key)?;
        let mut vars = val.iter();
        if let Some(var) = vars.next() {
            write!(buffer, "'{}' ", var)?;
            vars.map(|var| write!(buffer, ", '{}' ", var)).collect::<Result<Vec<_>, _>>()?;
        }
        writeln!(buffer, "]")?;
    }
    Ok(())
}

/// Represents: A variable store capable of setting local variables or
/// exporting variables to some global environment
impl<'b> Shell<'b> {
    /// Export a variable to the process environment given a binding
    pub fn export(&mut self, action: &ExportAction) -> Status {
        match action {
            ExportAction::Assign(ref keys, op, ref vals) => {
                let actions = AssignmentActions::new(keys, *op, vals);

                for action in actions {
                    let err = action.map_err(|e| e.to_string()).and_then(|act| {
                        let Action(key, operator, expression) = act;
                        value_check(self, expression, &key.kind)
                            .map_err(|e| format!("{}: {}", key.name, e))
                            // TODO: handle operators here in the same way as local
                            .and_then(|rhs| match &rhs {
                                Value::Array(_) if operator == Operator::Equal => {
                                    env::set_var(key.name, format!("{}", rhs));
                                    Ok(())
                                }
                                Value::Array(_) => Err("arithmetic operators on array \
                                                        expressions aren't supported yet."
                                    .to_string()),
                                Value::Str(_) => {
                                    env::set_var(&key.name, &format!("{}", rhs));
                                    Ok(())
                                }
                                _ => Err(format!(
                                    "{}: export of type '{}' is not supported",
                                    key.name, key.kind
                                )),
                            })
                    });

                    if let Err(why) = err {
                        return Status::error(format!("ion: assignment error: {}", why));
                    }
                }

                Status::SUCCESS
            }
            ExportAction::LocalExport(ref key) => match self.variables.get_str(key) {
                Ok(var) => {
                    env::set_var(key, &*var);
                    Status::SUCCESS
                }
                Err(_) => {
                    Status::error(format!("ion: cannot export {} because it does not exist.", key))
                }
            },
            ExportAction::List => {
                let stdout = io::stdout();
                let mut stdout = stdout.lock();
                for (key, val) in env::vars() {
                    let _ = writeln!(stdout, "{} = \"{}\"", key, val);
                }
                Status::SUCCESS
            }
        }
    }

    /// Collect all updates to perform on variables for a given assignment action
    pub(crate) fn calculate<'a>(
        &mut self,
        actions: AssignmentActions<'a>,
    ) -> Result<Vec<(Key<'a>, Value<Rc<Function<'b>>>)>, String> {
        let mut backup: Vec<_> = Vec::with_capacity(4);
        for action in actions {
            let Action(key, operator, expression) = action.map_err(|e| e.to_string())?;

            // sanitize variable names
            if ["HOME", "HOST", "PWD", "MWD", "SWD", "?"].contains(&key.name) {
                return Err(format!("not allowed to set `{}`", key.name));
            }

            if !Variables::is_valid_name(key.name) {
                return Err("invalid variable name\nVariable names may only be (unicode) \
                            alphanumeric or `_`\nThe first character must be alphabetic or `_`"
                    .to_string());
            }

            if operator == Operator::OptionalEqual && self.variables.get(key.name).is_some() {
                continue;
            }

            let rhs = value_check(self, expression, &key.kind)
                .map_err(|why| format!("{}: {}", key.name, why))?;

            match (&rhs, &key.kind) {
                (Value::HashMap(_), Primitive::Indexed(..)) => {
                    Err("cannot insert hmap into index".to_string())?
                }
                (Value::BTreeMap(_), Primitive::Indexed(..)) => {
                    Err("cannot insert bmap into index".to_string())?
                }
                (Value::Array(_), Primitive::Indexed(..)) => {
                    Err("multi-dimensional arrays are not yet supported".to_string())?
                }
                _ if [Operator::Equal, Operator::OptionalEqual].contains(&operator) => {
                    backup.push((key, rhs))
                }
                _ => {
                    let lhs = self.variables.get(key.name).ok_or_else(|| {
                        format!("cannot update non existing variable `{}`", key.name)
                    })?;
                    let val = apply(operator, lhs, rhs).map_err(|_| {
                        format!(
                            "type error: variable `{}` of type `{}` does not support operator",
                            key.name, key.kind
                        )
                    })?;
                    backup.push((key, val));
                }
            }
        }
        Ok(backup)
    }

    /// Set a local variable given a binding
    pub fn local(&mut self, action: &LocalAction) -> Status {
        match action {
            LocalAction::List => {
                let _ = list_vars(self);
                Status::SUCCESS
            }
            LocalAction::Assign(ref keys, op, ref vals) => {
                let actions = AssignmentActions::new(keys, *op, vals);
                if let Err(why) = self.calculate(actions).and_then(|apply| {
                    for (key, value) in apply {
                        self.assign(&key, value)?
                    }
                    Ok(())
                }) {
                    Status::error(format!("ion: assignment error: {}", why))
                } else {
                    Status::SUCCESS
                }
            }
        }
    }
}

// This should logically be a method over operator, but Value is only accessible in the main repo
// TODO: too much allocations occur over here. We need to expand variables before they get
// parsed
fn apply<'a>(
    op: Operator,
    lhs: &Value<Rc<Function<'a>>>,
    rhs: Value<Rc<Function<'a>>>,
) -> Result<Value<Rc<Function<'a>>>, OpError> {
    match op {
        Operator::Add => lhs + rhs,
        Operator::Divide => lhs / rhs,
        Operator::IntegerDivide => lhs.eucl_div(rhs),
        Operator::Subtract => lhs - rhs,
        Operator::Multiply => lhs * rhs,
        Operator::Exponent => lhs.pow(rhs),
        Operator::Concatenate => {
            let mut lhs = lhs.clone();
            lhs.append(rhs);
            Ok(lhs)
        }
        Operator::ConcatenateHead => {
            let mut lhs = lhs.clone();
            lhs.prepend(rhs);
            Ok(lhs)
        }
        Operator::Filter => match (&lhs, &rhs) {
            (Value::Array(ref array), Value::Str(_)) => {
                // TODO: this should be avoided, but for now values are expanded too late, so we
                // must store copies of arrays to update
                let mut array = array.clone();
                array.retain(|item| item != &rhs);
                Ok(Value::Array(array))
            }
            (Value::Array(ref array), Value::Array(values)) => {
                // TODO: this should be avoided, but for now values are expanded too late, so we
                // must store copies of arrays to update
                let mut array = array.clone();
                array.retain(|item| !values.contains(item));
                Ok(Value::Array(array))
            }
            _ => Err(OpError::TypeError),
        },
        _ => unreachable!(),
    }
}
<commit_msg>improv: fix error message<commit_after>use super::{
    flow_control::{ExportAction, LocalAction},
    Shell,
};
use crate::{
    assignments::*,
    builtins::Status,
    parser::lexers::assignments::{Key, Operator, Primitive},
    shell::{flow_control::Function, Value, Variables},
};
use std::{
    env,
    io::{self, BufWriter, Write},
    rc::Rc,
    result::Result,
};
use types_rs::{EuclDiv, Modifications, OpError, Pow};

fn list_vars(shell: &Shell<'_>) -> Result<(), io::Error> {
    let stdout = io::stdout();
    let mut buffer = BufWriter::new(stdout.lock());

    // Write all the string variables to the buffer.
    buffer.write_all(b"# String Variables\n")?;
    for (key, val) in shell.variables.string_vars() {
        writeln!(buffer, "{} = {}", key, val)?;
    }

    // Then immediately follow that with a list of array variables.
    buffer.write_all(b"\n# Array Variables\n")?;
    for (key, val) in shell.variables.arrays() {
        write!(buffer, "{} = [ ", key)?;
        let mut vars = val.iter();
        if let Some(var) = vars.next() {
            write!(buffer, "'{}' ", var)?;
            vars.map(|var| write!(buffer, ", '{}' ", var)).collect::<Result<Vec<_>, _>>()?;
        }
        writeln!(buffer, "]")?;
    }
    Ok(())
}

/// Represents: A variable store capable of setting local variables or
/// exporting variables to some global environment
impl<'b> Shell<'b> {
    /// Export a variable to the process environment given a binding
    pub fn export(&mut self, action: &ExportAction) -> Status {
        match action {
            ExportAction::Assign(ref keys, op, ref vals) => {
                let actions = AssignmentActions::new(keys, *op, vals);

                for action in actions {
                    let err = action.map_err(|e| e.to_string()).and_then(|act| {
                        let Action(key, operator, expression) = act;
                        value_check(self, expression, &key.kind)
                            .map_err(|e| format!("{}: {}", key.name, e))
                            // TODO: handle operators here in the same way as local
                            .and_then(|rhs| match &rhs {
                                Value::Array(_) if operator == Operator::Equal => {
                                    env::set_var(key.name, format!("{}", rhs));
                                    Ok(())
                                }
                                Value::Array(_) => Err("arithmetic operators on array \
                                                        expressions aren't supported yet."
                                    .to_string()),
                                Value::Str(_) => {
                                    env::set_var(&key.name, &format!("{}", rhs));
                                    Ok(())
                                }
                                _ => Err(format!(
                                    "{}: export of type '{}' is not supported",
                                    key.name, key.kind
                                )),
                            })
                    });

                    if let Err(why) = err {
                        return Status::error(format!("ion: assignment error: {}", why));
                    }
                }

                Status::SUCCESS
            }
            ExportAction::LocalExport(ref key) => match self.variables.get_str(key) {
                Ok(var) => {
                    env::set_var(key, &*var);
                    Status::SUCCESS
                }
                Err(_) => {
                    Status::error(format!("ion: cannot export {} because it does not exist.", key))
                }
            },
            ExportAction::List => {
                let stdout = io::stdout();
                let mut stdout = stdout.lock();
                for (key, val) in env::vars() {
                    let _ = writeln!(stdout, "{} = \"{}\"", key, val);
                }
                Status::SUCCESS
            }
        }
    }

    /// Collect all updates to perform on variables for a given assignment action
    pub(crate) fn calculate<'a>(
        &mut self,
        actions: AssignmentActions<'a>,
    ) -> Result<Vec<(Key<'a>, Value<Rc<Function<'b>>>)>, String> {
        let mut backup: Vec<_> = Vec::with_capacity(4);
        for action in actions {
            let Action(key, operator, expression) = action.map_err(|e| e.to_string())?;

            // sanitize variable names
            if ["HOME", "HOST", "PWD", "MWD", "SWD", "?"].contains(&key.name) {
                return Err(format!("not allowed to set `{}`", key.name));
            }

            if !Variables::is_valid_name(key.name) {
                return Err("invalid variable name: only alphanumerical characters and underscores are supported"
                    .to_string());
            }

            if operator == Operator::OptionalEqual && self.variables.get(key.name).is_some() {
                continue;
            }

            let rhs = value_check(self, expression, &key.kind)
                .map_err(|why| format!("{}: {}", key.name, why))?;

            match (&rhs, &key.kind) {
                (Value::HashMap(_), Primitive::Indexed(..)) => {
                    Err("cannot insert hmap into index".to_string())?
                }
                (Value::BTreeMap(_), Primitive::Indexed(..)) => {
                    Err("cannot insert bmap into index".to_string())?
                }
                (Value::Array(_), Primitive::Indexed(..)) => {
                    Err("multi-dimensional arrays are not yet supported".to_string())?
                }
                _ if [Operator::Equal, Operator::OptionalEqual].contains(&operator) => {
                    backup.push((key, rhs))
                }
                _ => {
                    let lhs = self.variables.get(key.name).ok_or_else(|| {
                        format!("cannot update non existing variable `{}`", key.name)
                    })?;
                    let val = apply(operator, lhs, rhs).map_err(|_| {
                        format!(
                            "type error: variable `{}` of type `{}` does not support operator",
                            key.name, key.kind
                        )
                    })?;
                    backup.push((key, val));
                }
            }
        }
        Ok(backup)
    }

    /// Set a local variable given a binding
    pub fn local(&mut self, action: &LocalAction) -> Status {
        match action {
            LocalAction::List => {
                let _ = list_vars(self);
                Status::SUCCESS
            }
            LocalAction::Assign(ref keys, op, ref vals) => {
                let actions = AssignmentActions::new(keys, *op, vals);
                if let Err(why) = self.calculate(actions).and_then(|apply| {
                    for (key, value) in apply {
                        self.assign(&key, value)?
                    }
                    Ok(())
                }) {
                    Status::error(format!("ion: assignment error: {}", why))
                } else {
                    Status::SUCCESS
                }
            }
        }
    }
}

// This should logically be a method over operator, but Value is only accessible in the main repo
// TODO: too much allocations occur over here. We need to expand variables before they get
// parsed
fn apply<'a>(
    op: Operator,
    lhs: &Value<Rc<Function<'a>>>,
    rhs: Value<Rc<Function<'a>>>,
) -> Result<Value<Rc<Function<'a>>>, OpError> {
    match op {
        Operator::Add => lhs + rhs,
        Operator::Divide => lhs / rhs,
        Operator::IntegerDivide => lhs.eucl_div(rhs),
        Operator::Subtract => lhs - rhs,
        Operator::Multiply => lhs * rhs,
        Operator::Exponent => lhs.pow(rhs),
        Operator::Concatenate => {
            let mut lhs = lhs.clone();
            lhs.append(rhs);
            Ok(lhs)
        }
        Operator::ConcatenateHead => {
            let mut lhs = lhs.clone();
            lhs.prepend(rhs);
            Ok(lhs)
        }
        Operator::Filter => match (&lhs, &rhs) {
            (Value::Array(ref array), Value::Str(_)) => {
                // TODO: this should be avoided, but for now values are expanded too late, so we
                // must store copies of arrays to update
                let mut array = array.clone();
                array.retain(|item| item != &rhs);
                Ok(Value::Array(array))
            }
            (Value::Array(ref array), Value::Array(values)) => {
                // TODO: this should be avoided, but for now values are expanded too late, so we
                // must store copies of arrays to update
                let mut array = array.clone();
                array.retain(|item| !values.contains(item));
                Ok(Value::Array(array))
            }
            _ => Err(OpError::TypeError),
        },
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>use std::slice::Iter;
use std::iter::Iterator;
use std::iter::Peekable;

use index::boolean_index::*;
use index::boolean_index::posting::Posting;
use index::boolean_index::query_result_iterator::nary_query_iterator::*;

pub mod nary_query_iterator;

macro_rules! unwrap_or_return_none{
    ($operand:expr) => {
        if let Some(x) = $operand {
            x
        } else {
            return None;
        }
    }
}

// The BooleanIndex implementation works with query iterators only. Why?
// 1. It is faster (no stack or heap allocation)
// 2. It is lazy
/// Wrapper around different query iterator types
/// Used to be able to simply and elegantly use nested queries of different types
pub enum QueryResultIterator<'a> {
    Empty,
    Atom(usize, Peekable<Iter<'a, Posting>>),
    NAry(NAryQueryIterator<'a>),
    Filter(FilterIterator<'a>),
}

impl<'a> Iterator for QueryResultIterator<'a> {
    type Item = &'a Posting;

    fn next(&mut self) -> Option<&'a Posting> {
        match *self {
            QueryResultIterator::Empty => None,
            QueryResultIterator::Atom(_, ref mut iter) => iter.next(),
            QueryResultIterator::NAry(ref mut iter) => iter.next(),
            QueryResultIterator::Filter(ref mut iter) => iter.next(),
        }
    }
}

impl<'a> QueryResultIterator<'a> {
    /// Used to be able to sort queries according to their estimated number of results
    /// This can be used to optimize efficiency on intersecting queries
    fn estimate_length(&self) -> usize {
        match *self {
            QueryResultIterator::Empty => 0,
            QueryResultIterator::Atom(_, ref iter) => iter.len(),
            QueryResultIterator::NAry(ref iter) => iter.estimate_length(),
            QueryResultIterator::Filter(ref iter) => iter.estimate_length(),
        }
    }

    /// Return the relative position of a query-part in the whole query
    /// Necessary for positional queries
    fn relative_position(&self) -> usize {
        match *self {
            QueryResultIterator::Atom(rpos, _) => rpos,
            _ => 0,
        }

    }

    /// Allows peeking. Used for union queries,
    /// which need to advance operands in some cases and peek in others
    fn peek(&mut self) -> Option<&'a Posting> {
        match *self {
            QueryResultIterator::Empty => None,
            QueryResultIterator::Atom(_, ref mut iter) => iter.peek().map(|val| *val),
            QueryResultIterator::NAry(ref mut iter) => iter.peek(),
            QueryResultIterator::Filter(ref mut iter) => iter.peek(),
        }
    }
}

pub struct FilterIterator<'a> {
    operator: FilterOperator,
    sand: Box<QueryResultIterator<'a>>,
    sieve: Box<QueryResultIterator<'a>>,
    peeked_value: Option<Option<&'a Posting>>,
}

impl<'a> Iterator for FilterIterator<'a> {
    type Item = &'a Posting;

    fn next(&mut self) -> Option<Self::Item> {
        if self.peeked_value.is_none() {
            match self.operator {
                FilterOperator::Not => self.next_not(),
            }
        } else {
            self.peeked_value.take().unwrap()
        }
    }
}

impl<'a> FilterIterator<'a> {
    pub fn new(operator: FilterOperator,
               sand: Box<QueryResultIterator<'a>>,
               sieve: Box<QueryResultIterator<'a>>)
               -> Self {
        FilterIterator {
            operator: operator,
            sand: sand,
            sieve: sieve,
            peeked_value: None,
        }
    }

    fn peek(&mut self) -> Option<&'a Posting> {
        if self.peeked_value.is_none() {
            self.peeked_value = Some(self.next())
        }
        self.peeked_value.unwrap()
    }

    fn estimate_length(&self) -> usize {
        let sand_len = self.sand.estimate_length();
        let sieve_len = self.sieve.estimate_length();
        if sand_len > sieve_len {
            sand_len - sieve_len
        } else {
            0
        }
    }

    fn next_not(&mut self) -> Option<&'a Posting> {
        'sand: loop {
            if let Some(sand) = self.sand.next() {
                'sieve: loop {
                    if let Some(sieve) = self.sieve.peek() {
                        if sand.0 < sieve.0 {
                            return Some(sand);
                        } else if sand.0 > sieve.0 {
                            self.sieve.next();
                            continue 'sieve;
                        } else {
                            continue 'sand;
                        }
                    } else {
                        return Some(sand);
                    }
                }
            } else {
                return None;
            }
        }

    }
}

#[cfg(test)]
mod tests {
    use index::Index;
    use index::boolean_index::*;

    fn prepare_index() -> BooleanIndex<usize> {
        let mut index = BooleanIndex::new();
        index.index_document(0..10);
        index.index_document((0..10).map(|i| i * 2));
        index.index_document(vec![5, 4, 3, 2, 1, 0].into_iter());

        index
    }

    #[test]
    fn peek() {
        let index = prepare_index();
        let mut qri = index.run_atom(0, &0);
        assert!(qri.peek() == qri.peek());
        qri = index.run_nary_query(&BooleanOperator::And,
                                   &vec![BooleanQuery::Atom(QueryAtom::new(0, 0)),
                                         BooleanQuery::Atom(QueryAtom::new(0, 0))]);
        assert!(qri.peek() == qri.peek());

    }

    #[test]
    fn estimate_length() {
        let index = prepare_index();
        assert!(index.run_atom(0, &0).estimate_length() == 3);
        assert!(index.run_atom(0, &3).estimate_length() == 2);
        assert!(index.run_atom(0, &16).estimate_length() == 1);
        assert!(index.run_nary_query(&BooleanOperator::And,
                            &vec![BooleanQuery::Atom(QueryAtom::new(0, 3)),
                                  BooleanQuery::Atom(QueryAtom::new(0, 16))])
            .estimate_length() == 1);
        assert!(index.run_nary_query(&BooleanOperator::Or,
                            &vec![BooleanQuery::Atom(QueryAtom::new(0, 3)),
                                  BooleanQuery::Atom(QueryAtom::new(0, 16))])
            .estimate_length() == 2);
    }

}
<commit_msg>PostingDecoder Interfaces<commit_after>use std::slice::Iter;
use std::iter::Iterator;
use std::iter::Peekable;
use std::io::{Read, Result};

use index::boolean_index::*;
use index::boolean_index::posting::Posting;
use index::boolean_index::query_result_iterator::nary_query_iterator::*;
use index::boolean_index::persistence::VByteDecoder;

pub mod nary_query_iterator;

macro_rules! unwrap_or_return_none{
    ($operand:expr) => {
        if let Some(x) = $operand {
            x
        } else {
            return None;
        }
    }
}

// The BooleanIndex implementation works with query iterators only. Why?
// 1. It is faster (no stack or heap allocation)
// 2. It is lazy
/// Wrapper around different query iterator types
/// Used to be able to simply and elegantly use nested queries of different types
pub enum QueryResultIterator<'a> {
    Empty,
    Atom(usize, Peekable<Iter<'a, Posting>>),
    NAry(NAryQueryIterator<'a>),
    Filter(FilterIterator<'a>),
}

impl<'a> Iterator for QueryResultIterator<'a> {
    type Item = &'a Posting;

    fn next(&mut self) -> Option<&'a Posting> {
        match *self {
            QueryResultIterator::Empty => None,
            QueryResultIterator::Atom(_, ref mut iter) => iter.next(),
            QueryResultIterator::NAry(ref mut iter) => iter.next(),
            QueryResultIterator::Filter(ref mut iter) => iter.next(),
        }
    }
}

impl<'a> QueryResultIterator<'a> {
    /// Used to be able to sort queries according to their estimated number of results
    /// This can be used to optimize efficiency on intersecting queries
    fn estimate_length(&self) -> usize {
        match *self {
            QueryResultIterator::Empty => 0,
            QueryResultIterator::Atom(_, ref iter) => iter.len(),
            QueryResultIterator::NAry(ref iter) => iter.estimate_length(),
            QueryResultIterator::Filter(ref iter) => iter.estimate_length(),
        }
    }

    /// Return the relative position of a query-part in the whole query
    /// Necessary for positional queries
    fn relative_position(&self) -> usize {
        match *self {
            QueryResultIterator::Atom(rpos, _) => rpos,
            _ => 0,
        }

    }

    /// Allows peeking. Used for union queries,
    /// which need to advance operands in some cases and peek in others
    fn peek(&mut self) -> Option<&'a Posting> {
        match *self {
            QueryResultIterator::Empty => None,
            QueryResultIterator::Atom(_, ref mut iter) => iter.peek().map(|val| *val),
            QueryResultIterator::NAry(ref mut iter) => iter.peek(),
            QueryResultIterator::Filter(ref mut iter) => iter.peek(),
        }
    }
}

pub struct PostingDecoder{
    bytes: VByteDecoder<>
}

impl<'a> PostingDecoder<'a> {
    pub fn new<TSource: Read>(source: &mut TSource) -> Result<Self> {
        let mut bytes = Vec::new();
        try!(source.read_to_end(&mut bytes));
        Ok(PostingDecoder{
            bytes: Box::new(bytes.into_iter())
        })
    }
}

impl<'a> Iterator for PostingDecoder<'a>{
    type Item = &'a Posting;

    fn next(&mut self) -> Option<Self::Item> {

    }
}

pub struct FilterIterator<'a> {
    operator: FilterOperator,
    sand: Box<QueryResultIterator<'a>>,
    sieve: Box<QueryResultIterator<'a>>,
    peeked_value: Option<Option<&'a Posting>>,
}

impl<'a> Iterator for FilterIterator<'a> {
    type Item = &'a Posting;

    fn next(&mut self) -> Option<Self::Item> {
        if self.peeked_value.is_none() {
            match self.operator {
                FilterOperator::Not => self.next_not(),
            }
        } else {
            self.peeked_value.take().unwrap()
        }
    }
}

impl<'a> FilterIterator<'a> {
    pub fn new(operator: FilterOperator,
               sand: Box<QueryResultIterator<'a>>,
               sieve: Box<QueryResultIterator<'a>>)
               -> Self {
        FilterIterator {
            operator: operator,
            sand: sand,
            sieve: sieve,
            peeked_value: None,
        }
    }

    fn peek(&mut self) -> Option<&'a Posting> {
        if self.peeked_value.is_none() {
            self.peeked_value = Some(self.next())
        }
        self.peeked_value.unwrap()
    }

    fn estimate_length(&self) -> usize {
        let sand_len = self.sand.estimate_length();
        let sieve_len = self.sieve.estimate_length();
        if sand_len > sieve_len {
            sand_len - sieve_len
        } else {
            0
        }
    }

    fn next_not(&mut self) -> Option<&'a Posting> {
        'sand: loop {
            if let Some(sand) = self.sand.next() {
                'sieve: loop {
                    if let Some(sieve) = self.sieve.peek() {
                        if sand.0 < sieve.0 {
                            return Some(sand);
                        } else if sand.0 > sieve.0 {
                            self.sieve.next();
                            continue 'sieve;
                        } else {
                            continue 'sand;
                        }
                    } else {
                        return Some(sand);
                    }
                }
            } else {
                return None;
            }
        }

    }
}

#[cfg(test)]
mod tests {
    use index::Index;
    use index::boolean_index::*;

    fn prepare_index() -> BooleanIndex<usize> {
        let mut index = BooleanIndex::new();
        index.index_document(0..10);
        index.index_document((0..10).map(|i| i * 2));
        index.index_document(vec![5, 4, 3, 2, 1, 0].into_iter());

        index
    }

    #[test]
    fn peek() {
        let index = prepare_index();
        let mut qri = index.run_atom(0, &0);
        assert!(qri.peek() == qri.peek());
        qri = index.run_nary_query(&BooleanOperator::And,
                                   &vec![BooleanQuery::Atom(QueryAtom::new(0, 0)),
                                         BooleanQuery::Atom(QueryAtom::new(0, 0))]);
        assert!(qri.peek() == qri.peek());

    }

    #[test]
    fn estimate_length() {
        let index = prepare_index();
        assert!(index.run_atom(0, &0).estimate_length() == 3);
        assert!(index.run_atom(0, &3).estimate_length() == 2);
        assert!(index.run_atom(0, &16).estimate_length() == 1);
        assert!(index.run_nary_query(&BooleanOperator::And,
                            &vec![BooleanQuery::Atom(QueryAtom::new(0, 3)),
                                  BooleanQuery::Atom(QueryAtom::new(0, 16))])
            .estimate_length() == 1);
        assert!(index.run_nary_query(&BooleanOperator::Or,
                            &vec![BooleanQuery::Atom(QueryAtom::new(0, 3)),
                                  BooleanQuery::Atom(QueryAtom::new(0, 16))])
            .estimate_length() == 2);
    }

}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use graphics::{ Context, DrawState, Graphics, Viewport };
use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;
use { gfx, Texture };

const POS_COMPONENTS: usize = 2;
const UV_COMPONENTS: usize = 2;

// Boiler plate for automatic attribute construction.
// Needs to be improved on gfx-rs side.
// For some reason, using ``*_COMPONENT` triggers some macros errors.

#[vertex_format]
struct PositionFormat { pos: [f32; 2] }

#[vertex_format]
struct ColorFormat { color: [f32; 4] }

#[vertex_format]
struct TexCoordsFormat { uv: [f32; 2] }

#[shader_param]
struct Params<R: gfx::Resources> {
    color: [f32; 4],
    _dummy: PhantomData<R>,
}

#[shader_param]
struct ParamsUV<R: gfx::Resources> {
    color: [f32; 4],
    s_texture: gfx::shade::TextureParam<R>,
}

/// The data used for drawing 2D graphics.
pub struct Gfx2d<R: gfx::Resources> {
    buffer_pos: gfx::BufferHandle<R, f32>,
    buffer_uv: gfx::BufferHandle<R, f32>,
    batch: gfx::batch::OwnedBatch<Params<R>>,
    batch_uv: gfx::batch::OwnedBatch<ParamsUV<R>>,
}

impl<R: gfx::Resources> Gfx2d<R> {
    /// Creates a new G2D object.
    pub fn new<D, F>(device: &mut D, factory: &mut F) -> Self
        where D: gfx::Device,
              F: gfx::Factory<R>
    {
        use gfx::traits::*;
        use gfx::VertexFormat;
        use shaders::{ colored, textured };

        let ref capabilities = device.get_capabilities();

        let program = {
            let vertex = gfx::ShaderSource {
                glsl_120: Some(colored::VERTEX_GLSL_120),
                glsl_150: Some(colored::VERTEX_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            let fragment = gfx::ShaderSource {
                glsl_120: Some(colored::FRAGMENT_GLSL_120),
                glsl_150: Some(colored::FRAGMENT_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            factory.link_program_source(
                vertex,
                fragment,
                capabilities
            ).unwrap()
        };

        let program_uv = {
            let vertex = gfx::ShaderSource {
                glsl_120: Some(textured::VERTEX_GLSL_120),
                glsl_150: Some(textured::VERTEX_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            let fragment = gfx::ShaderSource {
                glsl_120: Some(textured::FRAGMENT_GLSL_120),
                glsl_150: Some(textured::FRAGMENT_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            factory.link_program_source(
                vertex,
                fragment,
                capabilities
            ).unwrap()
        };

        let buffer_pos = factory.create_buffer(
            POS_COMPONENTS * BUFFER_SIZE,
            gfx::BufferUsage::Dynamic
        );
        let buffer_uv = factory.create_buffer(
            UV_COMPONENTS * BUFFER_SIZE,
            gfx::BufferUsage::Dynamic
        );

        let mut mesh = gfx::Mesh::new(BUFFER_SIZE as u32);
        mesh.attributes.extend(
            PositionFormat::generate(
                buffer_pos.raw().clone()
            )
        );

        // Reuse parameters from `mesh`.
        let mut mesh_uv = mesh.clone();
        mesh_uv.attributes.extend(
            TexCoordsFormat::generate(
                buffer_uv.raw().clone()
            )
        );

        let params = Params {
            color: [1.0; 4],
            _dummy: PhantomData,
        };
        let mut batch = gfx::batch::OwnedBatch::new(
            mesh,
            program,
            params
        ).unwrap();

        let sampler = factory.create_sampler(
            gfx::tex::SamplerInfo::new(
                gfx::tex::FilterMethod::Trilinear,
                gfx::tex::WrapMode::Clamp
            )
        );

        let tex_handle = Texture::empty(factory).unwrap().handle();

        let params_uv = ParamsUV {
            color: [1.0; 4],
            s_texture: (tex_handle, Some(sampler))
        };
        let mut batch_uv = gfx::batch::OwnedBatch::new(
            mesh_uv,
            program_uv,
            params_uv
        ).unwrap();

        // Disable culling.
        batch.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullFace::Nothing);
        batch_uv.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullFace::Nothing);

        Gfx2d {
            buffer_pos: buffer_pos,
            buffer_uv: buffer_uv,
            batch: batch,
            batch_uv: batch_uv,
        }
    }

    /// Renders graphics to a Gfx renderer.
    pub fn draw<C, O, F>(
        &mut self,
        renderer: &mut gfx::Renderer<R, C>,
        output: &O,
        viewport: Viewport,
        mut f: F
    )
        where C: gfx::CommandBuffer<R>,
              O: gfx::Output<R>,
              F: FnMut(Context, &mut GfxGraphics<R, C, O>)
    {
        let ref mut g = GfxGraphics::new(
            renderer,
            output,
            self
        );
        let c = Context::new_viewport(viewport);
        f(c, g);
    }
}

/// Used for rendering 2D graphics.
pub struct GfxGraphics<'a, R, C, O>
    where R: gfx::Resources + 'a,
          C: gfx::CommandBuffer<R> + 'a,
          O: gfx::Output<R> + 'a,
          R::Buffer: 'a,
          R::ArrayBuffer: 'a,
          R::Shader: 'a,
          R::Program: 'a,
          R::FrameBuffer: 'a,
          R::Surface: 'a,
          R::Texture: 'a,
          R::Sampler: 'a
{
    renderer: &'a mut gfx::Renderer<R, C>,
    output: &'a O,
    g2d: &'a mut Gfx2d<R>,
}

impl<'a, R, C, O> GfxGraphics<'a, R, C, O>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>,
          O: gfx::Output<R>
{
    /// Creates a new object for rendering 2D graphics.
    pub fn new(renderer: &'a mut gfx::Renderer<R, C>,
               output: &'a O,
               g2d: &'a mut Gfx2d<R>) -> Self {
        GfxGraphics {
            renderer: renderer,
            output: output,
            g2d: g2d,
        }
    }

    /// Returns true if texture has alpha channel.
    pub fn has_texture_alpha(&self, texture: &Texture<R>) -> bool {
        use gfx::tex::Components::RGBA;

        texture.handle().get_info().format.get_components() == Some(RGBA)
    }
}

impl<'a, R, C, O> Graphics for GfxGraphics<'a, R, C, O>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>,
          O: gfx::Output<R>,
          R::Buffer: 'a,
          R::ArrayBuffer: 'a,
          R::Shader: 'a,
          R::Program: 'a,
          R::FrameBuffer: 'a,
          R::Surface: 'a,
          R::Texture: 'a,
          R::Sampler: 'a
{
    type Texture = Texture<R>;

    fn clear_color(&mut self, color: [f32; 4]) {
        let &mut GfxGraphics {
            ref mut renderer,
            output,
            ..
        } = self;
        renderer.clear(
            gfx::ClearData {
                color: color,
                depth: 0.0,
                stencil: 0,
            },
            gfx::COLOR,
            output
        );
    }

    fn clear_stencil(&mut self, value: u8) {
        let &mut GfxGraphics {
            ref mut renderer,
            output,
            ..
        } = self;
        renderer.clear(
            gfx::ClearData {
                color: [0.0; 4],
                depth: 0.0,
                stencil: value,
            },
            gfx::STENCIL,
            output
        );
    }

    fn tri_list<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref output,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut batch,
                ..
            },
        } = self;

        batch.state = *draw_state;
        batch.param.color = *color;

        f(&mut |vertices: &[f32]| {
            renderer.update_buffer_vec(&buffer_pos, vertices, 0);

            let n = vertices.len() / POS_COMPONENTS;
            batch.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
            };
            let _ = renderer.draw(batch, *output);
        })
    }

    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref output,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_uv,
                ref mut batch_uv,
                ..
            },
        } = self;

        batch_uv.state = *draw_state;
        batch_uv.param.s_texture.0 = texture.handle();
        batch_uv.param.color = *color;

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            assert_eq!(
                vertices.len() * UV_COMPONENTS,
                texture_coords.len() * POS_COMPONENTS
            );
            renderer.update_buffer_vec(&buffer_pos, vertices, 0);
            renderer.update_buffer_vec(&buffer_uv, texture_coords, 0);

            let n = vertices.len() / POS_COMPONENTS;
            batch_uv.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
            };
            let _ = renderer.draw(batch_uv, *output);
        })
    }
}
<commit_msg>Fixed the handle usage<commit_after>use std::marker::PhantomData;
use graphics::{ Context, DrawState, Graphics, Viewport };
use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;
use { gfx, Texture };

const POS_COMPONENTS: usize = 2;
const UV_COMPONENTS: usize = 2;

// Boiler plate for automatic attribute construction.
// Needs to be improved on gfx-rs side.
// For some reason, using ``*_COMPONENT` triggers some macros errors.

#[vertex_format]
struct PositionFormat { pos: [f32; 2] }

#[vertex_format]
struct ColorFormat { color: [f32; 4] }

#[vertex_format]
struct TexCoordsFormat { uv: [f32; 2] }

#[shader_param]
struct Params<R: gfx::Resources> {
    color: [f32; 4],
    _dummy: PhantomData<R>,
}

#[shader_param]
struct ParamsUV<R: gfx::Resources> {
    color: [f32; 4],
    s_texture: gfx::shade::TextureParam<R>,
}

/// The data used for drawing 2D graphics.
pub struct Gfx2d<R: gfx::Resources> {
    buffer_pos: gfx::handle::Buffer<R, f32>,
    buffer_uv: gfx::handle::Buffer<R, f32>,
    batch: gfx::batch::OwnedBatch<Params<R>>,
    batch_uv: gfx::batch::OwnedBatch<ParamsUV<R>>,
}

impl<R: gfx::Resources> Gfx2d<R> {
    /// Creates a new G2D object.
    pub fn new<D, F>(device: &mut D, factory: &mut F) -> Self
        where D: gfx::Device,
              F: gfx::Factory<R>
    {
        use gfx::traits::*;
        use gfx::VertexFormat;
        use shaders::{ colored, textured };

        let ref capabilities = device.get_capabilities();

        let program = {
            let vertex = gfx::ShaderSource {
                glsl_120: Some(colored::VERTEX_GLSL_120),
                glsl_150: Some(colored::VERTEX_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            let fragment = gfx::ShaderSource {
                glsl_120: Some(colored::FRAGMENT_GLSL_120),
                glsl_150: Some(colored::FRAGMENT_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            factory.link_program_source(
                vertex,
                fragment,
                capabilities
            ).unwrap()
        };

        let program_uv = {
            let vertex = gfx::ShaderSource {
                glsl_120: Some(textured::VERTEX_GLSL_120),
                glsl_150: Some(textured::VERTEX_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            let fragment = gfx::ShaderSource {
                glsl_120: Some(textured::FRAGMENT_GLSL_120),
                glsl_150: Some(textured::FRAGMENT_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            factory.link_program_source(
                vertex,
                fragment,
                capabilities
            ).unwrap()
        };

        let buffer_pos = factory.create_buffer(
            POS_COMPONENTS * BUFFER_SIZE,
            gfx::BufferUsage::Dynamic
        );
        let buffer_uv = factory.create_buffer(
            UV_COMPONENTS * BUFFER_SIZE,
            gfx::BufferUsage::Dynamic
        );

        let mut mesh = gfx::Mesh::new(BUFFER_SIZE as u32);
        mesh.attributes.extend(
            PositionFormat::generate(
                buffer_pos.raw().clone()
            )
        );

        // Reuse parameters from `mesh`.
        let mut mesh_uv = mesh.clone();
        mesh_uv.attributes.extend(
            TexCoordsFormat::generate(
                buffer_uv.raw().clone()
            )
        );

        let params = Params {
            color: [1.0; 4],
            _dummy: PhantomData,
        };
        let mut batch = gfx::batch::OwnedBatch::new(
            mesh,
            program,
            params
        ).unwrap();

        let sampler = factory.create_sampler(
            gfx::tex::SamplerInfo::new(
                gfx::tex::FilterMethod::Trilinear,
                gfx::tex::WrapMode::Clamp
            )
        );

        let tex_handle = Texture::empty(factory).unwrap().handle();

        let params_uv = ParamsUV {
            color: [1.0; 4],
            s_texture: (tex_handle, Some(sampler))
        };
        let mut batch_uv = gfx::batch::OwnedBatch::new(
            mesh_uv,
            program_uv,
            params_uv
        ).unwrap();

        // Disable culling.
        batch.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullFace::Nothing);
        batch_uv.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullFace::Nothing);

        Gfx2d {
            buffer_pos: buffer_pos,
            buffer_uv: buffer_uv,
            batch: batch,
            batch_uv: batch_uv,
        }
    }

    /// Renders graphics to a Gfx renderer.
    pub fn draw<C, O, F>(
        &mut self,
        renderer: &mut gfx::Renderer<R, C>,
        output: &O,
        viewport: Viewport,
        mut f: F
    )
        where C: gfx::CommandBuffer<R>,
              O: gfx::Output<R>,
              F: FnMut(Context, &mut GfxGraphics<R, C, O>)
    {
        let ref mut g = GfxGraphics::new(
            renderer,
            output,
            self
        );
        let c = Context::new_viewport(viewport);
        f(c, g);
    }
}

/// Used for rendering 2D graphics.
pub struct GfxGraphics<'a, R, C, O>
    where R: gfx::Resources + 'a,
          C: gfx::CommandBuffer<R> + 'a,
          O: gfx::Output<R> + 'a,
          R::Buffer: 'a,
          R::ArrayBuffer: 'a,
          R::Shader: 'a,
          R::Program: 'a,
          R::FrameBuffer: 'a,
          R::Surface: 'a,
          R::Texture: 'a,
          R::Sampler: 'a
{
    renderer: &'a mut gfx::Renderer<R, C>,
    output: &'a O,
    g2d: &'a mut Gfx2d<R>,
}

impl<'a, R, C, O> GfxGraphics<'a, R, C, O>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>,
          O: gfx::Output<R>
{
    /// Creates a new object for rendering 2D graphics.
    pub fn new(renderer: &'a mut gfx::Renderer<R, C>,
               output: &'a O,
               g2d: &'a mut Gfx2d<R>) -> Self {
        GfxGraphics {
            renderer: renderer,
            output: output,
            g2d: g2d,
        }
    }

    /// Returns true if texture has alpha channel.
    pub fn has_texture_alpha(&self, texture: &Texture<R>) -> bool {
        use gfx::tex::Components::RGBA;

        texture.handle().get_info().format.get_components() == Some(RGBA)
    }
}

impl<'a, R, C, O> Graphics for GfxGraphics<'a, R, C, O>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>,
          O: gfx::Output<R>,
          R::Buffer: 'a,
          R::ArrayBuffer: 'a,
          R::Shader: 'a,
          R::Program: 'a,
          R::FrameBuffer: 'a,
          R::Surface: 'a,
          R::Texture: 'a,
          R::Sampler: 'a
{
    type Texture = Texture<R>;

    fn clear_color(&mut self, color: [f32; 4]) {
        let &mut GfxGraphics {
            ref mut renderer,
            output,
            ..
        } = self;
        renderer.clear(
            gfx::ClearData {
                color: color,
                depth: 0.0,
                stencil: 0,
            },
            gfx::COLOR,
            output
        );
    }

    fn clear_stencil(&mut self, value: u8) {
        let &mut GfxGraphics {
            ref mut renderer,
            output,
            ..
        } = self;
        renderer.clear(
            gfx::ClearData {
                color: [0.0; 4],
                depth: 0.0,
                stencil: value,
            },
            gfx::STENCIL,
            output
        );
    }

    fn tri_list<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref output,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut batch,
                ..
            },
        } = self;

        batch.state = *draw_state;
        batch.param.color = *color;

        f(&mut |vertices: &[f32]| {
            renderer.update_buffer_vec(&buffer_pos, vertices, 0);

            let n = vertices.len() / POS_COMPONENTS;
            batch.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
            };
            let _ = renderer.draw(batch, *output);
        })
    }

    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref output,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_uv,
                ref mut batch_uv,
                ..
            },
        } = self;

        batch_uv.state = *draw_state;
        batch_uv.param.s_texture.0 = texture.handle();
        batch_uv.param.color = *color;

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            assert_eq!(
                vertices.len() * UV_COMPONENTS,
                texture_coords.len() * POS_COMPONENTS
            );
            renderer.update_buffer_vec(&buffer_pos, vertices, 0);
            renderer.update_buffer_vec(&buffer_uv, texture_coords, 0);

            let n = vertices.len() / POS_COMPONENTS;
            batch_uv.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
            };
            let _ = renderer.draw(batch_uv, *output);
        })
    }
}
<|endoftext|>