<commit_before>extern crate security_framework;

use std::mem;
use std::io::{self, Read, Write, Error, ErrorKind};

use self::security_framework::base::Error as StError;
use self::security_framework::certificate::SecCertificate;
use self::security_framework::identity::SecIdentity;
use self::security_framework::secure_transport as st;
use self::security_framework::trust::TrustResult;
use futures::{Poll, Future};

pub struct TlsStream<S> {
    stream: st::SslStream<S>,
}

pub struct ServerContext {
    inner: st::SslContext,
}

pub struct ClientContext {
    inner: st::SslContext,
    certs: Vec<SecCertificate>,
}

fn cx_new(side: st::ProtocolSide, kind: st::ConnectionType)
          -> io::Result<st::SslContext> {
    st::SslContext::new(side, kind).map_err(translate)
}

impl ClientContext {
    pub fn new() -> io::Result<ClientContext> {
        let cx = try!(cx_new(st::ProtocolSide::Client,
                             st::ConnectionType::Stream));
        Ok(ClientContext {
            inner: cx,
            certs: Vec::new(),
        })
    }

    pub fn handshake<S>(self,
                        domain: &str,
                        stream: S) -> ClientHandshake<S>
        where S: Read + Write,
    {
        let mut inner = self.inner;
        let res = inner.set_peer_domain_name(domain)
                       .map_err(st::HandshakeError::Failure)
                       .and_then(|()| {
            inner.handshake(stream)
        });
        ClientHandshake {
            inner: Handshake::new(res, self.certs),
        }
    }
}

impl ServerContext {
    pub fn handshake<S>(self, stream: S) -> ServerHandshake<S>
        where S: Read + Write,
    {
        ServerHandshake {
            inner: Handshake::new(self.inner.handshake(stream), Vec::new()),
        }
    }
}

pub struct ClientHandshake<S> {
    inner: Handshake<S>,
}

pub struct ServerHandshake<S> {
    inner: Handshake<S>,
}

enum Handshake<S> {
    Error(io::Error),
    Stream(st::SslStream<S>),
    Interrupted(st::MidHandshakeSslStream<S>,
                Vec<SecCertificate>),
    Empty,
}

impl<S> Handshake<S> {
    fn new(res: Result<st::SslStream<S>, st::HandshakeError<S>>,
           certs: Vec<SecCertificate>)
           -> Handshake<S> {
        match res {
            Ok(s) => {
                assert!(certs.len() == 0);
                Handshake::Stream(s)
            }
            Err(st::HandshakeError::Failure(e)) => {
                Handshake::Error(Error::new(ErrorKind::Other, e))
            }
            Err(st::HandshakeError::Interrupted(s)) => {
                Handshake::Interrupted(s, certs)
            }
        }
    }
}

impl<S> Future for ClientHandshake<S>
    where S: Read + Write,
{
    type Item = TlsStream<S>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<TlsStream<S>, io::Error> {
        self.inner.poll()
    }
}

impl<S> Future for ServerHandshake<S>
    where S: Read + Write,
{
    type Item = TlsStream<S>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<TlsStream<S>, io::Error> {
        self.inner.poll()
    }
}

impl<S> Handshake<S> {
    fn validate_certs(&self,
                      stream: &st::MidHandshakeSslStream<S>,
                      certs: &[SecCertificate]) -> io::Result<()> {
        // Copied from ClientBuilder in secure_transport.rs
        //
        // TODO: this should happen when we get an interrupted error during
        //       read/write.
        if certs.len() == 0 || !stream.server_auth_completed() {
            return Ok(())
        }

        debug!("handshake auth completed, checking for validity");
        let mut trust = try!(stream.context().peer_trust().map_err(translate));
        try!(trust.set_anchor_certificates(&certs).map_err(translate));
        let trusted = try!(trust.evaluate().map_err(translate));
        match trusted {
            TrustResult::Proceed |
            TrustResult::Unspecified => Ok(()),
            TrustResult::Invalid |
            TrustResult::OtherError => {
                Err(Error::new(ErrorKind::Other, "bad ssl request"))
            }
            TrustResult::Deny => {
                Err(Error::new(ErrorKind::Other, "trust setting denied cert"))
            }
            TrustResult::RecoverableTrustFailure |
            TrustResult::FatalTrustFailure => {
                Err(Error::new(ErrorKind::Other, "not a trusted certificate"))
            }
        }
    }
}

impl<S> Future for Handshake<S>
    where S: Read + Write,
{
    type Item = TlsStream<S>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<TlsStream<S>, io::Error> {
        let (stream, certs) = match mem::replace(self, Handshake::Empty) {
            Handshake::Error(e) => return Poll::Err(e),
            Handshake::Empty => panic!("can't poll handshake twice"),
            Handshake::Stream(s) => return Poll::Ok(TlsStream::new(s)),
            Handshake::Interrupted(s, certs) => (s, certs),
        };

        if let Err(e) = self.validate_certs(&stream, &certs) {
            return Poll::Err(e)
        }

        // TODO: dedup with Handshake::new
        match stream.handshake() {
            Ok(s) => Poll::Ok(TlsStream::new(s)),
            Err(st::HandshakeError::Failure(e)) => {
                Poll::Err(Error::new(ErrorKind::Other, e))
            }
            Err(st::HandshakeError::Interrupted(s)) => {
                *self = Handshake::Interrupted(s, certs);
                Poll::NotReady
            }
        }
    }
}

fn translate(err: StError) -> Error {
    Error::new(ErrorKind::Other, err)
}

impl<S> TlsStream<S> {
    fn new(stream: st::SslStream<S>) -> TlsStream<S> {
        TlsStream {
            stream: stream,
        }
    }
}

impl<S: Read + Write> Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.stream.read(buf)
    }
}

impl<S: Read + Write> Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stream.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stream.flush()
    }
}

/// Extension trait for servers backed by SecureTransport.
pub trait ServerContextExt: Sized {
    /// Creates a new server given the specified `identity` and list of
    /// certificates supported by the server.
    ///
    /// The server will use this identity and certificates for the certificates
    /// to use when connections are initiated to it.
    fn new(identity: &SecIdentity,
           certs: &[SecCertificate]) -> io::Result<Self>;

    /// Gets a mutable reference to the underlying SSL context, allowing further
    /// configuration.
    ///
    /// The SSL context here will eventually get used to initiate the client
    /// connection, and it will otherwise be configured to validate the hostname
    /// given to `handshake` by default.
    fn ssl_context_mut(&mut self) -> &mut st::SslContext;
}

impl ServerContextExt for ::ServerContext {
    fn new(identity: &SecIdentity,
           certs: &[SecCertificate]) -> io::Result<::ServerContext> {
        let mut cx = try!(cx_new(st::ProtocolSide::Server,
                                 st::ConnectionType::Stream));
        try!(cx.set_certificate(&identity, certs)
               .map_err(translate));
        Ok(::ServerContext { inner: ServerContext { inner: cx } })
    }

    fn ssl_context_mut(&mut self) -> &mut st::SslContext {
        &mut self.inner.inner
    }
}

/// Extension trait for clients backed by OpenSSL.
pub trait ClientContextExt {
    /// Adds a certificate to be used when validating the trust of the remote
    /// peer.
    ///
    /// The certificates provided will be trusted when communicating with the
    /// remote host.
    fn anchor_certificates(&mut self,
                           certs: &[SecCertificate]) -> io::Result<()>;

    /// Gets a mutable reference to the underlying SSL context, allowing further
    /// configuration.
    ///
    /// The SSL context here will eventually get used to initiate the client
    /// connection, and it will otherwise be configured to validate the hostname
    /// given to `handshake` by default.
    fn ssl_context_mut(&mut self) -> &mut st::SslContext;
}

impl ClientContextExt for ::ClientContext {
    fn anchor_certificates(&mut self,
                           certs: &[SecCertificate]) -> io::Result<()> {
        try!(self.inner.inner.set_break_on_server_auth(true).map_err(translate));
        self.inner.certs.extend(certs.iter().cloned());
        Ok(())
    }

    fn ssl_context_mut(&mut self) -> &mut st::SslContext {
        &mut self.inner.inner
    }
}

/// Extension trait for streams backed by SecureTransport.
pub trait TlsStreamExt {
    /// Gets a shared reference to the underlying SSL context, allowing further
    /// configuration and/or inspection of the SSL/TLS state.
    fn ssl_context(&self) -> &st::SslContext;

    /// Gets a mutable reference to the underlying SSL context, allowing further
    /// configuration and/or inspection of the SSL/TLS state.
    fn ssl_context_mut(&mut self) -> &mut st::SslContext;
}

impl<S> TlsStreamExt for ::TlsStream<S> {
    fn ssl_context(&self) -> &st::SslContext {
        self.inner.stream.context()
    }

    fn ssl_context_mut(&mut self) -> &mut st::SslContext {
        self.inner.stream.context_mut()
    }
}
<commit_msg>Fix deadlock on OSX TLS<commit_after>extern crate security_framework;

use std::mem;
use std::io::{self, Read, Write, Error, ErrorKind};

use self::security_framework::base::Error as StError;
use self::security_framework::certificate::SecCertificate;
use self::security_framework::identity::SecIdentity;
use self::security_framework::secure_transport as st;
use self::security_framework::trust::TrustResult;
use futures::{Poll, Future};

pub struct TlsStream<S> {
    stream: st::SslStream<S>,
}

pub struct ServerContext {
    inner: st::SslContext,
}

pub struct ClientContext {
    inner: st::SslContext,
    certs: Vec<SecCertificate>,
}

fn cx_new(side: st::ProtocolSide, kind: st::ConnectionType)
          -> io::Result<st::SslContext> {
    st::SslContext::new(side, kind).map_err(translate)
}

impl ClientContext {
    pub fn new() -> io::Result<ClientContext> {
        let cx = try!(cx_new(st::ProtocolSide::Client,
                             st::ConnectionType::Stream));
        Ok(ClientContext {
            inner: cx,
            certs: Vec::new(),
        })
    }

    pub fn handshake<S>(self,
                        domain: &str,
                        stream: S) -> ClientHandshake<S>
        where S: Read + Write,
    {
        let mut inner = self.inner;
        let res = inner.set_peer_domain_name(domain)
                       .map_err(st::HandshakeError::Failure)
                       .and_then(|()| {
            inner.handshake(stream)
        });
        ClientHandshake {
            inner: Handshake::new(res, self.certs),
        }
    }
}

impl ServerContext {
    pub fn handshake<S>(self, stream: S) -> ServerHandshake<S>
        where S: Read + Write,
    {
        ServerHandshake {
            inner: Handshake::new(self.inner.handshake(stream), Vec::new()),
        }
    }
}

pub struct ClientHandshake<S> {
    inner: Handshake<S>,
}

pub struct ServerHandshake<S> {
    inner: Handshake<S>,
}

enum Handshake<S> {
    Error(io::Error),
    Stream(st::SslStream<S>),
    Interrupted(st::MidHandshakeSslStream<S>,
                Vec<SecCertificate>),
    Empty,
}

