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

use std::net::SocketAddr;
use std::os::unix::io::RawFd;

use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use tokio::net::{TcpListener, TcpStream};
use tokio_stream::StreamExt;
use tracing::{error, info, warn};

use crate::config::Config;

use crate::identity::CertificateProvider;
use crate::proxy::inbound::InboundConnect::Hbone;
use crate::tls::TlsError;
use crate::workload::WorkloadInformation;

use super::Error;

pub struct Inbound {
    cfg: Config,
    listener: TcpListener,
    cert_manager: Box<dyn CertificateProvider>,
    workloads: WorkloadInformation,
}

impl Inbound {
    pub async fn new(
        cfg: Config,
        workloads: WorkloadInformation,
        cert_manager: Box<dyn CertificateProvider>,
    ) -> Result<Inbound, Error> {
        let listener: TcpListener = TcpListener::bind(cfg.inbound_addr)
            .await
            .map_err(|e| Error::Bind(cfg.inbound_addr, e))?;
        match crate::socket::set_transparent(&listener) {
            Err(_e) => info!("running without transparent mode"),
            _ => info!("running with transparent mode"),
        };
        Ok(Inbound {
            cfg,
            workloads,
            listener,
            cert_manager,
        })
    }

    pub(super) fn address(&self) -> SocketAddr {
        self.listener.local_addr().unwrap()
    }

    pub(super) async fn run(self) {
        let addr = self.listener.local_addr().unwrap();
        if self.cfg.tls {
            // TODO avoid duplication here
            let service = make_service_fn(|_| async {
                Ok::<_, hyper::Error>(service_fn(Self::serve_connect))
            });
            let boring_acceptor = crate::tls::BoringTlsAcceptor {
                acceptor: InboundCertProvider {
                    workloads: self.workloads.clone(),
                    cert_manager: self.cert_manager.clone(),
                },
            };
            let mut listener = hyper::server::conn::AddrIncoming::from_listener(self.listener)
                .expect("hbone bind");
            listener.set_nodelay(true);
            let incoming = hyper::server::accept::from_stream(
                tls_listener::builder(boring_acceptor)
                    .listen(listener)
                    .filter(|conn| {
                        // Avoid 'By default, if a client fails the TLS handshake, that is treated as an error, and the TlsListener will return an Err'
                        if let Err(err) = conn {
                            warn!("TLS handshake error: {}", err);
                            false
                        } else {
                            info!("TLS handshake succeeded");
                            true
                        }
                    }),
            );

            let server = Server::builder(incoming)
                .http2_only(true)
                .http2_initial_stream_window_size(self.cfg.window_size)
                .http2_initial_connection_window_size(self.cfg.connection_window_size)
                .http2_max_frame_size(self.cfg.frame_size)
                .serve(service);

            info!("HBONE listener established {}", addr);

            if let Err(e) = server.await {
                error!("server error: {}", e);
            }
        } else {
            warn!("TLS disabled");
            let service = make_service_fn(|_| async {
                Ok::<_, hyper::Error>(service_fn(Self::serve_connect))
            });

            let server = hyper::server::conn::AddrIncoming::from_listener(self.listener)
                .map(Server::builder)
                .expect("hbone bind")
                .http2_only(true)
                .http2_initial_stream_window_size(self.cfg.window_size)
                .http2_initial_connection_window_size(self.cfg.connection_window_size)
                .http2_max_frame_size(self.cfg.frame_size)
                .serve(service);

            info!("HBONE listener established {}", addr);

            if let Err(e) = server.await {
                error!("server error: {}", e);
            }
        }
    }

    /// handle_inbound serves an inbound connection with a target address `addr`.
    pub(super) async fn handle_inbound(
        request_type: InboundConnect,
        addr: SocketAddr,
    ) -> Result<(), std::io::Error> {
        let stream = tokio::net::TcpStream::connect(addr).await;
        match stream {
            Err(err) => {
                warn!("connect to {} failed: {}", addr, err);
                Err(err)
            }
            Ok(stream) => {
                let mut stream = stream;
                tokio::task::spawn(async move {
                    match request_type {
                        InboundConnect::DirectPath(mut incoming) => {
                            tokio::io::copy_bidirectional(&mut incoming, &mut stream)
                                .await
                                .expect("internal server copy");
                        }
                        Hbone(req) => match hyper::upgrade::on(req).await {
                            Ok(mut upgraded) => {
                                super::copy_hbone("hbone server", &mut upgraded, &mut stream)
                                    .await
                                    .expect("hbone server copy");
                            }
                            Err(e) => {
                                // Not sure if this can even happen
                                error!("No upgrade {e}");
                            }
                        },
                    }
                });
                // Send back our 200. We do this regardless of if our spawned task copies the data;
                // we need to respond with headers immediately once connection is established for the
                // stream of bytes to begin.
                Ok(())
            }
        }
    }

    async fn serve_connect(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
        let mut res = Response::new(Body::empty());
        match req.method() {
            &Method::CONNECT => {
                let uri = req.uri();
                info!("Got {} request to {}", req.method(), uri);
                let addr: SocketAddr = uri.to_string().as_str().parse().expect("must be an addr");
                *res.status_mut() =
                    match Self::handle_inbound(InboundConnect::Hbone(req), addr).await {
                        Ok(_) => StatusCode::OK,
                        Err(_) => StatusCode::SERVICE_UNAVAILABLE,
                    };
                Ok(res)
            }
            // Return the 404 Not Found for other routes.
            method => {
                info!("Sending 404, got {method}");
                let mut not_found = Response::default();
                *not_found.status_mut() = StatusCode::NOT_FOUND;
                Ok(not_found)
            }
        }
    }
}

pub(super) enum InboundConnect {
    /// DirectPath is an optimization when we are connecting to an endpoint on the same node.
    /// Rather than doing a full HBONE connection over the localhost network, we just pass the outbound
    /// context directly to the inbound handling in memory.
    DirectPath(TcpStream),
    /// Hbone is a standard HBONE request coming from the network.
    Hbone(Request<Body>),
}

#[derive(Clone)]
struct InboundCertProvider {
    cert_manager: Box<dyn CertificateProvider>,
    workloads: WorkloadInformation,
}

#[async_trait::async_trait]
impl crate::tls::CertProvider for InboundCertProvider {
    async fn fetch_cert(&mut self, fd: RawFd) -> Result<boring::ssl::SslAcceptor, TlsError> {
        let orig = crate::socket::orig_dst_addr_fd(fd).map_err(TlsError::DestinationLookup)?;
        let identity = {
            let remote_addr = super::to_canonical_ip(orig);
            self.workloads
                .find_workload(&remote_addr)
                .ok_or(TlsError::CertificateLookup(remote_addr))?
                .identity()
        };
        info!("tls: accepting connection to {:?} ({})", orig, identity);
        let cert = self.cert_manager.fetch_certificate(&identity).await?;
        let acc = cert.acceptor()?;
        Ok(acc)
    }
}
<commit_msg>Prevent panic when uri is invalid (#162)<commit_after>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::net::SocketAddr;
use std::os::unix::io::RawFd;

use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use tokio::net::{TcpListener, TcpStream};
use tokio_stream::StreamExt;
use tracing::{error, info, warn};

use crate::config::Config;

use crate::identity::CertificateProvider;
use crate::proxy::inbound::InboundConnect::Hbone;
use crate::tls::TlsError;
use crate::workload::WorkloadInformation;

use super::Error;

pub struct Inbound {
    cfg: Config,
    listener: TcpListener,
    cert_manager: Box<dyn CertificateProvider>,
    workloads: WorkloadInformation,
}

impl Inbound {
    pub async fn new(
        cfg: Config,
        workloads: WorkloadInformation,
        cert_manager: Box<dyn CertificateProvider>,
    ) -> Result<Inbound, Error> {
        let listener: TcpListener = TcpListener::bind(cfg.inbound_addr)
            .await
            .map_err(|e| Error::Bind(cfg.inbound_addr, e))?;
        match crate::socket::set_transparent(&listener) {
            Err(_e) => info!("running without transparent mode"),
            _ => info!("running with transparent mode"),
        };
        Ok(Inbound {
            cfg,
            workloads,
            listener,
            cert_manager,
        })
    }

    pub(super) fn address(&self) -> SocketAddr {
        self.listener.local_addr().unwrap()
    }

    pub(super) async fn run(self) {
        let addr = self.listener.local_addr().unwrap();
        if self.cfg.tls {
            // TODO avoid duplication here
            let service = make_service_fn(|_| async {
                Ok::<_, hyper::Error>(service_fn(Self::serve_connect))
            });
            let boring_acceptor = crate::tls::BoringTlsAcceptor {
                acceptor: InboundCertProvider {
                    workloads: self.workloads.clone(),
                    cert_manager: self.cert_manager.clone(),
                },
            };
            let mut listener = hyper::server::conn::AddrIncoming::from_listener(self.listener)
                .expect("hbone bind");
            listener.set_nodelay(true);
            let incoming = hyper::server::accept::from_stream(
                tls_listener::builder(boring_acceptor)
                    .listen(listener)
                    .filter(|conn| {
                        // Avoid 'By default, if a client fails the TLS handshake, that is treated as an error, and the TlsListener will return an Err'
                        if let Err(err) = conn {
                            warn!("TLS handshake error: {}", err);
                            false
                        } else {
                            info!("TLS handshake succeeded");
                            true
                        }
                    }),
            );

            let server = Server::builder(incoming)
                .http2_only(true)
                .http2_initial_stream_window_size(self.cfg.window_size)
                .http2_initial_connection_window_size(self.cfg.connection_window_size)
                .http2_max_frame_size(self.cfg.frame_size)
                .serve(service);

            info!("HBONE listener established {}", addr);

            if let Err(e) = server.await {
                error!("server error: {}", e);
            }
        } else {
            warn!("TLS disabled");
            let service = make_service_fn(|_| async {
                Ok::<_, hyper::Error>(service_fn(Self::serve_connect))
            });

            let server = hyper::server::conn::AddrIncoming::from_listener(self.listener)
                .map(Server::builder)
                .expect("hbone bind")
                .http2_only(true)
                .http2_initial_stream_window_size(self.cfg.window_size)
                .http2_initial_connection_window_size(self.cfg.connection_window_size)
                .http2_max_frame_size(self.cfg.frame_size)
                .serve(service);

            info!("HBONE listener established {}", addr);

            if let Err(e) = server.await {
                error!("server error: {}", e);
            }
        }
    }

    /// handle_inbound serves an inbound connection with a target address `addr`.
    pub(super) async fn handle_inbound(
        request_type: InboundConnect,
        addr: SocketAddr,
    ) -> Result<(), std::io::Error> {
        let stream = tokio::net::TcpStream::connect(addr).await;
        match stream {
            Err(err) => {
                warn!("connect to {} failed: {}", addr, err);
                Err(err)
            }
            Ok(stream) => {
                let mut stream = stream;
                tokio::task::spawn(async move {
                    match request_type {
                        InboundConnect::DirectPath(mut incoming) => {
                            tokio::io::copy_bidirectional(&mut incoming, &mut stream)
                                .await
                                .expect("internal server copy");
                        }
                        Hbone(req) => match hyper::upgrade::on(req).await {
                            Ok(mut upgraded) => {
                                super::copy_hbone("hbone server", &mut upgraded, &mut stream)
                                    .await
                                    .expect("hbone server copy");
                            }
                            Err(e) => {
                                // Not sure if this can even happen
                                error!("No upgrade {e}");
                            }
                        },
                    }
                });
                // Send back our 200. We do this regardless of if our spawned task copies the data;
                // we need to respond with headers immediately once connection is established for the
                // stream of bytes to begin.
                Ok(())
            }
        }
    }

    async fn serve_connect(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
        let mut res = Response::new(Body::empty());
        match req.method() {
            &Method::CONNECT => {
                let uri = req.uri();
                info!("Got {} request to {}", req.method(), uri);
                let addr: Result<SocketAddr, _> = uri.to_string().as_str().parse();
                if addr.is_err() {
                    info!("Sending 400, {:?}", addr.err());
                    let mut bad_request = Response::default();
                    *bad_request.status_mut() = StatusCode::BAD_REQUEST;
                    return Ok(bad_request);
                }

                let addr: SocketAddr = addr.unwrap();
                *res.status_mut() =
                    match Self::handle_inbound(InboundConnect::Hbone(req), addr).await {
                        Ok(_) => StatusCode::OK,
                        Err(_) => StatusCode::SERVICE_UNAVAILABLE,
                    };
                Ok(res)
            }
            // Return the 404 Not Found for other routes.
            method => {
                info!("Sending 404, got {method}");
                let mut not_found = Response::default();
                *not_found.status_mut() = StatusCode::NOT_FOUND;
                Ok(not_found)
            }
        }
    }
}

pub(super) enum InboundConnect {
    /// DirectPath is an optimization when we are connecting to an endpoint on the same node.
    /// Rather than doing a full HBONE connection over the localhost network, we just pass the outbound
    /// context directly to the inbound handling in memory.
    DirectPath(TcpStream),
    /// Hbone is a standard HBONE request coming from the network.
    Hbone(Request<Body>),
}

#[derive(Clone)]
struct InboundCertProvider {
    cert_manager: Box<dyn CertificateProvider>,
    workloads: WorkloadInformation,
}

#[async_trait::async_trait]
impl crate::tls::CertProvider for InboundCertProvider {
    async fn fetch_cert(&mut self, fd: RawFd) -> Result<boring::ssl::SslAcceptor, TlsError> {
        let orig = crate::socket::orig_dst_addr_fd(fd).map_err(TlsError::DestinationLookup)?;
        let identity = {
            let remote_addr = super::to_canonical_ip(orig);
            self.workloads
                .find_workload(&remote_addr)
                .ok_or(TlsError::CertificateLookup(remote_addr))?
                .identity()
        };
        info!("tls: accepting connection to {:?} ({})", orig, identity);
        let cert = self.cert_manager.fetch_certificate(&identity).await?;
        let acc = cert.acceptor()?;
        Ok(acc)
    }
}
<|endoftext|>


<commit_before>use std;
import std::option;

fn f<@T>(&o: mutable option::t<T>) {
    assert o == option::none;
}

fn main() {
    f::<int>(option::none);
}<commit_msg>Remove deprecated mutable type constructor from run-pass/issue-511<commit_after>use std;
import std::option;

fn f<@T>(&o: option::t<T>) {
    assert o == option::none;
}

fn main() {
    f::<int>(option::none);
}<|endoftext|>


<commit_before>//xfail-stage1
//xfail-stage2
//xfail-stage3

fn main() {

    obj a() {
        fn foo() -> int { ret 2; }
    }

    let my_a = a();

    let my_b = obj () { with my_a };

    assert (my_b.foo() == 2);

    let my_c = obj () { with my_b };

    assert (my_c.foo() == 2);
}

<commit_msg>The self-stack stuff (from #702) also closes issue #718.<commit_after>fn main() {

    obj a() {
        fn foo() -> int { ret 2; }
    }

    let my_a = a();

    let my_b = obj () { with my_a };

    assert (my_b.foo() == 2);

    let my_c = obj () { with my_b };

    assert (my_c.foo() == 2);

    // ...One more for good measure.
    let my_d = obj () { with my_b };

    assert (my_d.foo() == 2);
}

<|endoftext|>


<commit_before>extern crate tabwriter;

use std::io::Write;
use std::str::FromStr;
use self::tabwriter::TabWriter;

use super::tree::FSNode;

pub fn print_tree(tree: &FSNode, max_depth: i64, max_dir_entries: i64, size_format: SizeFormat) {
    let mut tw = TabWriter::new(Vec::new());

    print_tree_impl(tree, &mut tw, "", 0, max_depth, max_dir_entries, size_format);

    tw.flush().unwrap();
    let bytes = tw.into_inner().unwrap();
    let tabulated = String::from_utf8_lossy(&bytes);

	print!("{}", tabulated);
}

const SUM: &'static str = "(Σ)";
const BRANCH: &'static str = "├── ";
const LAST_BRANCH: &'static str = "└── ";
const INDENT: &'static str = "    ";
const NESTED_INDENT: &'static str = "│   ";

fn print_tree_impl<T: Write>(node: &FSNode, mut tw: &mut TabWriter<T>, prefix: &str, depth: i64, max_depth: i64, max_dir_entries: i64, size_format: SizeFormat) {
    let sum_suffix = if node.is_dir() {
        SUM
    } else {
        ""
    };

    writeln!(&mut tw,
             "{}\t{}\t{}",
             node.name(),
             size_format.human_readable_byte_size(node.size()),
             sum_suffix,
             )
        .unwrap();

    if max_depth < 0 || max_depth >= depth {
        let mut size_not_shown: u64 = 0;
        for (idx, item) in node.children().enumerate() {
            let last = idx == (node.children().count() - 1);
            let (branch, nested) = if last {
                (LAST_BRANCH, INDENT)
            } else {
                (BRANCH, NESTED_INDENT)
            };

            if max_dir_entries < 0 || (idx as i64) < max_dir_entries {
                write!(&mut tw, "{}{}", prefix, branch).unwrap();

                let nested_prefix = format!("{}{}", prefix, nested);
                print_tree_impl(item, &mut tw, &nested_prefix, depth + 1, max_depth, max_dir_entries, size_format);
            } else {
                size_not_shown += item.size();

                if last {
                    let _ = writeln!(&mut tw, "{}{}...\t{}\t{}", prefix, branch, size_format.human_readable_byte_size(size_not_shown), SUM);
                }
            }

        }
    }
}

#[derive(Copy, Clone)]
pub enum SizeFormat {
    Human,
    Si,
    Raw,
}

impl FromStr for SizeFormat {
    type Err = ();

    fn from_str(s: &str) -> Result<SizeFormat, ()> {
        match s {
            "h" | "human" => Ok(SizeFormat::Human),
            "H" | "si" => Ok(SizeFormat::Si),
            "r" | "raw" => Ok(SizeFormat::Raw),
            _ => Err(()),
        }
    }
}

impl SizeFormat {
    pub const VALUES: &'static [&'static str] = &["h", "human", "H", "si", "r", "raw"];

    pub fn human_readable_byte_size(self, bytes: u64) -> String {
        static HUMAN_PREFIX: &[&str] = &["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
        static SI_PREFIX: &[&str] = &["B", "KB", "MB", "GB", "TB", "PB", "EB"];

        let (power, prefix) = match self {
            SizeFormat::Human => (1024, HUMAN_PREFIX),
            SizeFormat::Si => (1000, SI_PREFIX),
            SizeFormat::Raw => return raw_format(bytes),
        };

        if bytes < power {
            return raw_format(bytes);
        }

        let which = log2(bytes) / 10;
        let decimal = bytes as f64 / (power as f64).powf(which as f64);
        return format!("{:.1}\t{}", decimal, prefix[which as usize]);
    }
}

fn raw_format(bytes: u64) -> String {
    format!("{}\tB", bytes)
}

fn log2(mut x: u64) -> u64 {
    let mut n: u64 = 0;

    while (x >> 1) > 0 {
        x >>= 1;
        n += 1;
    }
    n
}
<commit_msg>Don't allocate for each size<commit_after>extern crate tabwriter;

use std::fmt;
use std::io::Write;
use std::str::FromStr;
use self::tabwriter::TabWriter;

use super::tree::FSNode;

pub fn print_tree(tree: &FSNode, max_depth: i64, max_dir_entries: i64, size_format: SizeFormat) {
    let mut tw = TabWriter::new(Vec::new());

    print_tree_impl(tree, &mut tw, "", 0, max_depth, max_dir_entries, size_format);

    tw.flush().unwrap();
    let bytes = tw.into_inner().unwrap();
    let tabulated = String::from_utf8_lossy(&bytes);

	print!("{}", tabulated);
}

const SUM: &'static str = "(Σ)";
const BRANCH: &'static str = "├── ";
const LAST_BRANCH: &'static str = "└── ";
const INDENT: &'static str = "    ";
const NESTED_INDENT: &'static str = "│   ";

fn print_tree_impl<T: Write>(node: &FSNode, mut tw: &mut TabWriter<T>, prefix: &str, depth: i64, max_depth: i64, max_dir_entries: i64, size_format: SizeFormat) {
    let sum_suffix = if node.is_dir() {
        SUM
    } else {
        ""
    };

    writeln!(&mut tw,
             "{}\t{}\t{}",
             node.name(),
             size_format.human_readable_byte_size(node.size()),
             sum_suffix,
             )
        .unwrap();

    if max_depth < 0 || max_depth >= depth {
        let mut size_not_shown: u64 = 0;
        for (idx, item) in node.children().enumerate() {
            let last = idx == (node.children().count() - 1);
            let (branch, nested) = if last {
                (LAST_BRANCH, INDENT)
            } else {
                (BRANCH, NESTED_INDENT)
            };

            if max_dir_entries < 0 || (idx as i64) < max_dir_entries {
                write!(&mut tw, "{}{}", prefix, branch).unwrap();

                let nested_prefix = format!("{}{}", prefix, nested);
                print_tree_impl(item, &mut tw, &nested_prefix, depth + 1, max_depth, max_dir_entries, size_format);
            } else {
                size_not_shown += item.size();

                if last {
                    let _ = writeln!(&mut tw, "{}{}...\t{}\t{}", prefix, branch, size_format.human_readable_byte_size(size_not_shown), SUM);
                }
            }

        }
    }
}

#[derive(Copy, Clone)]
pub enum SizeFormat {
    Human,
    Si,
    Raw,
}

impl FromStr for SizeFormat {
    type Err = ();

    fn from_str(s: &str) -> Result<SizeFormat, ()> {
        match s {
            "h" | "human" => Ok(SizeFormat::Human),
            "H" | "si" => Ok(SizeFormat::Si),
            "r" | "raw" => Ok(SizeFormat::Raw),
            _ => Err(()),
        }
    }
}

impl SizeFormat {
    pub const VALUES: &'static [&'static str] = &["h", "human", "H", "si", "r", "raw"];

    pub fn human_readable_byte_size(self, bytes: u64) -> SizeFormatter {
        SizeFormatter(self, bytes)
    }
}