impl<S> Handshake<S> {
    fn new(res: Result<st::SslStream<S>, st::HandshakeError<S>>,
           certs: Vec<SecCertificate>)
           -> Handshake<S> {
        match res {
            Ok(s) => {
                assert!(certs.len() == 0);
                Handshake::Stream(s)
            }
            Err(st::HandshakeError::Failure(e)) => {
                Handshake::Error(Error::new(ErrorKind::Other, e))
            }
            Err(st::HandshakeError::Interrupted(s)) => {
                Handshake::Interrupted(s, certs)
            }
        }
    }
}

impl<S> Future for ClientHandshake<S>
    where S: Read + Write,
{
    type Item = TlsStream<S>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<TlsStream<S>, io::Error> {
        self.inner.poll()
    }
}

impl<S> Future for ServerHandshake<S>
    where S: Read + Write,
{
    type Item = TlsStream<S>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<TlsStream<S>, io::Error> {
        self.inner.poll()
    }
}

impl<S> Handshake<S> {
    fn validate_certs(&self,
                      stream: &st::MidHandshakeSslStream<S>,
                      certs: &[SecCertificate]) -> io::Result<()> {
        // Copied from ClientBuilder in secure_transport.rs
        //
        // TODO: this should happen when we get an interrupted error during
        //       read/write.
        if certs.len() == 0 || !stream.server_auth_completed() {
            return Ok(())
        }

        debug!("handshake auth completed, checking for validity");
        let mut trust = try!(stream.context().peer_trust().map_err(translate));
        try!(trust.set_anchor_certificates(&certs).map_err(translate));
        let trusted = try!(trust.evaluate().map_err(translate));
        match trusted {
            TrustResult::Proceed |
            TrustResult::Unspecified => Ok(()),
            TrustResult::Invalid |
            TrustResult::OtherError => {
                Err(Error::new(ErrorKind::Other, "bad ssl request"))
            }
            TrustResult::Deny => {
                Err(Error::new(ErrorKind::Other, "trust setting denied cert"))
            }
            TrustResult::RecoverableTrustFailure |
            TrustResult::FatalTrustFailure => {
                Err(Error::new(ErrorKind::Other, "not a trusted certificate"))
            }
        }
    }
}

impl<S> Future for Handshake<S>
    where S: Read + Write,
{
    type Item = TlsStream<S>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<TlsStream<S>, io::Error> {
        let (mut stream, certs) = match mem::replace(self, Handshake::Empty) {
            Handshake::Error(e) => return Poll::Err(e),
            Handshake::Empty => panic!("can't poll handshake twice"),
            Handshake::Stream(s) => return Poll::Ok(TlsStream::new(s)),
            Handshake::Interrupted(s, certs) => (s, certs),
        };

        loop {
            if let Err(e) = self.validate_certs(&stream, &certs) {
                return Poll::Err(e)
            }

            // TODO: dedup with Handshake::new
            stream = match stream.handshake() {
                Ok(s) => return Poll::Ok(TlsStream::new(s)),
                Err(st::HandshakeError::Failure(e)) => {
                    return Poll::Err(Error::new(ErrorKind::Other, e))
                }
                Err(st::HandshakeError::Interrupted(s)) => s,
            };

            if stream.would_block() {
                *self = Handshake::Interrupted(stream, certs);
                return Poll::NotReady
            }
        }
    }
}

fn translate(err: StError) -> Error {
    Error::new(ErrorKind::Other, err)
}

impl<S> TlsStream<S> {
    fn new(stream: st::SslStream<S>) -> TlsStream<S> {
        TlsStream {
            stream: stream,
        }
    }
}

impl<S: Read + Write> Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.stream.read(buf)
    }
}

impl<S: Read + Write> Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stream.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stream.flush()
    }
}

/// Extension trait for servers backed by SecureTransport.
pub trait ServerContextExt: Sized {
    /// Creates a new server given the specified `identity` and list of
    /// certificates supported by the server.
    ///
    /// The server will use this identity and certificates for the certificates
    /// to use when connections are initiated to it.
    fn new(identity: &SecIdentity,
           certs: &[SecCertificate]) -> io::Result<Self>;

    /// Gets a mutable reference to the underlying SSL context, allowing further
    /// configuration.
    ///
    /// The SSL context here will eventually get used to initiate the client
    /// connection, and it will otherwise be configured to validate the hostname
    /// given to `handshake` by default.
    fn ssl_context_mut(&mut self) -> &mut st::SslContext;
}

impl ServerContextExt for ::ServerContext {
    fn new(identity: &SecIdentity,
           certs: &[SecCertificate]) -> io::Result<::ServerContext> {
        let mut cx = try!(cx_new(st::ProtocolSide::Server,
                                 st::ConnectionType::Stream));
        try!(cx.set_certificate(&identity, certs)
               .map_err(translate));
        Ok(::ServerContext { inner: ServerContext { inner: cx } })
    }

    fn ssl_context_mut(&mut self) -> &mut st::SslContext {
        &mut self.inner.inner
    }
}

/// Extension trait for clients backed by OpenSSL.
pub trait ClientContextExt {
    /// Adds a certificate to be used when validating the trust of the remote
    /// peer.
    ///
    /// The certificates provided will be trusted when communicating with the
    /// remote host.
    fn anchor_certificates(&mut self,
                           certs: &[SecCertificate]) -> io::Result<()>;

    /// Gets a mutable reference to the underlying SSL context, allowing further
    /// configuration.
    ///
    /// The SSL context here will eventually get used to initiate the client
    /// connection, and it will otherwise be configured to validate the hostname
    /// given to `handshake` by default.
    fn ssl_context_mut(&mut self) -> &mut st::SslContext;
}

impl ClientContextExt for ::ClientContext {
    fn anchor_certificates(&mut self,
                           certs: &[SecCertificate]) -> io::Result<()> {
        try!(self.inner.inner.set_break_on_server_auth(true).map_err(translate));
        self.inner.certs.extend(certs.iter().cloned());
        Ok(())
    }

    fn ssl_context_mut(&mut self) -> &mut st::SslContext {
        &mut self.inner.inner
    }
}

/// Extension trait for streams backed by SecureTransport.
pub trait TlsStreamExt {
    /// Gets a shared reference to the underlying SSL context, allowing further
    /// configuration and/or inspection of the SSL/TLS state.
    fn ssl_context(&self) -> &st::SslContext;

    /// Gets a mutable reference to the underlying SSL context, allowing further
    /// configuration and/or inspection of the SSL/TLS state.
    fn ssl_context_mut(&mut self) -> &mut st::SslContext;
}

impl<S> TlsStreamExt for ::TlsStream<S> {
    fn ssl_context(&self) -> &st::SslContext {
        self.inner.stream.context()
    }

    fn ssl_context_mut(&mut self) -> &mut st::SslContext {
        self.inner.stream.context_mut()
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate adapton_lab;

use std::cmp::Ordering;
use std::rc::Rc;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Write;
use std::io::BufRead;
use std::fs::File;
use std::collections::HashMap;

use adapton_lab::labviz::{Div,WriteHTML};

// Temp: Make this better at some point?
type Graph = HashMap<Rc<String>, Vec<Rc<String>>>;
type NodeSet = HashMap<Rc<String>,()>;
type NodeCount = HashMap<Rc<String>,usize>;
type ConsCount = HashMap<Rc<String>,usize>;

#[derive(Debug)]
struct Options {
    tips: bool,
    tips_visited: bool,
}

impl Options {
    fn new() -> Self {
        Options {
            tips:true,
            tips_visited:false,
        }
    }
}

struct St {
    nodes: NodeSet,
    leaves:NodeSet,
    roots: NodeSet,
    graph: Graph,
    edges: Vec<(Rc<String>, Rc<String>)>,
    rev_graph:Graph,
    cons_count:ConsCount,
}

impl St {
    fn new() -> Self {
        St {
            nodes: HashMap::new(),
            leaves:HashMap::new(),
            roots: HashMap::new(),
            graph: HashMap::new(),
            edges: Vec::new(),
            rev_graph:HashMap::new(),
            cons_count:HashMap::new(),
        }
    }
    fn process_nodes(self:&mut Self) {
        self.leaves=HashMap::new();
        self.roots=HashMap::new();
        for (node,_) in self.nodes.iter() {

            let cons = Rc::new(cons_of_node_name(&node));
            let cons_count = (self.cons_count.get(&cons).unwrap_or(&0)).clone();
            self.cons_count.insert(cons, cons_count + 1);

            match self.graph.get(node) {
                None => { self.leaves.insert(node.clone(),()); },
                Some(_out) => {}
            };
            match self.rev_graph.get(node) {
                None => { self.roots.insert(node.clone(),()); },
                Some(_out) => {}
            };
        }
    }
    fn add_edge(self:&mut Self, src:Rc<String>, tgt:Rc<String>) {
        self.nodes.insert(src.clone(), ());
        self.nodes.insert(tgt.clone(), ());
        let edge = if false {
            (src, tgt)
        } else {
            (tgt, src)
        };
        self.edges.push(edge.clone());

        let inserted : bool = match self.graph.get_mut(&edge.0) {
            None => { false }
            Some(out) => {
                out.push(edge.1.clone());
                true
            },
        };
        if !inserted {
            self.graph.insert(edge.0.clone(), vec![ edge.1.clone() ]);
        };

        let inserted : bool = match self.rev_graph.get_mut(&edge.1) {
            None => { false }
            Some(out) => {
                out.push(edge.0.clone());
                true
            },
        };
        if !inserted {
            self.rev_graph.insert(edge.1, vec![ edge.0 ]);
        };
    }
}

fn main() {
    let mut options = Options::new();
    let args = clap::App::new("dep-viz")
        .version("0.1")
        .author("Matthew Hammer <matthew.hammer@colorado.edu>")
        .about("Consumes Rustc dependency information; Produces visualizations as HTML via a depth-first traversal.")
        .args_from_usage("\
                --no-tips            'smaller HTML files: do not show tips for nodes on hover'
                --tips-visited       'even larger HTML files: show tips even for visited nodes'
            -i, --infile=[infile]    'name for input file'
            -o, --outfile=[outfile]  'name for output file'
            -c, --countfile=[countfile]  'name for output file with DepNode constuctor counts'
"
        )
    .get_matches();
    let infile_name = args.value_of("infile").unwrap_or("dep_graph.txt");
    let outfile_name = args.value_of("outfile").unwrap_or("dep_graph.html");

    options.tips = !(args.is_present("no-tips"));
    options.tips_visited = args.is_present("tips-visited");

    println!("{:?}\n", options);

    println!("Reading input file: {}", infile_name);
    let f = File::open(infile_name).unwrap();
    let file = BufReader::new(&f);
    let mut st = St::new();
    for (_num, line) in file.lines().enumerate() {
        let line = line.unwrap();
        let v: Vec<&str> = line.split(" -> ").map(|s|s.trim()).collect();
        assert_eq!(v.len(), 2);
        st.add_edge(Rc::new(v[0].to_string()),
                    Rc::new(v[1].to_string()));
    }
    println!("...Read input file: {}", infile_name);
    st.process_nodes();
    println!("  Found {} edges", st.edges.len());
    println!("  Found {} nodes", st.nodes.len());
    println!("    Found {} root nodes", st.roots.len());
    println!("    Found {} leaf nodes", st.leaves.len());

    println!("\nCounting DepNode constructor frequencies...");
    let count_outfile_name = args.value_of("countfile").unwrap_or("dep_graph.counts.txt");
    let count_outfile = File::create(count_outfile_name).unwrap();
    let mut buf_writer = BufWriter::new(count_outfile);
    let mut counts = vec![];
    for (cons,cons_count) in st.cons_count { counts.push((cons, cons_count)) }
    counts.sort_by(|&(_,c1),&(_,c2)| if c1 <= c2 { Ordering::Less } else { Ordering::Greater } );
    for (cons,cons_count) in counts {
        let _ = write!(&mut buf_writer, "{:8} {}\n", cons_count, cons);
    }
    let _ = buf_writer.flush();
    println!("...Wrote DepNode constructor frequencies: {}", count_outfile_name);

    println!("\nPerforming DFS on graph...");
    let mut stack = vec![];
    for (node,_) in st.roots.iter() {
        stack.push(node.clone());
    }

    let (div, visited) = dfs(&options, &st.graph, stack);
    println!("...Performed DFS on graph.");
    println!("  Visited {} nodes, {:.1}% of total", visited.len(),
             100f32 * (visited.len() as f32) / (st.nodes.len() as f32));

    if false {
        println!("\nFinding unvisited graph nodes...");
        let mut unvisited : NodeSet = HashMap::new();
        for (node, _) in st.nodes.iter() {
            if visited.get(node) == None {
                unvisited.insert(node.clone(), ());
            }
        }
        assert_eq!(visited.len() + unvisited.len(),
                   st.nodes.len());
        println!("...Found {} unvisited graph nodes, {:.1}% of total nodes.",
                 unvisited.len(),
                 100f32 * (unvisited.len() as f32) / (st.nodes.len() as f32));
    }

    println!("\nWriting HTML output: {}", outfile_name);
    let outfile = File::create(outfile_name).unwrap();
    let mut buf_writer = BufWriter::new(outfile);
    let _ = buf_writer.write_all(style_string().as_bytes());
    div.write_html(&mut buf_writer);
    let _ = buf_writer.flush();
    println!("..Wrote HTML output: {}", outfile_name);
}

fn dfs (options:&Options, graph: &Graph, stack:Vec<Rc<String>>) -> (Div, NodeCount) {
    let mut visited:NodeCount = HashMap::new();
    let mut divs:Vec<Div> = Vec::new();
    for n in stack.iter() {
        divs.push(dfs_rec(options, graph, &mut visited, 0, n));
        //if visited.len() > 10000 { break } else { continue }
    }
    (Div{tag:"dfs".to_string(),
         classes:vec![],
         extent:Box::new(divs),
         text: None}, visited)
}

fn cons_of_node_name(node:&Rc<String>) -> String {
    let cons : Vec<&str> = node.trim().split("(").collect();
    assert!(cons.len() > 0 && cons[0] != "");
    let cons : Vec<&str> = cons[0].trim().split("{").collect();
    assert!(cons.len() > 0 && cons[0] != "");
    cons[0].to_string()
}

fn append_classes_of_node(node:&Rc<String>, mut classes:Vec<String>) -> Vec<String> {
    classes.push(cons_of_node_name(node));
    classes
}

fn append_tooltip(options:&Options, node:&Rc<String>, mut extent:Vec<Div>, visited:bool, depth: usize) -> Vec<Div> {
    if options.tips && (!visited || options.tips_visited) {
        let show_full_details_depth = 2;
        extent.insert(0, Div{tag:"tooltip".to_string(),
                             classes:vec![],
                             extent:Box::new(vec![]),
                             text: Some(
                                 format!("{}: {}", depth,
                                         if !visited && depth < show_full_details_depth
                                         { (**node).clone() }
                                         else
                                         { cons_of_node_name(node) }))
        });
        extent
    }
    else {
        extent
    }
}

fn dfs_rec (options:&Options, graph: &Graph,
            visited: &mut NodeCount,
            depth:usize, node:&Rc<String>) -> Div
{
    if visited.get(node) == None {
        visited.insert(node.clone(),1);
        match graph.get(node) {
            None => {
                /* Has no children */
                Div{tag:if false {(**node).clone()} else { "".to_string() },
                    classes:append_classes_of_node(node, vec![
                        "node".to_string(),
                        "no-extent".to_string()
                    ]),
                    extent:Box::new(append_tooltip(options, node, vec![], false, depth)),
                    text:None}
            }
            Some(out) => {
                /* Has children */
                let mut divs = vec![];
                for n in out {
                    divs.push(dfs_rec(options, graph, visited, depth+1, n))
                }
                Div{tag:if false {(**node).clone()} else { "".to_string() },
                    classes:append_classes_of_node(node, vec![
                        "node".to_string()
                    ]),
                    extent:Box::new(append_tooltip(options, node, divs, false, depth)),
                    text:None}
            }
        }
    }
    else {
        // increment counter
        let node_count = visited.get(node).unwrap() + 1;
        visited.insert(node.clone(), node_count);
        // Already visited
        Div{tag:if false {(**node).clone()} else { "".to_string() },
            classes:append_classes_of_node(node, vec![
                "node".to_string(),
                "visited".to_string()
            ]),
            extent:Box::new(append_tooltip(options, node, vec![], true, depth)),
            //extent:Box::new(vec![]),
            text:None}
    }
}

pub fn style_string() -> &'static str {
"
<html>
<head>
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>

<style>
div {
  display: inline
}
body {
  display: inline;
  color: #aa88cc;
  background: #313;
  font-family: sans-serif;
  text-decoration: none;
  padding: 0px;
  margin: 0px;
}
body:visited {
  color: #aa88cc;
}
a {
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
hr {
  display: block;
  float: left;
  clear: both;
  width: 0px;
  border: none;
}

.node {
  color: black;
  display: inline-block;
  border-style: solid;
  border-color: #221133;
  border-width: 1px;
  background: #ffccff;
  font-size: 0px;
  padding: 0px;
  margin: 1px;
  border-radius: 5px;
}

.visited {
  border-width: 2px;
}

.no-extent {
  padding: 3px;
}
.tooltip {
  visibility: hidden;
  background-color: #324;
  border-style: solid;
  border-color: #eeeeee;
  border-width: 1px;
  color: #eae;
  text-align: left;
  padding: 5px 0;
  border-radius: 2px;
  font-size: 12px;
  position: absolute;
  z-index: 1;
}
.node:hover {
  border-color: white;
}
.node:hover > .tooltip {
  visibility: visible;
}

/* Very unique things
Krate
CoherenceOverlapCheck
TypeckBodiesKrate
LateLintCheck
Reachability
Coherence
CrateVariances
PrivacyAccessLevels
AllLocalTraitImpls
CoherenceCheckTrait
ItemVarianceConstraints
*/

/* Less common things. */

.WorkProduct {
  border-width: 3px;
  border-color: blue;
  background: #999;
}
.Hir {
  border-width: 3px;
  border-color: #005555;
  background: #00aaff;
}
.Mir {
  border-width: 3px;
  border-color: #005555;
  background: #00aaff;
}

/* Extremely common things */

.ItemSignature {
  border-color: #550000;
  background: #ff0000;
}
.ItemAttrs {
  border-color: #555500;
  background: #ffff00;
}
.MetaData {
  border-color: #000055;
  background: #0000ff;
}
.DefSpan {
  border-color: #550055;
  background: #ff00ff;
}
.SymbolName {
  border-color: #555555;
  background: #ffffff;
}

</style>
</head>
<body>
"
}
<commit_msg>minor output tweaks<commit_after>extern crate clap;
extern crate adapton_lab;

use std::cmp::Ordering;
use std::rc::Rc;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Write;
use std::io::BufRead;
use std::fs::File;
use std::collections::HashMap;

use adapton_lab::labviz::{Div,WriteHTML};

// Temp: Make this better at some point?
type Graph = HashMap<Rc<String>, Vec<Rc<String>>>;
type NodeSet = HashMap<Rc<String>,()>;
type NodeCount = HashMap<Rc<String>,usize>;
type ConsCount = HashMap<Rc<String>,usize>;

#[derive(Debug)]
struct Options {
    tips: bool,
    tips_visited: bool,
}

impl Options {
    fn new() -> Self {
        Options {
            tips:true,
            tips_visited:false,
        }
    }
}

struct St {
    nodes: NodeSet,
    leaves:NodeSet,
    roots: NodeSet,
    graph: Graph,
    edges: Vec<(Rc<String>, Rc<String>)>,
    rev_graph:Graph,
    cons_count:ConsCount,
}

impl St {
    fn new() -> Self {
        St {
            nodes: HashMap::new(),
            leaves:HashMap::new(),
            roots: HashMap::new(),
            graph: HashMap::new(),
            edges: Vec::new(),
            rev_graph:HashMap::new(),
            cons_count:HashMap::new(),
        }
    }
    fn process_nodes(self:&mut Self) {
        self.leaves=HashMap::new();
        self.roots=HashMap::new();
        for (node,_) in self.nodes.iter() {

            let cons = Rc::new(cons_of_node_name(&node));
            let cons_count = (self.cons_count.get(&cons).unwrap_or(&0)).clone();
            self.cons_count.insert(cons, cons_count + 1);

            match self.graph.get(node) {
                None => { self.leaves.insert(node.clone(),()); },
                Some(_out) => {}
            };
            match self.rev_graph.get(node) {
                None => { self.roots.insert(node.clone(),()); },
                Some(_out) => {}
            };
        }
    }
    fn add_edge(self:&mut Self, src:Rc<String>, tgt:Rc<String>) {
        self.nodes.insert(src.clone(), ());
        self.nodes.insert(tgt.clone(), ());
        let edge = if false {
            (src, tgt)
        } else {
            (tgt, src)
        };
        self.edges.push(edge.clone());

        let inserted : bool = match self.graph.get_mut(&edge.0) {
            None => { false }
            Some(out) => {
                out.push(edge.1.clone());
                true
            },
        };
        if !inserted {
            self.graph.insert(edge.0.clone(), vec![ edge.1.clone() ]);
        };

        let inserted : bool = match self.rev_graph.get_mut(&edge.1) {
            None => { false }
            Some(out) => {
                out.push(edge.0.clone());
                true
            },
        };
        if !inserted {
            self.rev_graph.insert(edge.1, vec![ edge.0 ]);
        };
    }
}

fn main() {
    let mut options = Options::new();
    let args = clap::App::new("dep-viz")
        .version("0.1")
        .author("Matthew Hammer <matthew.hammer@colorado.edu>")
        .about("Consumes Rustc dependency information; Produces visualizations as HTML via a depth-first traversal.")
        .args_from_usage("\
                --no-tips            'smaller HTML files: do not show tips for nodes on hover'
                --tips-visited       'even larger HTML files: show tips even for visited nodes'
            -i, --infile=[infile]    'name for input file'
            -o, --outfile=[outfile]  'name for output file'
            -c, --countfile=[countfile]  'name for output file with DepNode constuctor counts'
"
        )
    .get_matches();
    let infile_name = args.value_of("infile").unwrap_or("dep_graph.txt");
    let outfile_name = args.value_of("outfile").unwrap_or("dep_graph.html");

    options.tips = !(args.is_present("no-tips"));
    options.tips_visited = args.is_present("tips-visited");

    println!("{:?}\n", options);

    println!("Reading input file: {}", infile_name);
    let f = File::open(infile_name).unwrap();
    let file = BufReader::new(&f);
    let mut st = St::new();
    for (_num, line) in file.lines().enumerate() {
        let line = line.unwrap();
        let v: Vec<&str> = line.split(" -> ").map(|s|s.trim()).collect();
        assert_eq!(v.len(), 2);
        st.add_edge(Rc::new(v[0].to_string()),
                    Rc::new(v[1].to_string()));
    }
    println!("...Read input file: {}", infile_name);
    st.process_nodes();
    println!("  Found {} edges", st.edges.len());
    println!("  Found {} nodes", st.nodes.len());
    println!("    Found {} root nodes", st.roots.len());
    println!("    Found {} leaf nodes", st.leaves.len());

    println!("\nCounting DepNode constructor frequencies...");
    let count_outfile_name = args.value_of("countfile").unwrap_or("dep_graph.counts.txt");
    let count_outfile = File::create(count_outfile_name).unwrap();
    let mut buf_writer = BufWriter::new(count_outfile);
    let mut counts = vec![];
    for (cons,cons_count) in st.cons_count { counts.push((cons, cons_count)) }
    counts.sort_by(|&(_,c1),&(_,c2)| if c1 <= c2 { Ordering::Less } else { Ordering::Greater } );
    let counts_len = counts.len();
    for (cons,cons_count) in counts {
        let _ = write!(&mut buf_writer, "{:8} {}\n", cons_count, cons);
    }
    let _ = buf_writer.flush();
    println!("...Wrote DepNode constructor frequencies: {}", count_outfile_name);
    println!("  Found {} distinct node constructors; Each becomes a CSS class in HTML output.", counts_len);

    println!("\nTraversing graph (DFS)...");
    let mut stack = vec![];
    for (node,_) in st.roots.iter() {
        stack.push(node.clone());
    }

    let (div, visited) = dfs(&options, &st.graph, stack);
    println!("...Traversed graph.");
    println!("  Visited {} nodes, {:.1}% of total", visited.len(),
             100f32 * (visited.len() as f32) / (st.nodes.len() as f32));

    if false {
        println!("\nFinding unvisited graph nodes...");
        let mut unvisited : NodeSet = HashMap::new();
        for (node, _) in st.nodes.iter() {
            if visited.get(node) == None {
                unvisited.insert(node.clone(), ());
            }
        }
        assert_eq!(visited.len() + unvisited.len(),
                   st.nodes.len());
        println!("...Found {} unvisited graph nodes, {:.1}% of total nodes.",
                 unvisited.len(),
                 100f32 * (unvisited.len() as f32) / (st.nodes.len() as f32));
    }

    println!("\nWriting HTML output: {}", outfile_name);
    let outfile = File::create(outfile_name).unwrap();
    let mut buf_writer = BufWriter::new(outfile);
    let _ = buf_writer.write_all(style_string().as_bytes());
    div.write_html(&mut buf_writer);
    let _ = buf_writer.flush();
    println!("..Wrote HTML output: {}", outfile_name);
}

fn dfs (options:&Options, graph: &Graph, stack:Vec<Rc<String>>) -> (Div, NodeCount) {
    let mut visited:NodeCount = HashMap::new();
    let mut divs:Vec<Div> = Vec::new();
    for n in stack.iter() {
        divs.push(dfs_rec(options, graph, &mut visited, 0, n));
        //if visited.len() > 10000 { break } else { continue }
    }
    (Div{tag:"dfs".to_string(),
         classes:vec![],
         extent:Box::new(divs),
         text: None}, visited)
}

fn cons_of_node_name(node:&Rc<String>) -> String {
    let cons : Vec<&str> = node.trim().split("(").collect();
    assert!(cons.len() > 0 && cons[0] != "");
    let cons : Vec<&str> = cons[0].trim().split("{").collect();
    assert!(cons.len() > 0 && cons[0] != "");
    cons[0].to_string()
}

fn append_classes_of_node(node:&Rc<String>, mut classes:Vec<String>) -> Vec<String> {
    classes.push(cons_of_node_name(node));
    classes
}

fn append_tooltip(options:&Options, node:&Rc<String>, mut extent:Vec<Div>, visited:bool, depth: usize) -> Vec<Div> {
    if options.tips && (!visited || options.tips_visited) {
        let show_full_details_depth = 2;
        extent.insert(0, Div{tag:"tooltip".to_string(),
                             classes:vec![],
                             extent:Box::new(vec![]),
                             text: Some(
                                 format!("{}: {}", depth,
                                         if !visited && depth < show_full_details_depth
                                         { (**node).clone() }
                                         else
                                         { cons_of_node_name(node) }))
        });
        extent
    }
    else {
        extent
    }
}

fn dfs_rec (options:&Options, graph: &Graph,
            visited: &mut NodeCount,
            depth:usize, node:&Rc<String>) -> Div
{
    if visited.get(node) == None {
        visited.insert(node.clone(),1);
        match graph.get(node) {
            None => {
                /* Has no children */
                Div{tag:if false {(**node).clone()} else { "".to_string() },
                    classes:append_classes_of_node(node, vec![
                        "node".to_string(),
                        "no-extent".to_string()
                    ]),
                    extent:Box::new(append_tooltip(options, node, vec![], false, depth)),
                    text:None}
            }
            Some(out) => {
                /* Has children */
                let mut divs = vec![];
                for n in out {
                    divs.push(dfs_rec(options, graph, visited, depth+1, n))
                }
                Div{tag:if false {(**node).clone()} else { "".to_string() },
                    classes:append_classes_of_node(node, vec![
                        "node".to_string()
                    ]),
                    extent:Box::new(append_tooltip(options, node, divs, false, depth)),
                    text:None}
            }
        }
    }
    else {
        // increment counter
        let node_count = visited.get(node).unwrap() + 1;
        visited.insert(node.clone(), node_count);
        // Already visited
        Div{tag:if false {(**node).clone()} else { "".to_string() },
            classes:append_classes_of_node(node, vec![
                "node".to_string(),
                "visited".to_string()
            ]),
            extent:Box::new(append_tooltip(options, node, vec![], true, depth)),
            //extent:Box::new(vec![]),
            text:None}
    }
}

pub fn style_string() -> &'static str {
"
<html>
<head>
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>

<style>
div {
  display: inline
}
body {
  display: inline;
  color: #aa88cc;
  background: #313;
  font-family: sans-serif;
  text-decoration: none;
  padding: 0px;
  margin: 0px;
}
body:visited {
  color: #aa88cc;
}
a {
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
hr {
  display: block;
  float: left;
  clear: both;
  width: 0px;
  border: none;
}

.node {
  color: black;
  display: inline-block;
  border-style: solid;
  border-color: #221133;
  border-width: 1px;
  background: #ffccff;
  font-size: 0px;
  padding: 0px;
  margin: 1px;
  border-radius: 5px;
}

.visited {
  border-width: 2px;
}

.no-extent {
  padding: 3px;
}
.tooltip {
  visibility: hidden;
  background-color: #324;
  border-style: solid;
  border-color: #eeeeee;
  border-width: 1px;
  color: #eae;
  text-align: left;
  padding: 5px 0;
  border-radius: 2px;
  font-size: 12px;
  position: absolute;
  z-index: 1;
}
.node:hover {
  border-color: white;
}
.node:hover > .tooltip {
  visibility: visible;
}

/* Very unique things
Krate
CoherenceOverlapCheck
TypeckBodiesKrate
LateLintCheck
Reachability
Coherence
CrateVariances
PrivacyAccessLevels
AllLocalTraitImpls
CoherenceCheckTrait
ItemVarianceConstraints
*/

/* Less common things. */

.WorkProduct {
  border-width: 3px;
  border-color: blue;
  background: #999;
}
.Hir {
  border-width: 3px;
  border-color: #005555;
  background: #00aaff;
}
.Mir {
  border-width: 3px;
  border-color: #005555;
  background: #00aaff;
}

/* Extremely common things */

.ItemSignature {
  border-color: #550000;
  background: #ff0000;
}
.ItemAttrs {
  border-color: #555500;
  background: #ffff00;
}
.MetaData {
  border-color: #000055;
  background: #0000ff;
}
.DefSpan {
  border-color: #550055;
  background: #ff00ff;
}
.SymbolName {
  border-color: #555555;
  background: #ffffff;
}

</style>
</head>
<body>
"
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fs::File;
use std::path::{Path, PathBuf};

use api::{CaptureBits, ExternalImageData, ImageDescriptor, TexelRect};
#[cfg(feature = "png")]
use device::ReadPixelsFormat;
use ron;
use serde;

#[derive(Clone)]
pub struct CaptureConfig {
    pub root: PathBuf,
    pub bits: CaptureBits,
    #[cfg(feature = "capture")]
    pretty: ron::ser::PrettyConfig,
}

impl CaptureConfig {
    #[cfg(feature = "capture")]
    pub fn new(root: PathBuf, bits: CaptureBits) -> Self {
        CaptureConfig {
            root,
            bits,
            #[cfg(feature = "capture")]
            pretty: ron::ser::PrettyConfig {
                enumerate_arrays: true,
                .. ron::ser::PrettyConfig::default()
            },
        }
    }

    #[cfg(feature = "capture")]
    pub fn serialize<T, P>(&self, data: &T, name: P)
    where
        T: serde::Serialize,
        P: AsRef<Path>,
    {
        use std::io::Write;

        let ron = ron::ser::to_string_pretty(data, self.pretty.clone())
            .unwrap();
        let path = self.root
            .join(name)
            .with_extension("ron");
        let mut file = File::create(path)
            .unwrap();
        write!(file, "{}\n", ron)
            .unwrap();
    }

    #[cfg(feature = "replay")]
    pub fn deserialize<T, P>(root: &PathBuf, name: P) -> Option<T>
    where
        T: for<'a> serde::Deserialize<'a>,
        P: AsRef<Path>,
    {
        use std::io::Read;

        let mut string = String::new();
        let path = root
            .join(name.as_ref())
            .with_extension("ron");
        File::open(path)
            .ok()?
            .read_to_string(&mut string)
            .unwrap();
        match ron::de::from_str(&string) {
            Ok(out) => Some(out),
            Err(e) => panic!("File {:?} deserilalization failed: {:?}", name.as_ref(), e),
        }
    }

    #[cfg(feature = "png")]
    pub fn save_png(
        path: PathBuf, size: (u32, u32), format: ReadPixelsFormat, data: &[u8],
    ) {
        use api::ImageFormat;
        use png::{BitDepth, ColorType, Encoder, HasParameters};
        use std::io::BufWriter;

        let color_type = match format {
            ReadPixelsFormat::Rgba8 => ColorType::RGBA,
            ReadPixelsFormat::Standard(ImageFormat::BGRA8) => {
                warn!("Unable to swizzle PNG of BGRA8 type");
                ColorType::RGBA
            },
            ReadPixelsFormat::Standard(ImageFormat::R8) => ColorType::Grayscale,
            ReadPixelsFormat::Standard(ImageFormat::RG8) => ColorType::GrayscaleAlpha,
            ReadPixelsFormat::Standard(fm) => {
                error!("Unable to save PNG of {:?}", fm);
                return;
            }
        };
        let w = BufWriter::new(File::create(path).unwrap());
        let mut enc = Encoder::new(w, size.0, size.1);
        enc
            .set(color_type)
            .set(BitDepth::Eight);
        enc
            .write_header()
            .unwrap()
            .write_image_data(&data)
            .unwrap();
    }
}

/// An image that `ResourceCache` is unable to resolve during a capture.
/// The image has to be transferred to `Renderer` and locked with the
/// external image handler to get the actual contents and serialize them.
#[derive(Deserialize, Serialize)]
pub struct ExternalCaptureImage {
    pub short_path: String,
    pub descriptor: ImageDescriptor,
    pub external: ExternalImageData,
}

/// A short description of an external image to be saved separately as
/// "externals/XX.ron", redirecting into a specific texture/blob with
/// the corresponding UV rectangle.
#[derive(Deserialize, Serialize)]
pub struct PlainExternalImage {
    /// Path to the RON file describing the texel data.
    pub data: String,
    /// External image data source.
    pub external: ExternalImageData,
    /// UV sub-rectangle of the image.
    pub uv: TexelRect,
}
<commit_msg>fix spellig of capture loading error<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fs::File;
use std::path::{Path, PathBuf};

use api::{CaptureBits, ExternalImageData, ImageDescriptor, TexelRect};
#[cfg(feature = "png")]
use device::ReadPixelsFormat;
use ron;
use serde;

#[derive(Clone)]
pub struct CaptureConfig {
    pub root: PathBuf,
    pub bits: CaptureBits,
    #[cfg(feature = "capture")]
    pretty: ron::ser::PrettyConfig,
}

impl CaptureConfig {
    #[cfg(feature = "capture")]
    pub fn new(root: PathBuf, bits: CaptureBits) -> Self {
        CaptureConfig {
            root,
            bits,
            #[cfg(feature = "capture")]
            pretty: ron::ser::PrettyConfig {
                enumerate_arrays: true,
                .. ron::ser::PrettyConfig::default()
            },
        }
    }

    #[cfg(feature = "capture")]
    pub fn serialize<T, P>(&self, data: &T, name: P)
    where
        T: serde::Serialize,
        P: AsRef<Path>,
    {
        use std::io::Write;

        let ron = ron::ser::to_string_pretty(data, self.pretty.clone())
            .unwrap();
        let path = self.root
            .join(name)
            .with_extension("ron");
        let mut file = File::create(path)
            .unwrap();
        write!(file, "{}\n", ron)
            .unwrap();
    }

    #[cfg(feature = "replay")]
    pub fn deserialize<T, P>(root: &PathBuf, name: P) -> Option<T>
    where
        T: for<'a> serde::Deserialize<'a>,
        P: AsRef<Path>,
    {
        use std::io::Read;

        let mut string = String::new();
        let path = root
            .join(name.as_ref())
            .with_extension("ron");
        File::open(path)
            .ok()?
            .read_to_string(&mut string)
            .unwrap();
        match ron::de::from_str(&string) {
            Ok(out) => Some(out),
            Err(e) => panic!("File {:?} deserialization failed: {:?}", name.as_ref(), e),
        }
    }

    #[cfg(feature = "png")]
    pub fn save_png(
        path: PathBuf, size: (u32, u32), format: ReadPixelsFormat, data: &[u8],
    ) {
        use api::ImageFormat;
        use png::{BitDepth, ColorType, Encoder, HasParameters};
        use std::io::BufWriter;

        let color_type = match format {
            ReadPixelsFormat::Rgba8 => ColorType::RGBA,
            ReadPixelsFormat::Standard(ImageFormat::BGRA8) => {
                warn!("Unable to swizzle PNG of BGRA8 type");
                ColorType::RGBA
            },
            ReadPixelsFormat::Standard(ImageFormat::R8) => ColorType::Grayscale,
            ReadPixelsFormat::Standard(ImageFormat::RG8) => ColorType::GrayscaleAlpha,
            ReadPixelsFormat::Standard(fm) => {
                error!("Unable to save PNG of {:?}", fm);
                return;
            }
        };
        let w = BufWriter::new(File::create(path).unwrap());
        let mut enc = Encoder::new(w, size.0, size.1);
        enc
            .set(color_type)
            .set(BitDepth::Eight);
        enc
            .write_header()
            .unwrap()
            .write_image_data(&data)
            .unwrap();
    }
}

/// An image that `ResourceCache` is unable to resolve during a capture.
/// The image has to be transferred to `Renderer` and locked with the
/// external image handler to get the actual contents and serialize them.
#[derive(Deserialize, Serialize)]
pub struct ExternalCaptureImage {
    pub short_path: String,
    pub descriptor: ImageDescriptor,
    pub external: ExternalImageData,
}

/// A short description of an external image to be saved separately as
/// "externals/XX.ron", redirecting into a specific texture/blob with
/// the corresponding UV rectangle.
#[derive(Deserialize, Serialize)]
pub struct PlainExternalImage {
    /// Path to the RON file describing the texel data.
    pub data: String,
    /// External image data source.
    pub external: ExternalImageData,
    /// UV sub-rectangle of the image.
    pub uv: TexelRect,
}
<|endoftext|>


<commit_before>use byteorder::{LittleEndian, ReadBytesExt};
use crate::internal::prelude::*;
use audiopus::{
    Channels,
    coder::Decoder as OpusDecoder,
    Result as OpusResult,
};
use parking_lot::Mutex;
use serde_json;
use std::{
    ffi::OsStr,
    fs::File,
    io::{BufReader, ErrorKind as IoErrorKind, Read, Result as IoResult},
    process::{Child, Command, Stdio},
    result::Result as StdResult,
    sync::Arc,
};
use super::{AudioSource, AudioType, DcaError, DcaMetadata, VoiceError, audio};
use log::{debug, warn};

struct ChildContainer(Child);

impl Read for ChildContainer {
    fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
        self.0.stdout.as_mut().unwrap().read(buffer)
    }
}

impl Drop for ChildContainer {
    fn drop (&mut self) {
        if let Err(e) = self.0.kill() {
            debug!("[Voice] Error awaiting child process: {:?}", e);
        }
    }
}

// Since each audio item needs its own decoder, we need to
// work around the fact that OpusDecoders aint sendable.
struct SendDecoder(OpusDecoder);

impl SendDecoder {
    fn decode_float(&mut self, input: &[u8], output: &mut [f32], fec: bool) -> OpusResult<usize> {
        let &mut SendDecoder(ref mut sd) = self;
        sd.decode_float(input, output, fec)
    }
}

unsafe impl Send for SendDecoder {}

struct InputSource<R: Read + Send + 'static> {
    stereo: bool,
    reader: R,
    kind: AudioType,
    decoder: Option<Arc<Mutex<SendDecoder>>>,
}

impl<R: Read + Send> AudioSource for InputSource<R> {
    fn is_stereo(&mut self) -> bool { self.stereo }