pub struct SizeFormatter(SizeFormat, u64);

impl fmt::Display for SizeFormatter {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        static HUMAN_PREFIX: &[&str] = &["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
        static SI_PREFIX: &[&str] = &["B", "KB", "MB", "GB", "TB", "PB", "EB"];

        let (power, prefix) = match self.0 {
            SizeFormat::Human => (1024, HUMAN_PREFIX),
            SizeFormat::Si => (1000, SI_PREFIX),
            SizeFormat::Raw => return self.fmt_raw(f),
        };

        if self.1 < power {
            return self.fmt_raw(f);
        }

        let which = log2(self.1) / 10;
        let decimal = self.1 as f64 / (power as f64).powf(which as f64);
        write!(f, "{:.1}\t{}", decimal, prefix[which as usize])
    }
}

impl SizeFormatter {
    fn fmt_raw(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}\tB", self.1)
    }
}

fn log2(mut x: u64) -> u64 {
    let mut n: u64 = 0;

    while (x >> 1) > 0 {
        x >>= 1;
        n += 1;
    }
    n
}
<|endoftext|>


<commit_before>use std::cell::RefCell;

use cairo::{self, ImageSurface};

use attributes::Attribute;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::parse;
use property_bag::PropertyBag;
use srgb::{linearize_surface, unlinearize_surface};

use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
use super::input::Input;
use super::{make_result, Filter, FilterError, Primitive};

/// The `feMerge` filter primitive.
pub struct Merge {
    base: Primitive,
}

/// The `<feMergeNode>` element.
pub struct MergeNode {
    in_: RefCell<Option<Input>>,
}

impl Merge {
    /// Constructs a new `Merge` with empty properties.
    #[inline]
    pub fn new() -> Merge {
        Merge {
            base: Primitive::new::<Self>(),
        }
    }
}

impl MergeNode {
    /// Constructs a new `MergeNode` with empty properties.
    #[inline]
    pub fn new() -> MergeNode {
        MergeNode {
            in_: RefCell::new(None),
        }
    }
}

impl NodeTrait for Merge {
    #[inline]
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)
    }

    #[inline]
    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        self.base.get_c_impl()
    }
}

impl NodeTrait for MergeNode {
    #[inline]
    fn set_atts(
        &self,
        _node: &RsvgNode,
        _handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::In => {
                    self.in_.replace(Some(parse("in", value, (), None)?));
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl MergeNode {
    fn render(
        &self,
        ctx: &FilterContext,
        bounds: IRect,
        output_surface: Option<ImageSurface>,
    ) -> Result<ImageSurface, FilterError> {
        let input = make_result(ctx.get_input(self.in_.borrow().as_ref()))?;
        let input_surface = input.surface();
        let input_surface =
            linearize_surface(&input_surface, bounds).map_err(FilterError::BadInputSurfaceStatus)?;

        if output_surface.is_none() {
            return Ok(input_surface);
        }
        let output_surface = output_surface.unwrap();

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            bounds.x0 as f64,
            bounds.y0 as f64,
            (bounds.x1 - bounds.x0) as f64,
            (bounds.y1 - bounds.y0) as f64,
        );
        cr.clip();
        cr.set_source_surface(&input_surface, 0f64, 0f64);
        cr.set_operator(cairo::Operator::Over);
        cr.paint();

        Ok(output_surface)
    }
}

impl Filter for Merge {
    fn render(&self, node: &RsvgNode, ctx: &FilterContext) -> Result<FilterResult, FilterError> {
        let mut bounds = self.base.get_bounds(ctx);
        for child in node
            .children()
            .filter(|c| c.get_type() == NodeType::FilterPrimitiveMergeNode)
        {
            bounds = bounds.add_input(&child.with_impl(move |c: &MergeNode| {
                make_result(ctx.get_input(c.in_.borrow().as_ref()))
            })?);
        }
        let bounds = bounds.into_irect();

        let mut output_surface = None;
        for child in node
            .children()
            .filter(|c| c.get_type() == NodeType::FilterPrimitiveMergeNode)
        {
            output_surface =
                Some(child.with_impl(move |c: &MergeNode| c.render(ctx, bounds, output_surface))?);
        }

        let output_surface = output_surface
            .map(|surface| unlinearize_surface(&surface, bounds))
            .unwrap_or_else(|| {
                ImageSurface::create(
                    cairo::Format::ARgb32,
                    ctx.source_graphic().get_width(),
                    ctx.source_graphic().get_height(),
                )
            })
            .map_err(FilterError::OutputSurfaceCreation)?;

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }
}
<commit_msg>Add comments into feMerge<commit_after>use std::cell::RefCell;

use cairo::{self, ImageSurface};

use attributes::Attribute;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::parse;
use property_bag::PropertyBag;
use srgb::{linearize_surface, unlinearize_surface};

use super::context::{FilterContext, FilterOutput, FilterResult, IRect};
use super::input::Input;
use super::{make_result, Filter, FilterError, Primitive};

/// The `feMerge` filter primitive.
pub struct Merge {
    base: Primitive,
}

/// The `<feMergeNode>` element.
pub struct MergeNode {
    in_: RefCell<Option<Input>>,
}

impl Merge {
    /// Constructs a new `Merge` with empty properties.
    #[inline]
    pub fn new() -> Merge {
        Merge {
            base: Primitive::new::<Self>(),
        }
    }
}

impl MergeNode {
    /// Constructs a new `MergeNode` with empty properties.
    #[inline]
    pub fn new() -> MergeNode {
        MergeNode {
            in_: RefCell::new(None),
        }
    }
}

impl NodeTrait for Merge {
    #[inline]
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)
    }