    fn get_type(&self) -> AudioType { self.kind }

    fn read_pcm_frame(&mut self, buffer: &mut [i16]) -> Option<usize> {
        for (i, v) in buffer.iter_mut().enumerate() {
            *v = match self.reader.read_i16::<LittleEndian>() {
                Ok(v) => v,
                Err(ref e) => {
                    return if e.kind() == IoErrorKind::UnexpectedEof {
                        Some(i)
                    } else {
                        None
                    }
                },
            }
        }

        Some(buffer.len())
    }

    fn read_opus_frame(&mut self) -> Option<Vec<u8>> {
        match self.reader.read_i16::<LittleEndian>() {
            Ok(size) => {
                if size <= 0 {
                    warn!("Invalid opus frame size: {}", size);
                    return None;
                }

                let mut frame = Vec::with_capacity(size as usize);

                {
                    let reader = self.reader.by_ref();

                    if reader.take(size as u64).read_to_end(&mut frame).is_err() {
                        return None;
                    }
                }

                Some(frame)
            },
            Err(ref e) => if e.kind() == IoErrorKind::UnexpectedEof {
                Some(Vec::new())
            } else {
                None
            },
        }
    }

    fn decode_and_add_opus_frame(&mut self, float_buffer: &mut [f32; 1920], volume: f32) -> Option<usize> {
        let decoder_lock = self.decoder.as_mut()?.clone();
        let frame = self.read_opus_frame()?;
        let mut local_buf = [0f32; 960 * 2];

        let count = {
            let mut decoder = decoder_lock.lock();

            decoder.decode_float(frame.as_slice(), &mut local_buf, false).ok()?
        };

        for (i, float_buffer_element) in float_buffer.iter_mut().enumerate().take(1920) {
            *float_buffer_element += local_buf[i] * volume;
        }

        Some(count)
    }
}

/// Opens an audio file through `ffmpeg` and creates an audio source.
pub fn ffmpeg<P: AsRef<OsStr>>(path: P) -> Result<Box<dyn AudioSource>> {
    _ffmpeg(path.as_ref())
}

fn _ffmpeg(path: &OsStr) -> Result<Box<dyn AudioSource>> {
    // Will fail if the path is not to a file on the fs. Likely a YouTube URI.
    let is_stereo = is_stereo(path).unwrap_or(false);
    let stereo_val = if is_stereo { "2" } else { "1" };

    _ffmpeg_optioned(path, &[
        "-f",
        "s16le",
        "-ac",
        stereo_val,
        "-ar",
        "48000",
        "-acodec",
        "pcm_s16le",
        "-",
    ], Some(is_stereo))
}

/// Opens an audio file through `ffmpeg` and creates an audio source, with
/// user-specified arguments to pass to ffmpeg.
///
/// Note that this does _not_ build on the arguments passed by the [`ffmpeg`]
/// function.
///
/// # Examples
///
/// Pass options to create a custom ffmpeg streamer:
///
/// ```rust,no_run
/// use serenity::voice;
///
/// let stereo_val = "2";
///
/// let streamer = voice::ffmpeg_optioned("./some_file.mp3", &[
///     "-f",
///     "s16le",
///     "-ac",
///     stereo_val,
///     "-ar",
///     "48000",
///     "-acodec",
///     "pcm_s16le",
///     "-",
/// ]);
pub fn ffmpeg_optioned<P: AsRef<OsStr>>(
    path: P,
    args: &[&str],
) -> Result<Box<dyn AudioSource>> {
    _ffmpeg_optioned(path.as_ref(), args, None)
}

fn _ffmpeg_optioned(path: &OsStr, args: &[&str], is_stereo_known: Option<bool>) -> Result<Box<dyn AudioSource>> {
    let is_stereo = is_stereo_known
        .or_else(|| is_stereo(path).ok())
        .unwrap_or(false);

    let command = Command::new("ffmpeg")
        .arg("-i")
        .arg(path)
        .args(args)
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    Ok(pcm(is_stereo, ChildContainer(command)))
}

/// Creates a streamed audio source from a DCA file.
/// Currently only accepts the DCA1 format.
pub fn dca<P: AsRef<OsStr>>(path: P) -> StdResult<Box<dyn AudioSource>, DcaError> {
    _dca(path.as_ref())
}

fn _dca(path: &OsStr) -> StdResult<Box<dyn AudioSource>, DcaError> {
    let file = File::open(path).map_err(DcaError::IoError)?;

    let mut reader = BufReader::new(file);

    let mut header = [0u8; 4];

    // Read in the magic number to verify it's a DCA file.
    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    if header != b"DCA1"[..] {
        return Err(DcaError::InvalidHeader);
    }

    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    let size = (&header[..]).read_i32::<LittleEndian>().unwrap();

    // Sanity check
    if size < 2 {
        return Err(DcaError::InvalidSize(size));
    }

    let mut raw_json = Vec::with_capacity(size as usize);

    {
        let json_reader = reader.by_ref();
        json_reader
            .take(size as u64)
            .read_to_end(&mut raw_json)
            .map_err(DcaError::IoError)?;
    }

    let metadata = serde_json::from_slice::<DcaMetadata>(raw_json.as_slice())
        .map_err(DcaError::InvalidMetadata)?;

    Ok(opus(metadata.is_stereo(), reader))
}

/// Creates an Opus audio source. This makes certain assumptions: namely, that the input stream
/// is composed ONLY of opus frames of the variety that Discord expects.
///
/// If you want to decode a `.opus` file, use [`ffmpeg`]
///
/// [`ffmpeg`]: fn.ffmpeg.html
pub fn opus<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<dyn AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader,
        kind: AudioType::Opus,
        decoder: Some(
            Arc::new(Mutex::new(
                // We always want to decode *to* stereo, for mixing reasons.
                SendDecoder(OpusDecoder::new(audio::SAMPLE_RATE, Channels::Stereo).unwrap())
            ))
        ),
    })
}

/// Creates a PCM audio source.
pub fn pcm<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<dyn AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader,
        kind: AudioType::Pcm,
        decoder: None,
    })
}

/// Creates a streamed audio source with `youtube-dl` and `ffmpeg`.
pub fn ytdl(uri: &str) -> Result<Box<dyn AudioSource>> {
    let args = [
        "-f",
        "webm[abr>0]/bestaudio/best",
        "--no-playlist",
        "--print-json",
        "--skip-download",
        uri,
    ];

    let out = Command::new("youtube-dl")
        .args(&args)
        .stdin(Stdio::null())
        .output()?;

    if !out.status.success() {
        return Err(Error::Voice(VoiceError::YouTubeDLRun(out)));
    }

    let value = serde_json::from_reader(&out.stdout[..])?;
    let mut obj = match value {
        Value::Object(obj) => obj,
        other => return Err(Error::Voice(VoiceError::YouTubeDLProcessing(other))),
    };

    let uri = match obj.remove("url") {
        Some(v) => match v {
            Value::String(uri) => uri,
            other => return Err(Error::Voice(VoiceError::YouTubeDLUrl(other))),
        },
        None => return Err(Error::Voice(VoiceError::YouTubeDLUrl(Value::Object(obj)))),
    };

    ffmpeg(&uri)
}