    #[inline]
    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        self.base.get_c_impl()
    }
}

impl NodeTrait for MergeNode {
    #[inline]
    fn set_atts(
        &self,
        _node: &RsvgNode,
        _handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::In => {
                    self.in_.replace(Some(parse("in", value, (), None)?));
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl MergeNode {
    fn render(
        &self,
        ctx: &FilterContext,
        bounds: IRect,
        output_surface: Option<ImageSurface>,
    ) -> Result<ImageSurface, FilterError> {
        let input = make_result(ctx.get_input(self.in_.borrow().as_ref()))?;
        let input_surface = input.surface();
        let input_surface =
            linearize_surface(&input_surface, bounds).map_err(FilterError::BadInputSurfaceStatus)?;

        if output_surface.is_none() {
            return Ok(input_surface);
        }
        let output_surface = output_surface.unwrap();

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            bounds.x0 as f64,
            bounds.y0 as f64,
            (bounds.x1 - bounds.x0) as f64,
            (bounds.y1 - bounds.y0) as f64,
        );
        cr.clip();
        cr.set_source_surface(&input_surface, 0f64, 0f64);
        cr.set_operator(cairo::Operator::Over);
        cr.paint();

        Ok(output_surface)
    }
}

impl Filter for Merge {
    fn render(&self, node: &RsvgNode, ctx: &FilterContext) -> Result<FilterResult, FilterError> {
        // Compute the filter bounds, taking each child node's input into account.
        let mut bounds = self.base.get_bounds(ctx);
        for child in node
            .children()
            .filter(|c| c.get_type() == NodeType::FilterPrimitiveMergeNode)
        {
            bounds = bounds.add_input(&child.with_impl(move |c: &MergeNode| {
                make_result(ctx.get_input(c.in_.borrow().as_ref()))
            })?);
        }
        let bounds = bounds.into_irect();

        // Now merge them all.
        let mut output_surface = None;
        for child in node
            .children()
            .filter(|c| c.get_type() == NodeType::FilterPrimitiveMergeNode)
        {
            output_surface =
                Some(child.with_impl(move |c: &MergeNode| c.render(ctx, bounds, output_surface))?);
        }

        let output_surface = output_surface
            .map(|surface| unlinearize_surface(&surface, bounds))
            .unwrap_or_else(|| {
                ImageSurface::create(
                    cairo::Format::ARgb32,
                    ctx.source_graphic().get_width(),
                    ctx.source_graphic().get_height(),
                )
            })
            .map_err(FilterError::OutputSurfaceCreation)?;

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }
}
<|endoftext|>


<commit_before>use parser::Identifier;
use operations::{Operation, Error};
use operations::item_type::{ItemType, FuncArgType};
use operations::scope::{ScopeStack, ScopeItem, TypeId};

pub fn define_u8(scope: &mut ScopeStack, bool_type: TypeId) -> TypeId {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let unit_type = scope.primitives().unit();

    let u8_type = scope.declare_type(
        Identifier::from("u8"),
        ItemType::Primitive(1)
    );
    scope.register_primitive("u8", u8_type);

    scope.declare_builtin_function(
        // Special method for converting from literal
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::convert::From<{unsigned integer}>"),
        ItemType::Function {
            // This takes a single literal of the type specific within
            // the curly braces {} in the name
            args: vec![FuncArgType::Arg(u8_type)],
            // Return type signifies which type we are declaring supports integer literals
            return_type: u8_type,
        },
        move |scope, args, target| {
            let value = args[0].numeric_literal_value();

            // 8 is the size of this numeric type in bytes
            // We use >= here because 0 is reserved for zero so we
            // have (2^size - 1) numbers available
            if value >= (1 << 8) {
                Err(Error::OverflowingLiteral {
                    typ: scope.get_type(u8_type).clone()
                })
            }
            else {
                Ok(vec![
                    Operation::Increment {
                        target: target.position(),
                        amount: value as u8,
                    }
                ])
            }
        }
    );

    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_, args, _| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            Ok(vec![
                // Need to display numbers as numbers by first incrementing them to the proper
                // ascii code and then decrementing them again
                Operation::Increment {
                    target: mem.position(),
                    amount: b'0',
                },
                Operation::Write {
                    target: mem,
                },
                // Need to make sure we reverse the operation after or else there will be
                // unintended consequences
                Operation::Decrement {
                    target: mem.position(),
                    amount: b'0',
                },
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("increment"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            debug_assert!(mem.size() == 1);

            Ok(vec![
                Operation::Increment {
                    target: mem.position(),
                    amount: 1,
                }
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("decrement"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            debug_assert!(mem.size() == 1);

            Ok(vec![
                Operation::Decrement {
                    target: mem.position(),
                    amount: 1,
                }
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("std::cmp::PartialEq::eq"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type), FuncArgType::Arg(u8_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let u8_type = scope.primitives().u8();

            Ok(match (&args[0], &args[1]) {
                (&ScopeItem::TypedBlock {memory: x, ..}, &ScopeItem::TypedBlock {memory: y, ..}) => {
                    let temp_x = scope.allocate(u8_type);
                    let temp_y = scope.allocate(u8_type);

                    vec![Operation::TempAllocate {
                        temp: temp_x,
                        body: vec![Operation::TempAllocate {
                            temp: temp_y,
                            body: vec![
                                Operation::Copy {
                                    source: x.position(),
                                    target: temp_x.position(),
                                    size: x.size(),
                                },
                                Operation::Copy {
                                    source: y.position(),
                                    target: temp_y.position(),
                                    size: y.size(),
                                },

                                // Algorithm:
                                //
                                // z = x == y
                                // x[y-x-]z+y[z-y[-]]
                                Operation::Loop {
                                    cond: temp_x.position(),
                                    body: vec![
                                        Operation::Decrement {
                                            target: temp_y.position(),
                                            amount: 1,
                                        },
                                        Operation::Decrement {
                                            target: temp_x.position(),
                                            amount: 1,
                                        },
                                    ],
                                },
                                Operation::Increment {
                                    target: target.position(),
                                    amount: 1,
                                },
                                Operation::Loop {
                                    cond: temp_y.position(),
                                    body: vec![
                                        Operation::Decrement {
                                            target: target.position(),
                                            amount: 1,
                                        },
                                        Operation::Zero {
                                            target: temp_y,
                                        },
                                    ],
                                },
                            ],
                            should_zero: false,
                        }],
                        should_zero: false,
                    }]
                },
                _ => unreachable!(),
            })
        }
    );

    scope.declare_builtin_function(
        Identifier::from("std::cmp::PartialEq::ne"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type), FuncArgType::Arg(u8_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let u8_type = scope.primitives().bool();

            Ok(match (&args[0], &args[1]) {
                (&ScopeItem::TypedBlock {memory: x, ..}, &ScopeItem::TypedBlock {memory: y, ..}) => {
                    let temp_x = scope.allocate(u8_type);
                    let temp_y = scope.allocate(u8_type);

                    vec![Operation::TempAllocate {
                        temp: temp_x,
                        body: vec![Operation::TempAllocate {
                            temp: temp_y,
                            body: vec![
                                Operation::Copy {
                                    source: x.position(),
                                    target: temp_x.position(),
                                    size: x.size(),
                                },
                                Operation::Copy {
                                    source: y.position(),
                                    target: temp_y.position(),
                                    size: y.size(),
                                },

                                // Algorithm:
                                //
                                // z = x != y
                                // x[y-x-]y[z+y[-]]
                                Operation::Loop {
                                    cond: temp_x.position(),
                                    body: vec![
                                        Operation::Decrement {
                                            target: temp_y.position(),
                                            amount: 1,
                                        },
                                        Operation::Decrement {
                                            target: temp_x.position(),
                                            amount: 1,
                                        },
                                    ],
                                },
                                Operation::Loop {
                                    cond: temp_y.position(),
                                    body: vec![
                                        Operation::Increment {
                                            target: target.position(),
                                            amount: 1,
                                        },
                                        Operation::Zero {
                                            target: temp_y,
                                        },
                                    ],
                                },
                            ],
                            should_zero: false,
                        }],
                        should_zero: false,
                    }]
                },
                _ => unreachable!(),
            })
        }
    );

    // Need this so that this next method definition does not overwrite the previous one
    scope.push_scope();

    // Method on [u8; _]
    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Array {item: u8_type, size: None}],
            return_type: unit_type,
        },
        move |scope, args, _| {
            match args[0] {
                ScopeItem::Array {memory, ..} => Ok(vec![
                    Operation::Write {
                        target: memory,
                    },
                ]),
                ScopeItem::ByteLiteral(ref bytes) => {
                    let u8_type = scope.primitives().u8();
                    let mem = scope.allocate(u8_type);

                    Ok(vec![
                        Operation::TempAllocate {
                            temp: mem,
                            body: bytes.iter().flat_map(|&ch| vec![
                                Operation::Increment {
                                    target: mem.position(),
                                    amount: ch,
                                },
                                Operation::Write {
                                    target: mem,
                                },
                                Operation::Decrement {
                                    target: mem.position(),
                                    amount: ch,
                                },
                            ]).collect(),
                            should_zero: false,
                        },
                    ])
                },
                _ => unreachable!(),
            }
        }
    );

    u8_type
}

#[cfg(test)]
mod tests {
    use super::*;

    use operations::scope::{ScopeType};

    #[test]
    fn defines_primitive() {
        let mut scope = ScopeStack::new();
        define_u8(&mut scope);

        let u8_type_id = match **scope.lookup_type(&Identifier::from("u8")).first().unwrap() {
            ScopeType::Type(id) => id,
        };
        assert_eq!(scope.primitives().u8(), u8_type_id);
    }
}
<commit_msg>Fixed tests<commit_after>use parser::Identifier;
use operations::{Operation, Error};
use operations::item_type::{ItemType, FuncArgType};
use operations::scope::{ScopeStack, ScopeItem, TypeId};

pub fn define_u8(scope: &mut ScopeStack, bool_type: TypeId) -> TypeId {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let unit_type = scope.primitives().unit();

    let u8_type = scope.declare_type(
        Identifier::from("u8"),
        ItemType::Primitive(1)
    );
    scope.register_primitive("u8", u8_type);

    scope.declare_builtin_function(
        // Special method for converting from literal
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::convert::From<{unsigned integer}>"),
        ItemType::Function {
            // This takes a single literal of the type specific within
            // the curly braces {} in the name
            args: vec![FuncArgType::Arg(u8_type)],
            // Return type signifies which type we are declaring supports integer literals
            return_type: u8_type,
        },
        move |scope, args, target| {
            let value = args[0].numeric_literal_value();

            // 8 is the size of this numeric type in bytes
            // We use >= here because 0 is reserved for zero so we
            // have (2^size - 1) numbers available
            if value >= (1 << 8) {
                Err(Error::OverflowingLiteral {
                    typ: scope.get_type(u8_type).clone()
                })
            }
            else {
                Ok(vec![
                    Operation::Increment {
                        target: target.position(),
                        amount: value as u8,
                    }
                ])
            }
        }
    );

    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_, args, _| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            Ok(vec![
                // Need to display numbers as numbers by first incrementing them to the proper
                // ascii code and then decrementing them again
                Operation::Increment {
                    target: mem.position(),
                    amount: b'0',
                },
                Operation::Write {
                    target: mem,
                },
                // Need to make sure we reverse the operation after or else there will be
                // unintended consequences
                Operation::Decrement {
                    target: mem.position(),
                    amount: b'0',
                },
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("increment"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            debug_assert!(mem.size() == 1);

            Ok(vec![
                Operation::Increment {
                    target: mem.position(),
                    amount: 1,
                }
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("decrement"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            debug_assert!(mem.size() == 1);

            Ok(vec![
                Operation::Decrement {
                    target: mem.position(),
                    amount: 1,
                }
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("std::cmp::PartialEq::eq"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type), FuncArgType::Arg(u8_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let u8_type = scope.primitives().u8();

            Ok(match (&args[0], &args[1]) {
                (&ScopeItem::TypedBlock {memory: x, ..}, &ScopeItem::TypedBlock {memory: y, ..}) => {
                    let temp_x = scope.allocate(u8_type);
                    let temp_y = scope.allocate(u8_type);

                    vec![Operation::TempAllocate {
                        temp: temp_x,
                        body: vec![Operation::TempAllocate {
                            temp: temp_y,
                            body: vec![
                                Operation::Copy {
                                    source: x.position(),
                                    target: temp_x.position(),
                                    size: x.size(),
                                },
                                Operation::Copy {
                                    source: y.position(),
                                    target: temp_y.position(),
                                    size: y.size(),
                                },

                                // Algorithm:
                                //
                                // z = x == y
                                // x[y-x-]z+y[z-y[-]]
                                Operation::Loop {
                                    cond: temp_x.position(),
                                    body: vec![
                                        Operation::Decrement {
                                            target: temp_y.position(),
                                            amount: 1,
                                        },
                                        Operation::Decrement {
                                            target: temp_x.position(),
                                            amount: 1,
                                        },
                                    ],
                                },
                                Operation::Increment {
                                    target: target.position(),
                                    amount: 1,
                                },
                                Operation::Loop {
                                    cond: temp_y.position(),
                                    body: vec![
                                        Operation::Decrement {
                                            target: target.position(),
                                            amount: 1,
                                        },
                                        Operation::Zero {
                                            target: temp_y,
                                        },
                                    ],
                                },
                            ],
                            should_zero: false,
                        }],
                        should_zero: false,
                    }]
                },
                _ => unreachable!(),
            })
        }
    );

    scope.declare_builtin_function(
        Identifier::from("std::cmp::PartialEq::ne"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type), FuncArgType::Arg(u8_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let u8_type = scope.primitives().bool();

            Ok(match (&args[0], &args[1]) {
                (&ScopeItem::TypedBlock {memory: x, ..}, &ScopeItem::TypedBlock {memory: y, ..}) => {
                    let temp_x = scope.allocate(u8_type);
                    let temp_y = scope.allocate(u8_type);

                    vec![Operation::TempAllocate {
                        temp: temp_x,
                        body: vec![Operation::TempAllocate {
                            temp: temp_y,
                            body: vec![
                                Operation::Copy {
                                    source: x.position(),
                                    target: temp_x.position(),
                                    size: x.size(),
                                },
                                Operation::Copy {
                                    source: y.position(),
                                    target: temp_y.position(),
                                    size: y.size(),
                                },

                                // Algorithm:
                                //
                                // z = x != y
                                // x[y-x-]y[z+y[-]]
                                Operation::Loop {
                                    cond: temp_x.position(),
                                    body: vec![
                                        Operation::Decrement {
                                            target: temp_y.position(),
                                            amount: 1,
                                        },
                                        Operation::Decrement {
                                            target: temp_x.position(),
                                            amount: 1,
                                        },
                                    ],
                                },
                                Operation::Loop {
                                    cond: temp_y.position(),
                                    body: vec![
                                        Operation::Increment {
                                            target: target.position(),
                                            amount: 1,
                                        },
                                        Operation::Zero {
                                            target: temp_y,
                                        },
                                    ],
                                },
                            ],
                            should_zero: false,
                        }],
                        should_zero: false,
                    }]
                },
                _ => unreachable!(),
            })
        }
    );

    // Need this so that this next method definition does not overwrite the previous one
    scope.push_scope();

    // Method on [u8; _]
    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Array {item: u8_type, size: None}],
            return_type: unit_type,
        },
        move |scope, args, _| {
            match args[0] {
                ScopeItem::Array {memory, ..} => Ok(vec![
                    Operation::Write {
                        target: memory,
                    },
                ]),
                ScopeItem::ByteLiteral(ref bytes) => {
                    let u8_type = scope.primitives().u8();
                    let mem = scope.allocate(u8_type);

                    Ok(vec![
                        Operation::TempAllocate {
                            temp: mem,
                            body: bytes.iter().flat_map(|&ch| vec![
                                Operation::Increment {
                                    target: mem.position(),
                                    amount: ch,
                                },
                                Operation::Write {
                                    target: mem,
                                },
                                Operation::Decrement {
                                    target: mem.position(),
                                    amount: ch,
                                },
                            ]).collect(),
                            should_zero: false,
                        },
                    ])
                },
                _ => unreachable!(),
            }
        }
    );

    u8_type
}

#[cfg(test)]
mod tests {
    use super::*;

    use operations::scope::{ScopeType};

    #[test]
    fn defines_primitive() {
        let mut scope = ScopeStack::new();
        // Just put a random type ID for bool_type argument since it doesn't matter for this test
        define_u8(&mut scope, 1000);

        let u8_type_id = match **scope.lookup_type(&Identifier::from("u8")).first().unwrap() {
            ScopeType::Type(id) => id,
        };
        assert_eq!(scope.primitives().u8(), u8_type_id);
    }
}
<|endoftext|>


<commit_before>use num::Float;
use num::traits::FloatConst;
use std::cmp;
use std::marker::PhantomData;

use generate::generate::{IndexedPolygonGenerator, PolygonGenerator, SpatialPolygonGenerator,
                         SpatialVertexGenerator, VertexGenerator};
use generate::geometry::Unit;
use generate::topology::{Polygon, Quad, Triangle};

#[derive(Clone)]
pub struct UVSphere<T = f32>
where
    T: Float + FloatConst + Unit,
{
    nu: usize, // Meridians.
    nv: usize, // Parallels.
    unit: T,
    phantom: PhantomData<T>,
}

impl<T> UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn new(nu: usize, nv: usize, upper: T) -> Self {
        let nu = cmp::max(3, nu);
        let nv = cmp::max(2, nv);
        UVSphere {
            nu: nu,
            nv: nv,
            unit: upper,
            phantom: PhantomData,
        }
    }

    pub fn with_unit_radius(nu: usize, nv: usize) -> Self {
        Self::new(nu, nv, T::unit_radius().1)
    }

    pub fn with_unit_width(nu: usize, nv: usize) -> Self {
        Self::new(nu, nv, T::unit_width().1)
    }

    fn spatial_vertex(&self, u: usize, v: usize) -> (T, T, T) {
        let u = (T::from(u).unwrap() / T::from(self.nu).unwrap()) * T::PI() * (T::one() + T::one());
        let v = (T::from(v).unwrap() / T::from(self.nv).unwrap()) * T::PI();
        (
            self.unit * u.cos() * v.sin(),
            self.unit * u.sin() * v.sin(),
            self.unit * v.cos(),
        )
    }

    fn indexed_vertex(&self, u: usize, v: usize) -> usize {
        if v == 0 {
            0
        }
        else if v == self.nv {
            ((self.nv - 1) * self.nu) + 1
        }
        else {
            ((v - 1) * self.nu) + (u % self.nu) + 1
        }
    }

    fn map_polygon_index(&self, index: usize) -> (usize, usize) {
        (index % self.nu, index / self.nu)
    }
}

impl<T> VertexGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn vertex_count(&self) -> usize {
        (self.nv - 1) * self.nu + 2
    }
}

impl<T> SpatialVertexGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = (T, T, T);

    fn spatial_vertex(&self, index: usize) -> Self::Output {
        if index == 0 {
            self.spatial_vertex(0, 0)
        }
        else if index == self.vertex_count() - 1 {
            self.spatial_vertex(0, self.nv)
        }
        else {
            let index = index - 1;
            self.spatial_vertex(index % self.nu, (index / self.nu) + 1)
        }
    }
}

impl<T> PolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn polygon_count(&self) -> usize {
        self.nu * self.nv
    }
}

impl<T> SpatialPolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = Polygon<(T, T, T)>;

    fn spatial_polygon(&self, index: usize) -> Self::Output {
        // Prevent floating point rounding errors by wrapping the incremented
        // values for `(u, v)` into `(p, q)`. This is important for indexing
        // geometry, because small differences in the computation of spatial
        // vertices will produce unique output vertices.
        let (u, v) = self.map_polygon_index(index);
        let (p, q) = (u + 1, (v + 1) % self.nv);

        // Generate the vertices at the requested meridian and parallel. The
        // upper and lower bounds of (u, v) are always used, so generate them
        // in advance (`low` and `high`). Emit triangles at the poles,
        // otherwise quads.
        let low = self.spatial_vertex(u, v);
        let high = self.spatial_vertex(p, q);
        if v == 0 {
            Polygon::Triangle(Triangle::new(low, self.spatial_vertex(u, q), high))
        }
        else if v == self.nv - 1 {
            Polygon::Triangle(Triangle::new(high, self.spatial_vertex(p, v), low))
        }
        else {
            Polygon::Quad(Quad::new(
                low,
                self.spatial_vertex(u, q),
                high,
                self.spatial_vertex(p, v),
            ))
        }
    }
}

impl<T> IndexedPolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = Polygon<usize>;

    fn indexed_polygon(&self, index: usize) -> <Self as IndexedPolygonGenerator>::Output {
        let (u, v) = self.map_polygon_index(index);
        let (p, q) = (u + 1, v + 1);

        let low = self.indexed_vertex(u, v);
        let high = self.indexed_vertex(p, q);
        if v == 0 {
            Polygon::Triangle(Triangle::new(low, self.indexed_vertex(u, q), high))
        }
        else if v == self.nv - 1 {
            Polygon::Triangle(Triangle::new(high, self.indexed_vertex(p, v), low))
        }
        else {
            Polygon::Quad(Quad::new(
                low,
                self.indexed_vertex(u, q),
                high,
                self.indexed_vertex(p, v),
            ))
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;
    use std::iter::FromIterator;

    use super::super::*;

    #[test]
    fn vertex_count() {
        assert_eq!(
            5,
            sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                .spatial_vertices() // 5 conjoint vertices.
                .count()
        );
    }

    #[test]
    fn polygon_vertex_count() {
        assert_eq!(
            18,
            sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                .spatial_polygons() // 6 triangles, 18 vertices.
                .vertices()
                .count()
        );
    }

    #[test]
    fn index_to_vertex_mapping() {
        assert_eq!(
            5,
            BTreeSet::from_iter(
                sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                    .indexed_polygons() // 18 vertices, 5 indeces.
                    .vertices()
            ).len()
        )
    }
}
<commit_msg>Avoid floating point error by normalizing `u` at poles.<commit_after>use num::Float;
use num::traits::FloatConst;
use std::cmp;
use std::marker::PhantomData;

use generate::generate::{IndexedPolygonGenerator, PolygonGenerator, SpatialPolygonGenerator,
                         SpatialVertexGenerator, VertexGenerator};
use generate::geometry::Unit;
use generate::topology::{Polygon, Quad, Triangle};

#[derive(Clone)]
pub struct UVSphere<T = f32>
where
    T: Float + FloatConst + Unit,
{
    nu: usize, // Meridians.
    nv: usize, // Parallels.
    unit: T,
    phantom: PhantomData<T>,
}

impl<T> UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn new(nu: usize, nv: usize, upper: T) -> Self {
        let nu = cmp::max(3, nu);
        let nv = cmp::max(2, nv);
        UVSphere {
            nu: nu,
            nv: nv,
            unit: upper,
            phantom: PhantomData,
        }
    }

    pub fn with_unit_radius(nu: usize, nv: usize) -> Self {
        Self::new(nu, nv, T::unit_radius().1)
    }

    pub fn with_unit_width(nu: usize, nv: usize) -> Self {
        Self::new(nu, nv, T::unit_width().1)
    }

    fn spatial_vertex(&self, u: usize, v: usize) -> (T, T, T) {
        let u = (T::from(u).unwrap() / T::from(self.nu).unwrap()) * T::PI() * (T::one() + T::one());
        let v = (T::from(v).unwrap() / T::from(self.nv).unwrap()) * T::PI();
        (
            self.unit * u.cos() * v.sin(),
            self.unit * u.sin() * v.sin(),
            self.unit * v.cos(),
        )
    }

    fn indexed_vertex(&self, u: usize, v: usize) -> usize {
        if v == 0 {
            0
        }
        else if v == self.nv {
            ((self.nv - 1) * self.nu) + 1
        }
        else {
            ((v - 1) * self.nu) + (u % self.nu) + 1
        }
    }

    fn map_polygon_index(&self, index: usize) -> (usize, usize) {
        (index % self.nu, index / self.nu)
    }
}

impl<T> VertexGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn vertex_count(&self) -> usize {
        (self.nv - 1) * self.nu + 2
    }
}

impl<T> SpatialVertexGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = (T, T, T);

    fn spatial_vertex(&self, index: usize) -> Self::Output {
        if index == 0 {
            self.spatial_vertex(0, 0)
        }
        else if index == self.vertex_count() - 1 {
            self.spatial_vertex(0, self.nv)
        }
        else {
            let index = index - 1;
            self.spatial_vertex(index % self.nu, (index / self.nu) + 1)
        }
    }
}

impl<T> PolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    fn polygon_count(&self) -> usize {
        self.nu * self.nv
    }
}

impl<T> SpatialPolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = Polygon<(T, T, T)>;

    fn spatial_polygon(&self, index: usize) -> Self::Output {
        // Prevent floating point rounding errors by wrapping the incremented
        // values for `(u, v)` into `(p, q)`. This is important for indexing
        // geometry, because small differences in the computation of spatial
        // vertices will produce redundant output vertices. There should be
        // exactly `(nv - 1) * nu + 2` unique values of `(u, v)` used to
        // generate positions.
        //
        // There are two important observations:
        //
        //   1. `u` must wrap, but `v` need not. There are `nu` meridians of
        //      points and polygons, but there are `nv` parallels of polygons
        //      and `nv + 1` parallels of points.
        //   2. `u`, which represents a meridian, is meaningless at the poles,
        //      and can be normalized to zero.
        let (u, v) = self.map_polygon_index(index);
        let (p, q) = ((u + 1) % self.nu, v + 1);

        // Generate the vertices at the requested meridian and parallel. The
        // lower bound of `(u, v)` is always used, so compute that in advance
        // (`lower`). Emit triangles at the poles, otherwise quads.
        let lower = self.spatial_vertex(u, v);
        if v == 0 {
            Polygon::Triangle(Triangle::new(
                lower,
                self.spatial_vertex(u, q),
                self.spatial_vertex(p, q),
            ))
        }
        else if v == self.nv - 1 {
            Polygon::Triangle(Triangle::new(
                // Normalize `u` at the pole, using `(0, nv)` in place of
                // `(p, q)`.
                self.spatial_vertex(0, self.nv),
                self.spatial_vertex(p, v),
                lower,
            ))
        }
        else {
            Polygon::Quad(Quad::new(
                lower,
                self.spatial_vertex(u, q),
                self.spatial_vertex(p, q),
                self.spatial_vertex(p, v),
            ))
        }
    }
}

impl<T> IndexedPolygonGenerator for UVSphere<T>
where
    T: Float + FloatConst + Unit,
{
    type Output = Polygon<usize>;

    fn indexed_polygon(&self, index: usize) -> <Self as IndexedPolygonGenerator>::Output {
        let (u, v) = self.map_polygon_index(index);
        let (p, q) = (u + 1, v + 1);

        let low = self.indexed_vertex(u, v);
        let high = self.indexed_vertex(p, q);
        if v == 0 {
            Polygon::Triangle(Triangle::new(low, self.indexed_vertex(u, q), high))
        }
        else if v == self.nv - 1 {
            Polygon::Triangle(Triangle::new(high, self.indexed_vertex(p, v), low))
        }
        else {
            Polygon::Quad(Quad::new(
                low,
                self.indexed_vertex(u, q),
                high,
                self.indexed_vertex(p, v),
            ))
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;
    use std::iter::FromIterator;

    use super::super::*;

    #[test]
    fn vertex_count() {
        assert_eq!(
            5,
            sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                .spatial_vertices() // 5 conjoint vertices.
                .count()
        );
    }

    #[test]
    fn polygon_vertex_count() {
        assert_eq!(
            18,
            sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                .spatial_polygons() // 6 triangles, 18 vertices.
                .vertices()
                .count()
        );
    }

    #[test]
    fn index_to_vertex_mapping() {
        assert_eq!(
            5,
            BTreeSet::from_iter(
                sphere::UVSphere::<f32>::with_unit_radius(3, 2)
                    .indexed_polygons() // 18 vertices, 5 indeces.
                    .vertices()
            ).len()
        )
    }
}
<|endoftext|>


<commit_before>use std::any::Any;

use { GenericEvent, TOUCH };

/// Stores the touch state.
#[derive(Copy, Clone, RustcDecodable, RustcEncodable, PartialEq, Debug)]
pub enum Touch {
    /// The start of touch, for example
    /// a finger pressed down on a touch screen.
    Start,
    /// The move of touch, for example
    /// a finger moving while touching a touch screen.
    Move,
    /// The end of touch, for example
    /// taking a finger away from a touch screen.
    End,
    /// The cancel of touch, for example
    /// the window loses focus.
    Cancel,
}

/// Touch arguments
///
/// The `id` might be reused for different touches that do not overlap in time.
///
/// - Coordinates are normalized to support both touch screens and touch pads
/// - Supports both 2D and 3D touch
/// - The pressure direction vector should have maximum length 1
#[derive(Copy, Clone, RustcDecodable, RustcEncodable, PartialEq, Debug)]
pub struct TouchArgs {
    /// A unique identifier for touch device.
    pub device: i64,
    /// A unique identifier for touch event.
    pub id: i64,
    /// The x coordinate of the touch position, normalized 0..1.
    pub x: f64,
    /// The y coordinate of the touch position, normalized 0..1.
    pub y: f64,
    /// The z coordinate of the touch position, normalized 0..1.
    pub z: f64,
    /// The x coordinate of the touch pressure direction.
    pub px: f64,
    /// The y coordinate of the touch pressure direction.
    pub py: f64,
    /// The z coordinate of the touch pressure direction.
    pub pz: f64,
    /// Whether the touch is in 3D.
    pub is_3d: bool,
    /// The touch state.
    pub touch: Touch,
}

impl TouchArgs {
    /// Creates arguments for 2D touch.
    pub fn new(
        device: i64,
        id: i64,
        pos: [f64; 2],
        pressure: f64,
        touch: Touch
    ) -> TouchArgs {
        TouchArgs {
            device: device,
            id: id,
            x: pos[0],
            y: pos[1],
            z: 0.0,
            is_3d: false,
            px: 0.0,
            py: 0.0,
            pz: pressure,
            touch: touch,
        }
    }

    /// Creates arguments for 3D touch.
    ///
    /// The pressure direction vector should have maximum length 1.
    pub fn new_3d(
        device: i64,
        id: i64,
        pos: [f64; 3],
        pressure: [f64; 3],
        touch: Touch
    ) -> TouchArgs {
        TouchArgs {
            device: device,
            id: id,
            x: pos[0],
            y: pos[1],
            z: pos[2],
            is_3d: true,
            px: pressure[0],
            py: pressure[1],
            pz: pressure[2],
            touch: touch,
        }
    }

    /// The position of the touch in 2D.
    pub fn position(&self) -> [f64; 2] {
        [self.x, self.y]
    }

    /// The position of the touch in 3D.
    pub fn position3d(&self) -> [f64; 3] {
        [self.x, self.y, self.z]
    }

    /// The pressure magnitude, normalized 0..1.
    pub fn pressure(&self) -> f64 {
        (self.px * self.px + self.py * self.py + self.pz * self.pz).sqrt()
    }

    /// The pressure vector in 3D.
    pub fn pressure3d(&self) -> [f64; 3] {
        [self.px, self.py, self.pz]
    }
}

/// When a touch is started, moved, ended or cancelled.
pub trait TouchEvent: Sized {
    /// Creates a touch event.
    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self>;
    /// Calls closure if this is a touch event.
    fn touch<U, F>(&self, f: F) -> Option<U>
        where F: FnMut(&TouchArgs) -> U;
    /// Returns touch arguments.
    fn touch_args(&self) -> Option<TouchArgs> {
        self.touch(|args| args.clone())
    }
}

impl<T> TouchEvent for T where T: GenericEvent {
    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self> {
        GenericEvent::from_args(TOUCH, args as &Any, old_event)
    }

    fn touch<U, F>(&self, mut f: F) -> Option<U>
        where F: FnMut(&TouchArgs) -> U
    {
        if self.event_id() != TOUCH {
            return None;
        }
        self.with_args(|any| {
            if let Some(args) = any.downcast_ref::<TouchArgs>() {
                Some(f(args))
            } else {
                panic!("Expected TouchArgs")
            }
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_input_touch() {
        use super::super::{ Input, Motion };

        let pos = [0.0; 2];
        let e = Input::Move(Motion::Touch(
            TouchArgs::new(0, 0, pos, 1.0, Touch::Start)));
        let a: Option<Input> = TouchEvent::from_touch_args(
            &TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
        let b: Option<Input> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_event_touch() {
        use Event;
        use super::super::{ Input, Motion };

        let pos = [0.0; 2];
        let e = Event::Input(Input::Move(Motion::Touch(
            TouchArgs::new(0, 0, pos, 1.0, Touch::Start))));
        let a: Option<Event> = TouchEvent::from_touch_args(
            &TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
        let b: Option<Event> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_input_touch_3d() {
        use super::super::{ Input, Motion };

        let pos = [0.0; 3];
        let pressure = [0.0, 0.0, 1.0];
        let e = Input::Move(Motion::Touch(
            TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start)));
        let a: Option<Input> = TouchEvent::from_touch_args(
            &TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
        let b: Option<Input> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new_3d(t.device, t.id, t.position3d(), t.pressure3d(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_event_touch_3d() {
        use Event;
        use super::super::{ Input, Motion };

        let pos = [0.0; 3];
        let pressure = [0.0, 0.0, 1.0];
        let e = Event::Input(Input::Move(Motion::Touch(
            TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start))));
        let a: Option<Event> = TouchEvent::from_touch_args(
            &TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
        let b: Option<Event> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new_3d(t.device, t.id, t.position3d(), t.pressure3d(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }
}
<commit_msg>Fixed `_3d`<commit_after>use std::any::Any;

use { GenericEvent, TOUCH };

/// Stores the touch state.
#[derive(Copy, Clone, RustcDecodable, RustcEncodable, PartialEq, Debug)]
pub enum Touch {
    /// The start of touch, for example
    /// a finger pressed down on a touch screen.
    Start,
    /// The move of touch, for example
    /// a finger moving while touching a touch screen.
    Move,
    /// The end of touch, for example
    /// taking a finger away from a touch screen.
    End,
    /// The cancel of touch, for example
    /// the window loses focus.
    Cancel,
}

/// Touch arguments
///
/// The `id` might be reused for different touches that do not overlap in time.
///
/// - Coordinates are normalized to support both touch screens and touch pads
/// - Supports both 2D and 3D touch
/// - The pressure direction vector should have maximum length 1
#[derive(Copy, Clone, RustcDecodable, RustcEncodable, PartialEq, Debug)]
pub struct TouchArgs {
    /// A unique identifier for touch device.
    pub device: i64,
    /// A unique identifier for touch event.
    pub id: i64,
    /// The x coordinate of the touch position, normalized 0..1.
    pub x: f64,
    /// The y coordinate of the touch position, normalized 0..1.
    pub y: f64,
    /// The z coordinate of the touch position, normalized 0..1.
    pub z: f64,
    /// The x coordinate of the touch pressure direction.
    pub px: f64,
    /// The y coordinate of the touch pressure direction.
    pub py: f64,
    /// The z coordinate of the touch pressure direction.
    pub pz: f64,
    /// Whether the touch is in 3D.
    pub is_3d: bool,
    /// The touch state.
    pub touch: Touch,
}

impl TouchArgs {
    /// Creates arguments for 2D touch.
    pub fn new(
        device: i64,
        id: i64,
        pos: [f64; 2],
        pressure: f64,
        touch: Touch
    ) -> TouchArgs {
        TouchArgs {
            device: device,
            id: id,
            x: pos[0],
            y: pos[1],
            z: 0.0,
            is_3d: false,
            px: 0.0,
            py: 0.0,
            pz: pressure,
            touch: touch,
        }
    }

    /// Creates arguments for 3D touch.
    ///
    /// The pressure direction vector should have maximum length 1.
    pub fn new_3d(
        device: i64,
        id: i64,
        pos: [f64; 3],
        pressure: [f64; 3],
        touch: Touch
    ) -> TouchArgs {
        TouchArgs {
            device: device,
            id: id,
            x: pos[0],
            y: pos[1],
            z: pos[2],
            is_3d: true,
            px: pressure[0],
            py: pressure[1],
            pz: pressure[2],
            touch: touch,
        }
    }

    /// The position of the touch in 2D.
    pub fn position(&self) -> [f64; 2] {
        [self.x, self.y]
    }

    /// The position of the touch in 3D.
    pub fn position_3d(&self) -> [f64; 3] {
        [self.x, self.y, self.z]
    }

    /// The pressure magnitude, normalized 0..1.
    pub fn pressure(&self) -> f64 {
        (self.px * self.px + self.py * self.py + self.pz * self.pz).sqrt()
    }

    /// The pressure vector in 3D.
    pub fn pressure_3d(&self) -> [f64; 3] {
        [self.px, self.py, self.pz]
    }
}

/// When a touch is started, moved, ended or cancelled.
pub trait TouchEvent: Sized {
    /// Creates a touch event.
    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self>;
    /// Calls closure if this is a touch event.
    fn touch<U, F>(&self, f: F) -> Option<U>
        where F: FnMut(&TouchArgs) -> U;
    /// Returns touch arguments.
    fn touch_args(&self) -> Option<TouchArgs> {
        self.touch(|args| args.clone())
    }
}

impl<T> TouchEvent for T where T: GenericEvent {
    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self> {
        GenericEvent::from_args(TOUCH, args as &Any, old_event)
    }

    fn touch<U, F>(&self, mut f: F) -> Option<U>
        where F: FnMut(&TouchArgs) -> U
    {
        if self.event_id() != TOUCH {
            return None;
        }
        self.with_args(|any| {
            if let Some(args) = any.downcast_ref::<TouchArgs>() {
                Some(f(args))
            } else {
                panic!("Expected TouchArgs")
            }
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_input_touch() {
        use super::super::{ Input, Motion };

        let pos = [0.0; 2];
        let e = Input::Move(Motion::Touch(
            TouchArgs::new(0, 0, pos, 1.0, Touch::Start)));
        let a: Option<Input> = TouchEvent::from_touch_args(
            &TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
        let b: Option<Input> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_event_touch() {
        use Event;
        use super::super::{ Input, Motion };

        let pos = [0.0; 2];
        let e = Event::Input(Input::Move(Motion::Touch(
            TouchArgs::new(0, 0, pos, 1.0, Touch::Start))));
        let a: Option<Event> = TouchEvent::from_touch_args(
            &TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
        let b: Option<Event> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_input_touch_3d() {
        use super::super::{ Input, Motion };

        let pos = [0.0; 3];
        let pressure = [0.0, 0.0, 1.0];
        let e = Input::Move(Motion::Touch(
            TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start)));
        let a: Option<Input> = TouchEvent::from_touch_args(
            &TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
        let b: Option<Input> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new_3d(t.device, t.id, t.position3d(), t.pressure3d(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_event_touch_3d() {
        use Event;
        use super::super::{ Input, Motion };

        let pos = [0.0; 3];
        let pressure = [0.0, 0.0, 1.0];
        let e = Event::Input(Input::Move(Motion::Touch(
            TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start))));
        let a: Option<Event> = TouchEvent::from_touch_args(
            &TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
        let b: Option<Event> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new_3d(t.device, t.id, t.position3d(), t.pressure3d(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }
}
<|endoftext|>


<commit_before>use super::Status;
use crate as ion_shell;
use crate::{shell::Shell, types};
use builtins_proc::builtin;

// TODO: Add support for multiple name in builtins man
#[builtin(
    names = "eq, is",
    desc = "checks if two arguments are the same",
    man = "
SYNOPSIS
    is [ -h | --help ] [not]

DESCRIPTION
    Returns 0 if the two arguments are equal

OPTIONS
    not
        returns 0 if the two arguments are not equal."
)]
pub fn is(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
    match args.len() {
        4 => {
            if args[1] != "not" {
                return Status::error(format!("Expected 'not' instead found '{}'", args[1]));
            } else if eval_arg(&*args[2], shell) == eval_arg(&*args[3], shell) {
                return Status::error("");
            }
        }
        3 => {
            if eval_arg(&*args[1], shell) != eval_arg(&*args[2], shell) {
                return Status::error("");
            }
        }
        _ => return Status::error("is needs 3 or 4 arguments"),
    }

    Status::SUCCESS
}

fn eval_arg(arg: &str, shell: &mut Shell<'_>) -> types::Str {
    let value = get_var_string(arg, shell);
    if &*value != "" {
        return value;
    }
    arg.into()
}

// On error returns an empty String.
fn get_var_string(name: &str, shell: &mut Shell<'_>) -> types::Str {
    if name.chars().nth(0).unwrap() != '$' {
        return "".into();
    }

    match shell.variables().get_str(&name[1..]) {
        Ok(s) => s,
        Err(why) => {
            eprintln!("{}", why);
            "".into()
        }
    }
}

#[test]
fn test_is() {
    fn vec_string(args: &[&str]) -> Vec<types::Str> { args.iter().map(|&s| s.into()).collect() }
    let mut shell = Shell::default();
    shell.variables_mut().set("x", "value");
    shell.variables_mut().set("y", "0");

    // Four arguments
    assert!(builtin_is(&vec_string(&["is", " ", " ", " "]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", "not", " ", " "]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", "not", "$x", "$x"]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", "not", "2", "1"]), &mut shell).is_success());
    assert!(builtin_is(&vec_string(&["is", "not", "$x", "$y"]), &mut shell).is_success());

    // Three arguments
    assert!(builtin_is(&vec_string(&["is", "1", "2"]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", "$x", "$y"]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", " ", " "]), &mut shell).is_success());
    assert!(builtin_is(&vec_string(&["is", "$x", "$x"]), &mut shell).is_success());

    // Two arguments
    assert!(builtin_is(&vec_string(&["is", " "]), &mut shell).is_failure());

    // One argument
    assert!(builtin_is(&vec_string(&["is"]), &mut shell).is_failure());
}
<commit_msg>fix: Fix panic when eq argument was empty & use Option rather than ""<commit_after>use super::Status;
use crate as ion_shell;
use crate::{shell::Shell, types};
use builtins_proc::builtin;

// TODO: Add support for multiple name in builtins man
#[builtin(
    names = "eq, is",
    desc = "checks if two arguments are the same",
    man = "
SYNOPSIS
    is [ -h | --help ] [not]

DESCRIPTION
    Returns 0 if the two arguments are equal

OPTIONS
    not
        returns 0 if the two arguments are not equal."
)]
pub fn is(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
    match args.len() {
        4 => {
            if args[1] != "not" {
                return Status::error(format!("Expected 'not' instead found '{}'", args[1]));
            } else if eval_arg(&*args[2], shell) == eval_arg(&*args[3], shell) {
                return Status::error("");
            }
        }
        3 => {
            if eval_arg(&*args[1], shell) != eval_arg(&*args[2], shell) {
                return Status::error("");
            }
        }
        _ => return Status::error("is needs 3 or 4 arguments"),
    }

    Status::SUCCESS
}

fn eval_arg(arg: &str, shell: &mut Shell<'_>) -> types::Str {
    if let Some(value) = get_var_string(arg, shell) {
        value
    } else {
        arg.into()
    }
}

// On error returns an empty String.
fn get_var_string(name: &str, shell: &mut Shell<'_>) -> Option<types::Str> {
    if name.chars().nth(0)? != '$' {
        return None;
    }

    match shell.variables().get_str(&name[1..]) {
        Ok(s) => Some(s),
        Err(why) => {
            eprintln!("{}", why);
            None
        }
    }
}

#[test]
fn test_is() {
    fn vec_string(args: &[&str]) -> Vec<types::Str> { args.iter().map(|&s| s.into()).collect() }
    let mut shell = Shell::default();
    shell.variables_mut().set("x", "value");
    shell.variables_mut().set("y", "0");

    // Four arguments
    assert!(builtin_is(&vec_string(&["is", " ", " ", " "]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", "not", " ", " "]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", "not", "$x", "$x"]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", "not", "2", "1"]), &mut shell).is_success());
    assert!(builtin_is(&vec_string(&["is", "not", "$x", "$y"]), &mut shell).is_success());

    // Three arguments
    assert!(builtin_is(&vec_string(&["is", "1", "2"]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", "$x", "$y"]), &mut shell).is_failure());
    assert!(builtin_is(&vec_string(&["is", " ", " "]), &mut shell).is_success());
    assert!(builtin_is(&vec_string(&["is", "$x", "$x"]), &mut shell).is_success());

    // Two arguments
    assert!(builtin_is(&vec_string(&["is", " "]), &mut shell).is_failure());

    // One argument
    assert!(builtin_is(&vec_string(&["is"]), &mut shell).is_failure());
}
<|endoftext|>