fn is_stereo(path: &OsStr) -> Result<bool> {
    let args = ["-v", "quiet", "-of", "json", "-show-streams", "-i"];

    let out = Command::new("ffprobe")
        .args(&args)
        .arg(path)
        .stdin(Stdio::null())
        .output()?;

    let value: Value = serde_json::from_reader(&out.stdout[..])?;

    let streams = value
        .as_object()
        .and_then(|m| m.get("streams"))
        .and_then(|v| v.as_array())
        .ok_or(Error::Voice(VoiceError::Streams))?;

    let check = streams.iter().any(|stream| {
        let channels = stream
            .as_object()
            .and_then(|m| m.get("channels").and_then(|v| v.as_i64()));

        channels == Some(2)
    });

    Ok(check)
}
<commit_msg>Pipe youtube-dl to ffmpeg directly (#574)<commit_after>use byteorder::{LittleEndian, ReadBytesExt};
use crate::internal::prelude::*;
use audiopus::{
    Channels,
    coder::Decoder as OpusDecoder,
    Result as OpusResult,
};
use parking_lot::Mutex;
use serde_json;
use std::{
    ffi::OsStr,
    fs::File,
    io::{BufReader, ErrorKind as IoErrorKind, Read, Result as IoResult},
    process::{Child, Command, Stdio},
    result::Result as StdResult,
    sync::Arc,
};
use super::{AudioSource, AudioType, DcaError, DcaMetadata, VoiceError, audio};
use log::{debug, warn};
use crate::prelude::SerenityError;

struct ChildContainer(Child);

impl Read for ChildContainer {
    fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
        self.0.stdout.as_mut().unwrap().read(buffer)
    }
}

impl Drop for ChildContainer {
    fn drop (&mut self) {
        if let Err(e) = self.0.kill() {
            debug!("[Voice] Error awaiting child process: {:?}", e);
        }
    }
}

// Since each audio item needs its own decoder, we need to
// work around the fact that OpusDecoders aint sendable.
struct SendDecoder(OpusDecoder);

impl SendDecoder {
    fn decode_float(&mut self, input: &[u8], output: &mut [f32], fec: bool) -> OpusResult<usize> {
        let &mut SendDecoder(ref mut sd) = self;
        sd.decode_float(input, output, fec)
    }
}

unsafe impl Send for SendDecoder {}

struct InputSource<R: Read + Send + 'static> {
    stereo: bool,
    reader: R,
    kind: AudioType,
    decoder: Option<Arc<Mutex<SendDecoder>>>,
}

impl<R: Read + Send> AudioSource for InputSource<R> {
    fn is_stereo(&mut self) -> bool { self.stereo }

    fn get_type(&self) -> AudioType { self.kind }

    fn read_pcm_frame(&mut self, buffer: &mut [i16]) -> Option<usize> {
        for (i, v) in buffer.iter_mut().enumerate() {
            *v = match self.reader.read_i16::<LittleEndian>() {
                Ok(v) => v,
                Err(ref e) => {
                    return if e.kind() == IoErrorKind::UnexpectedEof {
                        Some(i)
                    } else {
                        None
                    }
                },
            }
        }

        Some(buffer.len())
    }

    fn read_opus_frame(&mut self) -> Option<Vec<u8>> {
        match self.reader.read_i16::<LittleEndian>() {
            Ok(size) => {
                if size <= 0 {
                    warn!("Invalid opus frame size: {}", size);
                    return None;
                }

                let mut frame = Vec::with_capacity(size as usize);

                {
                    let reader = self.reader.by_ref();

                    if reader.take(size as u64).read_to_end(&mut frame).is_err() {
                        return None;
                    }
                }

                Some(frame)
            },
            Err(ref e) => if e.kind() == IoErrorKind::UnexpectedEof {
                Some(Vec::new())
            } else {
                None
            },
        }
    }

    fn decode_and_add_opus_frame(&mut self, float_buffer: &mut [f32; 1920], volume: f32) -> Option<usize> {
        let decoder_lock = self.decoder.as_mut()?.clone();
        let frame = self.read_opus_frame()?;
        let mut local_buf = [0f32; 960 * 2];

        let count = {
            let mut decoder = decoder_lock.lock();

            decoder.decode_float(frame.as_slice(), &mut local_buf, false).ok()?
        };

        for (i, float_buffer_element) in float_buffer.iter_mut().enumerate().take(1920) {
            *float_buffer_element += local_buf[i] * volume;
        }

        Some(count)
    }
}

/// Opens an audio file through `ffmpeg` and creates an audio source.
pub fn ffmpeg<P: AsRef<OsStr>>(path: P) -> Result<Box<dyn AudioSource>> {
    _ffmpeg(path.as_ref())
}

fn _ffmpeg(path: &OsStr) -> Result<Box<dyn AudioSource>> {
    // Will fail if the path is not to a file on the fs. Likely a YouTube URI.
    let is_stereo = is_stereo(path).unwrap_or(false);
    let stereo_val = if is_stereo { "2" } else { "1" };

    _ffmpeg_optioned(path, &[
        "-f",
        "s16le",
        "-ac",
        stereo_val,
        "-ar",
        "48000",
        "-acodec",
        "pcm_s16le",
        "-",
    ], Some(is_stereo))
}

/// Opens an audio file through `ffmpeg` and creates an audio source, with
/// user-specified arguments to pass to ffmpeg.
///
/// Note that this does _not_ build on the arguments passed by the [`ffmpeg`]
/// function.
///
/// # Examples
///
/// Pass options to create a custom ffmpeg streamer:
///
/// ```rust,no_run
/// use serenity::voice;
///
/// let stereo_val = "2";
///
/// let streamer = voice::ffmpeg_optioned("./some_file.mp3", &[
///     "-f",
///     "s16le",
///     "-ac",
///     stereo_val,
///     "-ar",
///     "48000",
///     "-acodec",
///     "pcm_s16le",
///     "-",
/// ]);
pub fn ffmpeg_optioned<P: AsRef<OsStr>>(
    path: P,
    args: &[&str],
) -> Result<Box<dyn AudioSource>> {
    _ffmpeg_optioned(path.as_ref(), args, None)
}

fn _ffmpeg_optioned(path: &OsStr, args: &[&str], is_stereo_known: Option<bool>) -> Result<Box<dyn AudioSource>> {
    let is_stereo = is_stereo_known
        .or_else(|| is_stereo(path).ok())
        .unwrap_or(false);

    let command = Command::new("ffmpeg")
        .arg("-i")
        .arg(path)
        .args(args)
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    Ok(pcm(is_stereo, ChildContainer(command)))
}

/// Creates a streamed audio source from a DCA file.
/// Currently only accepts the DCA1 format.
pub fn dca<P: AsRef<OsStr>>(path: P) -> StdResult<Box<dyn AudioSource>, DcaError> {
    _dca(path.as_ref())
}

fn _dca(path: &OsStr) -> StdResult<Box<dyn AudioSource>, DcaError> {
    let file = File::open(path).map_err(DcaError::IoError)?;

    let mut reader = BufReader::new(file);

    let mut header = [0u8; 4];

    // Read in the magic number to verify it's a DCA file.
    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    if header != b"DCA1"[..] {
        return Err(DcaError::InvalidHeader);
    }

    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    let size = (&header[..]).read_i32::<LittleEndian>().unwrap();

    // Sanity check
    if size < 2 {
        return Err(DcaError::InvalidSize(size));
    }

    let mut raw_json = Vec::with_capacity(size as usize);

    {
        let json_reader = reader.by_ref();
        json_reader
            .take(size as u64)
            .read_to_end(&mut raw_json)
            .map_err(DcaError::IoError)?;
    }

    let metadata = serde_json::from_slice::<DcaMetadata>(raw_json.as_slice())
        .map_err(DcaError::InvalidMetadata)?;

    Ok(opus(metadata.is_stereo(), reader))
}

/// Creates an Opus audio source. This makes certain assumptions: namely, that the input stream
/// is composed ONLY of opus frames of the variety that Discord expects.
///
/// If you want to decode a `.opus` file, use [`ffmpeg`]
///
/// [`ffmpeg`]: fn.ffmpeg.html
pub fn opus<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<dyn AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader,
        kind: AudioType::Opus,
        decoder: Some(
            Arc::new(Mutex::new(
                // We always want to decode *to* stereo, for mixing reasons.
                SendDecoder(OpusDecoder::new(audio::SAMPLE_RATE, Channels::Stereo).unwrap())
            ))
        ),
    })
}

/// Creates a PCM audio source.
pub fn pcm<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<dyn AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader,
        kind: AudioType::Pcm,
        decoder: None,
    })
}

/// Creates a streamed audio source with `youtube-dl` and `ffmpeg`.
pub fn ytdl(uri: &str) -> Result<Box<dyn AudioSource>> {
    let ytdl_args = [
        "-f",
        "webm[abr>0]/bestaudio/best",
        "--no-playlist",
        "--ignore-config",
        uri,
        "-o",
        "-"
    ];

    let ffmpeg_args = [
        "-f",
        "s16le",
        "-ac",
        "2",
        "-ar",
        "48000",
        "-acodec",
        "pcm_s16le",
        "-",
    ];

    let youtube_dl = Command::new("youtube-dl")
        .args(&ytdl_args)
        .stdin(Stdio::null())
        .stderr(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    let ffmpeg = Command::new("ffmpeg")
        .arg("-re")
        .arg("-i")
        .arg("-")
        .args(&ffmpeg_args)
        .stdin(youtube_dl.stdout.ok_or(SerenityError::Other("Failed to open youtube-dl stdout"))?)
        .stderr(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    Ok(pcm(true, ChildContainer(ffmpeg)))
}

fn is_stereo(path: &OsStr) -> Result<bool> {
    let args = ["-v", "quiet", "-of", "json", "-show-streams", "-i"];

    let out = Command::new("ffprobe")
        .args(&args)
        .arg(path)
        .stdin(Stdio::null())
        .output()?;

    let value: Value = serde_json::from_reader(&out.stdout[..])?;

    let streams = value
        .as_object()
        .and_then(|m| m.get("streams"))
        .and_then(|v| v.as_array())
        .ok_or(Error::Voice(VoiceError::Streams))?;

    let check = streams.iter().any(|stream| {
        let channels = stream
            .as_object()
            .and_then(|m| m.get("channels").and_then(|v| v.as_i64()));

        channels == Some(2)
    });

    Ok(check)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![cfg_attr(feature = "nightly", feature(nonzero))]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments, float_cmp))]

extern crate app_units;
extern crate bincode;
#[macro_use]
extern crate bitflags;
extern crate byteorder;
#[cfg(feature = "nightly")]
extern crate core;
#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(target_os = "windows")]
extern crate dwrote;
extern crate euclid;
#[cfg(feature = "ipc")]
extern crate ipc_channel;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate time;

mod api;
pub mod channel;
mod color;
mod display_item;
mod display_list;
mod font;
mod image;
mod units;

pub use api::*;
pub use color::*;
pub use display_item::*;
pub use display_list::*;
pub use font::*;
pub use image::*;
pub use units::*;
<commit_msg>webrender_api: Ignore unreadable literal warnings from clippy.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![cfg_attr(feature = "nightly", feature(nonzero))]
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, too_many_arguments, unreadable_literal))]

extern crate app_units;
extern crate bincode;
#[macro_use]
extern crate bitflags;
extern crate byteorder;
#[cfg(feature = "nightly")]
extern crate core;
#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(target_os = "windows")]
extern crate dwrote;
extern crate euclid;
#[cfg(feature = "ipc")]
extern crate ipc_channel;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate time;

mod api;
pub mod channel;
mod color;
mod display_item;
mod display_list;
mod font;
mod image;
mod units;

pub use api::*;
pub use color::*;
pub use display_item::*;
pub use display_list::*;
pub use font::*;
pub use image::*;
pub use units::*;
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * 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::f32;

use super::{Drawable, MeasureMode, BoundingBox, AbsoluteLayout, AbsoluteLayoutParams, Glyph, GlyphIndex, Text};
use ::platform::Context;
use ::paint::{Point, Canvas, GlyphConstructionDirection, MathRuler, GlyphAssembly, GlyphAssemblyPart};
use ::props::{Color, Directionality, MathVariant};
use ::elements::Element;

type SymbolReader<T> = fn(&T) -> &str;
type SymmetricReader<T> = fn(&T) -> bool;
type SizeReader<T> = fn(&T) -> f32;
type DirReader<T> = fn(&T) -> &Directionality;
type ColorReader<T> = fn(&T) -> &Color;
type VariantReader<T> = fn(&T) -> &MathVariant;

pub struct Symbol<'a, T: Element + 'a> {
    props: &'a T,
    symbol_reader: SymbolReader<T>,
    math_variant_reader: VariantReader<T>,
    symmetric_reader: SymmetricReader<T>,
    base_size_reader: SizeReader<T>,
    max_size_reader: SizeReader<T>,
    min_size_reader: SizeReader<T>,
    dir_reader: DirReader<T>,
    color_reader: ColorReader<T>,

    bounding_box: BoundingBox,
    layout: AbsoluteLayout<'a>,
}

impl<'a, T: Element + 'a> Drawable for Symbol<'a, T> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        self.layout.draw(canvas, pen_pos);
    }

    fn calculate(&mut self, context: &Context, width_mode: &MeasureMode, height_mode: &MeasureMode) {
        let base_size = (self.base_size_reader)(self.props);
        let symbol = (self.symbol_reader)(self.props);
        let ruler = context.platform().get_math_ruler(self.props, base_size);

        let chars: Vec<char> = symbol.chars().collect();

        if chars.len() > 1 {
            self.set_text(context);
            return;
        }

        let symbol = chars[0] as u32;

        if let MeasureMode::UpTo(height) = *height_mode {
            let stretch_dir = GlyphConstructionDirection::Vertical;
            let stretched_size = base_size
                .max((self.min_size_reader)(self.props))
                .min((self.max_size_reader)(self.props))
                .max(height);

            if self.try_stretch_symbol(context,symbol, ruler, stretched_size, &stretch_dir) {
                return;
            }
        }

        if let MeasureMode::UpTo(width) = *width_mode {
            let stretch_dir = GlyphConstructionDirection::Horizontal;
            let stretched_size = base_size
                .max((self.min_size_reader)(self.props))
                .min((self.max_size_reader)(self.props))
                .max(width);

            if self.try_stretch_symbol(context,symbol, ruler, stretched_size, &stretch_dir) {
                return;
            }
        }

        self.set_single_glyph(context,GlyphIndex::Char(symbol));
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a, T: Element + 'a> Symbol<'a, T> {
    pub fn new(props: &'a T, symbol_reader: SymbolReader<T>, math_variant_reader: VariantReader<T>,
               symmetric_reader: SymmetricReader<T>,
               base_size_reader: SizeReader<T>, max_size_reader: SizeReader<T>,
               min_size_reader: SizeReader<T>, dir_reader: DirReader<T>,
               color_reader: ColorReader<T>) -> Symbol<'a, T> {
        Symbol {
            props,
            symbol_reader,
            math_variant_reader,
            symmetric_reader,
            base_size_reader,
            max_size_reader,
            min_size_reader,
            dir_reader,
            color_reader,
            bounding_box: BoundingBox::default(),
            layout: AbsoluteLayout::new(),
        }
    }

    fn set_single_glyph(&mut self, context: &Context, glyph: GlyphIndex) {
        let glyph = Glyph::new(
            self.props,
            glyph,
            self.base_size_reader,
            self.color_reader,
            self.dir_reader
        );

        self.layout.clear();
        self.layout.add_child(Box::new(glyph),
                              AbsoluteLayoutParams::new(Point::new(0., 0.)));

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        self.bounding_box = self.layout.bounding_box().clone();
    }

    fn try_stretch_symbol(&mut self, context: &Context, symbol: u32, ruler: &MathRuler,
                          stretched_size: f32, stretch_dir: &GlyphConstructionDirection) -> bool {
        let variants = ruler.glyph_variants(symbol, stretch_dir);

        for variant in variants {
            if variant.advance() >= stretched_size {
                self.set_single_glyph(context, GlyphIndex::Index(variant.glyph_index()));
                return true;
            }
        }

        let assembly = ruler.glyph_assembly(symbol, stretch_dir);
        if assembly.parts().len() >= 2 {
            self.set_glyph_assembly(context, assembly, &stretch_dir, stretched_size, ruler);
            return true;
        }

        return false;
    }

    fn set_glyph_assembly(&mut self, context: &Context, assembly: GlyphAssembly,
                          stretch_dir: &GlyphConstructionDirection,
                          stretched_size: f32, ruler: &MathRuler) {
        let min_connector_overlap = ruler.minimum_connector_overlap(stretch_dir);

        let mut num_extenders = 0;
        let mut total_extender_advance = 0f32;

        let mut num_non_extenders = 0;
        let mut total_non_extender_advance = 0f32;

        let mut max_connector_overlap = f32::INFINITY;

        for (index, part) in assembly.parts().iter().enumerate() {
            if part.is_extender() {
                num_extenders += 1;
                total_extender_advance += part.full_advance();
            } else {
                num_non_extenders += 1;
                total_non_extender_advance += part.full_advance();
            }

            if index != 0 {
                max_connector_overlap = max_connector_overlap.min(part.start_connector_length());
            }

            max_connector_overlap = max_connector_overlap.min(part.full_advance());

            if index != assembly.parts().len()-1 {
                max_connector_overlap = max_connector_overlap.min(part.end_connector_length());
            }
        }

        let num_extenders = num_extenders as f32;
        let num_non_extenders = num_non_extenders as f32;

        // Number of times extenders needs to be repeated to get size >= stretched size
        let extender_multiplier = ((stretched_size-total_non_extender_advance-
            min_connector_overlap*(1.-num_non_extenders)) /
            (total_extender_advance-(min_connector_overlap*num_extenders))).ceil();

        // Overlap adjustment to minimize size - stretched_size
        let required_overlap = (total_non_extender_advance + (extender_multiplier * total_extender_advance)
            - stretched_size) /
            (num_non_extenders + (extender_multiplier * num_extenders) - 1.);

        // Never use overlap greater than maximum connector overlap
        let overlap = required_overlap.min(max_connector_overlap);

        #[cfg(debug_assertions)]
        {
            assert!(max_connector_overlap >= min_connector_overlap);

            let size_with_overlap_correction = extender_multiplier*total_extender_advance
                +total_non_extender_advance -
                (overlap*(num_non_extenders+(extender_multiplier*num_extenders)-1.));

            let size_without_overlap_correction = extender_multiplier*total_extender_advance
                +total_non_extender_advance -
                (min_connector_overlap*(num_non_extenders+(extender_multiplier*num_extenders)-1.));

            assert!(size_without_overlap_correction >= stretched_size);

            assert!(size_with_overlap_correction <= size_without_overlap_correction);
        }

        self.layout.clear();

        let mut pen_pos = 0f32;

        // Ordering of parts in GlyphAssembly for vertical construction is from bottom to top
        // and for horizontal construction is from left to right
        match *stretch_dir {
            GlyphConstructionDirection::Vertical => {
                for part in assembly.parts().iter().rev() {
                    pen_pos = self.add_glyph_part_to_layout(part, stretch_dir, overlap,
                                                            extender_multiplier as u32, pen_pos);
                }
            },
            GlyphConstructionDirection::Horizontal => {
                for part in assembly.parts().iter() {
                    pen_pos = self.add_glyph_part_to_layout(part, stretch_dir, overlap,
                                                            extender_multiplier as u32, pen_pos);
                }
            }
        }

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);
        self.bounding_box = self.layout.bounding_box().clone();
    }

    fn add_glyph_part_to_layout(&mut self, part: &GlyphAssemblyPart, stretch_dir: &GlyphConstructionDirection,
                                overlap: f32, multiplier: u32, pen_pos: f32) -> f32 {
        let num_iters = if part.is_extender() {
            multiplier
        } else {
            1
        };

        let mut pen_pos = pen_pos;

        for _ in 0..num_iters {
            self.layout.add_child(
                Box::new(Glyph::new(
                    self.props,
                    GlyphIndex::Index(part.glyph_index()),
                    self.base_size_reader,
                    self.color_reader,
                    self.dir_reader)
                ),
                AbsoluteLayoutParams::new(match *stretch_dir {
                    GlyphConstructionDirection::Horizontal => Point::new(pen_pos, 0.),
                    GlyphConstructionDirection::Vertical => Point::new(0., pen_pos)
                })
            );

            pen_pos += part.full_advance()-overlap;
        }

        pen_pos
    }

    fn set_text(&mut self, context: &Context) {
        let text = Text::new(
            self.props,
            self.symbol_reader,
            self.base_size_reader,
            self.math_variant_reader,
            self.dir_reader,
            self.color_reader
        );

        self.layout.clear();
        self.layout.add_child(Box::new(text),
                              AbsoluteLayoutParams::new(Point::new(0., 0.)));

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        self.bounding_box = self.layout.bounding_box().clone();
    }

}<commit_msg>Fix improper bounding box<commit_after>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * 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::f32;

use super::{Drawable, MeasureMode, BoundingBox, AbsoluteLayout, AbsoluteLayoutParams, Glyph, GlyphIndex, Text};
use ::platform::Context;
use ::paint::{Point, Canvas, GlyphConstructionDirection, MathRuler, GlyphAssembly, GlyphAssemblyPart};
use ::props::{Color, Directionality, MathVariant};
use ::elements::Element;

type SymbolReader<T> = fn(&T) -> &str;
type SymmetricReader<T> = fn(&T) -> bool;
type SizeReader<T> = fn(&T) -> f32;
type DirReader<T> = fn(&T) -> &Directionality;
type ColorReader<T> = fn(&T) -> &Color;
type VariantReader<T> = fn(&T) -> &MathVariant;

pub struct Symbol<'a, T: Element + 'a> {
    props: &'a T,
    symbol_reader: SymbolReader<T>,
    math_variant_reader: VariantReader<T>,
    symmetric_reader: SymmetricReader<T>,
    base_size_reader: SizeReader<T>,
    max_size_reader: SizeReader<T>,
    min_size_reader: SizeReader<T>,
    dir_reader: DirReader<T>,
    color_reader: ColorReader<T>,

    bounding_box: BoundingBox,
    layout: AbsoluteLayout<'a>,
}

impl<'a, T: Element + 'a> Drawable for Symbol<'a, T> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        self.layout.draw(canvas, pen_pos);
    }

    fn calculate(&mut self, context: &Context, width_mode: &MeasureMode, height_mode: &MeasureMode) {
        let base_size = (self.base_size_reader)(self.props);
        let symbol = (self.symbol_reader)(self.props);
        let ruler = context.platform().get_math_ruler(self.props, base_size);

        let chars: Vec<char> = symbol.chars().collect();

        if chars.len() > 1 {
            self.set_text(context);
            return;
        }

        let symbol = chars[0] as u32;

        if let MeasureMode::UpTo(height) = *height_mode {
            let stretch_dir = GlyphConstructionDirection::Vertical;
            let stretched_size = base_size
                .max((self.min_size_reader)(self.props))
                .min((self.max_size_reader)(self.props))
                .max(height);

            if self.try_stretch_symbol(context,symbol, ruler, stretched_size, &stretch_dir) {
                return;
            }
        }

        if let MeasureMode::UpTo(width) = *width_mode {
            let stretch_dir = GlyphConstructionDirection::Horizontal;
            let stretched_size = base_size
                .max((self.min_size_reader)(self.props))
                .min((self.max_size_reader)(self.props))
                .max(width);

            if self.try_stretch_symbol(context,symbol, ruler, stretched_size, &stretch_dir) {
                return;
            }
        }

        self.set_single_glyph(context,GlyphIndex::Char(symbol));
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a, T: Element + 'a> Symbol<'a, T> {
    pub fn new(props: &'a T, symbol_reader: SymbolReader<T>, math_variant_reader: VariantReader<T>,
               symmetric_reader: SymmetricReader<T>,
               base_size_reader: SizeReader<T>, max_size_reader: SizeReader<T>,
               min_size_reader: SizeReader<T>, dir_reader: DirReader<T>,
               color_reader: ColorReader<T>) -> Symbol<'a, T> {
        Symbol {
            props,
            symbol_reader,
            math_variant_reader,
            symmetric_reader,
            base_size_reader,
            max_size_reader,
            min_size_reader,
            dir_reader,
            color_reader,
            bounding_box: BoundingBox::default(),
            layout: AbsoluteLayout::new(),
        }
    }

    fn set_single_glyph(&mut self, context: &Context, glyph: GlyphIndex) {
        let glyph = Glyph::new(
            self.props,
            glyph,
            self.base_size_reader,
            self.color_reader,
            self.dir_reader
        );

        self.layout.clear();
        self.layout.add_child(Box::new(glyph),
                              AbsoluteLayoutParams::new(Point::new(0., 0.)));

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        self.bounding_box = self.layout.iter().next().unwrap().drawable().bounding_box().clone();
    }

    fn try_stretch_symbol(&mut self, context: &Context, symbol: u32, ruler: &MathRuler,
                          stretched_size: f32, stretch_dir: &GlyphConstructionDirection) -> bool {
        let variants = ruler.glyph_variants(symbol, stretch_dir);

        for variant in variants {
            if variant.advance() >= stretched_size {
                self.set_single_glyph(context, GlyphIndex::Index(variant.glyph_index()));
                return true;
            }
        }

        let assembly = ruler.glyph_assembly(symbol, stretch_dir);
        if assembly.parts().len() >= 2 {
            self.set_glyph_assembly(context, assembly, &stretch_dir, stretched_size, ruler);
            return true;
        }

        return false;
    }

    fn set_glyph_assembly(&mut self, context: &Context, assembly: GlyphAssembly,
                          stretch_dir: &GlyphConstructionDirection,
                          stretched_size: f32, ruler: &MathRuler) {
        let min_connector_overlap = ruler.minimum_connector_overlap(stretch_dir);

        let mut num_extenders = 0;
        let mut total_extender_advance = 0f32;

        let mut num_non_extenders = 0;
        let mut total_non_extender_advance = 0f32;

        let mut max_connector_overlap = f32::INFINITY;

        for (index, part) in assembly.parts().iter().enumerate() {
            if part.is_extender() {
                num_extenders += 1;
                total_extender_advance += part.full_advance();
            } else {
                num_non_extenders += 1;
                total_non_extender_advance += part.full_advance();
            }

            if index != 0 {
                max_connector_overlap = max_connector_overlap.min(part.start_connector_length());
            }

            max_connector_overlap = max_connector_overlap.min(part.full_advance());

            if index != assembly.parts().len()-1 {
                max_connector_overlap = max_connector_overlap.min(part.end_connector_length());
            }
        }

        let num_extenders = num_extenders as f32;
        let num_non_extenders = num_non_extenders as f32;

        // Number of times extenders needs to be repeated to get size >= stretched size
        let extender_multiplier = ((stretched_size-total_non_extender_advance-
            min_connector_overlap*(1.-num_non_extenders)) /
            (total_extender_advance-(min_connector_overlap*num_extenders))).ceil();

        // Overlap adjustment to minimize size - stretched_size
        let required_overlap = (total_non_extender_advance + (extender_multiplier * total_extender_advance)
            - stretched_size) /
            (num_non_extenders + (extender_multiplier * num_extenders) - 1.);

        // Never use overlap greater than maximum connector overlap
        let overlap = required_overlap.min(max_connector_overlap);

        #[cfg(debug_assertions)]
        {
            assert!(max_connector_overlap >= min_connector_overlap);

            let size_with_overlap_correction = extender_multiplier*total_extender_advance
                +total_non_extender_advance -
                (overlap*(num_non_extenders+(extender_multiplier*num_extenders)-1.));

            let size_without_overlap_correction = extender_multiplier*total_extender_advance
                +total_non_extender_advance -
                (min_connector_overlap*(num_non_extenders+(extender_multiplier*num_extenders)-1.));

            assert!(size_without_overlap_correction >= stretched_size);

            assert!(size_with_overlap_correction <= size_without_overlap_correction);
        }

        self.layout.clear();

        let mut pen_pos = 0f32;

        // Ordering of parts in GlyphAssembly for vertical construction is from bottom to top
        // and for horizontal construction is from left to right
        match *stretch_dir {
            GlyphConstructionDirection::Vertical => {
                for part in assembly.parts().iter().rev() {
                    pen_pos = self.add_glyph_part_to_layout(part, stretch_dir, overlap,
                                                            extender_multiplier as u32, pen_pos);
                }
            },
            GlyphConstructionDirection::Horizontal => {
                for part in assembly.parts().iter() {
                    pen_pos = self.add_glyph_part_to_layout(part, stretch_dir, overlap,
                                                            extender_multiplier as u32, pen_pos);
                }
            }
        }

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);
        self.bounding_box = self.layout.bounding_box().clone();
    }

    fn add_glyph_part_to_layout(&mut self, part: &GlyphAssemblyPart, stretch_dir: &GlyphConstructionDirection,
                                overlap: f32, multiplier: u32, pen_pos: f32) -> f32 {
        let num_iters = if part.is_extender() {
            multiplier
        } else {
            1
        };

        let mut pen_pos = pen_pos;

        for _ in 0..num_iters {
            self.layout.add_child(
                Box::new(Glyph::new(
                    self.props,
                    GlyphIndex::Index(part.glyph_index()),
                    self.base_size_reader,
                    self.color_reader,
                    self.dir_reader)
                ),
                AbsoluteLayoutParams::new(match *stretch_dir {
                    GlyphConstructionDirection::Horizontal => Point::new(pen_pos, 0.),
                    GlyphConstructionDirection::Vertical => Point::new(0., pen_pos)
                })
            );

            pen_pos += part.full_advance()-overlap;
        }

        pen_pos
    }

    fn set_text(&mut self, context: &Context) {
        let text = Text::new(
            self.props,
            self.symbol_reader,
            self.base_size_reader,
            self.math_variant_reader,
            self.dir_reader,
            self.color_reader
        );

        self.layout.clear();
        self.layout.add_child(Box::new(text),
                              AbsoluteLayoutParams::new(Point::new(0., 0.)));

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        self.bounding_box = self.layout.iter().next().unwrap().drawable().bounding_box().clone();
    }

}<|endoftext|>