<commit_before>// Copyright (C) 2018, Cloudflare, Inc.
// Copyright (C) 2018, Alessandro Ghedini
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#[macro_use]
extern crate log;

use std::net;

use std::collections::HashMap;

use ring::rand::*;

const LOCAL_CONN_ID_LEN: usize = 16;

const MAX_DATAGRAM_SIZE: usize = 1350;

const USAGE: &str = "Usage:
  server [options]
  server -h | --help

Options:
  --listen <addr>   Listen on the given IP:port [default: 127.0.0.1:4433]
  --cert <file>     TLS certificate path [default: examples/cert.crt]
  --key <file>      TLS certificate key path [default: examples/cert.key]
  --root <dir>      Root directory [default: examples/root/]
  --name <str>      Name of the server [default: quic.tech]
  --no-retry        Disable stateless retry.
  -h --help         Show this screen.
";

type ConnMap = HashMap<Vec<u8>, (net::SocketAddr, Box<quiche::Connection>)>;

fn main() -> Result<(), Box<std::error::Error>> {
    let mut buf = [0; 65535];
    let mut out = [0; MAX_DATAGRAM_SIZE];

    env_logger::init();

    let args = docopt::Docopt::new(USAGE)
        .and_then(|dopt| dopt.parse())
        .unwrap_or_else(|e| e.exit());

    let socket = net::UdpSocket::bind(args.get_str("--listen"))?;

    let poll = mio::Poll::new()?;
    let mut events = mio::Events::with_capacity(1024);

    let socket = mio::net::UdpSocket::from_socket(socket)?;
    poll.register(
        &socket,
        mio::Token(0),
        mio::Ready::readable(),
        mio::PollOpt::edge(),
    )?;

    let mut connections = ConnMap::new();

    let mut config = quiche::Config::new(quiche::VERSION_DRAFT18)?;

    config.load_cert_chain_from_pem_file(args.get_str("--cert"))?;
    config.load_priv_key_from_pem_file(args.get_str("--key"))?;

    config.set_application_protos(&[b"h3-18", b"hq-18", b"http/0.9"])?;

    config.set_idle_timeout(30);
    config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
    config.set_initial_max_data(10_000_000);
    config.set_initial_max_stream_data_bidi_local(1_000_000);
    config.set_initial_max_stream_data_bidi_remote(1_000_000);
    config.set_initial_max_stream_data_uni(1_000_000);
    config.set_initial_max_streams_bidi(100);
    config.set_initial_max_streams_uni(5);
    config.set_disable_migration(true);

    loop {
        // TODO: use event loop that properly supports timers
        let timeout = connections.values().filter_map(|(_, c)| c.timeout()).min();

        poll.poll(&mut events, timeout)?;

        'read: loop {
            if events.is_empty() {
                debug!("timed out");

                connections.values_mut().for_each(|(_, c)| c.on_timeout());

                break 'read;
            }

            let (len, src) = match socket.recv_from(&mut buf) {
                Ok(v) => v,

                Err(e) => {
                    if e.kind() == std::io::ErrorKind::WouldBlock {
                        debug!("recv() would block");
                        break 'read;
                    }

                    panic!("recv() failed: {:?}", e);
                },
            };

            debug!("got {} bytes", len);

            let pkt_buf = &mut buf[..len];

            let hdr = match quiche::Header::from_slice(pkt_buf, LOCAL_CONN_ID_LEN)
            {
                Ok(v) => v,

                Err(e) => {
                    error!("Parsing packet header failed: {:?}", e);
                    continue;
                },
            };

            trace!("got packet {:?}", hdr);

            if hdr.ty == quiche::Type::VersionNegotiation {
                error!("Version negotiation invalid on the server");
                continue;
            }

            let (_, conn) = if !connections.contains_key(&hdr.dcid) {
                if hdr.ty != quiche::Type::Initial {
                    error!("Packet is not Initial");
                    continue;
                }

                if hdr.version != quiche::VERSION_DRAFT18 {
                    warn!("Doing version negotiation");

                    let len = quiche::negotiate_version(
                        &hdr.scid, &hdr.dcid, &mut out,
                    )?;

                    let out = &out[..len];

                    socket.send_to(out, &src)?;
                    continue;
                }

                let mut scid = [0; LOCAL_CONN_ID_LEN];
                SystemRandom::new().fill(&mut scid[..])?;

                let mut odcid = None;

                if !args.get_bool("--no-retry") {
                    // Token is always present in Initial packets.
                    let token = hdr.token.as_ref().unwrap();

                    if token.is_empty() {
                        warn!("Doing stateless retry");

                        let new_token = mint_token(&hdr, &src);

                        let len = quiche::retry(
                            &hdr.scid, &hdr.dcid, &scid, &new_token, &mut out,
                        )?;

                        let out = &out[..len];

                        socket.send_to(out, &src)?;
                        continue;
                    }

                    odcid = validate_token(&src, token);

                    if odcid == None {
                        error!("Invalid address validation token");
                        continue;
                    }
                }

                debug!(
                    "New connection: dcid={} scid={}",
                    hex_dump(&hdr.dcid),
                    hex_dump(&hdr.scid)
                );

                let conn = quiche::accept(&hdr.dcid, odcid, &mut config)?;

                connections.insert(hdr.dcid.to_vec(), (src, conn));

                connections.get_mut(&hdr.dcid).unwrap()
            } else {
                connections.get_mut(&hdr.dcid).unwrap()
            };

            // Process potentially coalesced packets.
            let read = match conn.recv(pkt_buf) {
                Ok(v) => v,

                Err(quiche::Error::Done) => {
                    debug!("{} done reading", conn.trace_id());
                    break;
                },

                Err(e) => {
                    error!("{} recv failed: {:?}", conn.trace_id(), e);
                    conn.close(false, e.to_wire(), b"fail")?;
                    break 'read;
                },
            };

            debug!("{} processed {} bytes", conn.trace_id(), read);

            let streams: Vec<u64> = conn.readable().collect();
            for s in streams {
                while let Ok((read, fin)) = conn.stream_recv(s, &mut buf) {
                    debug!("{} received {} bytes", conn.trace_id(), read);

                    let stream_buf = &buf[..read];

                    debug!(
                        "{} stream {} has {} bytes (fin? {})",
                        conn.trace_id(),
                        s,
                        stream_buf.len(),
                        fin
                    );

                    handle_stream(conn, s, stream_buf, args.get_str("--root"));
                }
            }
        }

        for (peer, conn) in connections.values_mut() {
            loop {
                let write = match conn.send(&mut out) {
                    Ok(v) => v,

                    Err(quiche::Error::Done) => {
                        debug!("{} done writing", conn.trace_id());
                        break;
                    },

                    Err(e) => {
                        error!("{} send failed: {:?}", conn.trace_id(), e);
                        conn.close(false, e.to_wire(), b"fail")?;
                        break;
                    },
                };

                // TODO: coalesce packets.
                socket.send_to(&out[..write], &peer)?;

                debug!("{} written {} bytes", conn.trace_id(), write);
            }
        }

        // Garbage collect closed connections.
        connections.retain(|_, (_, ref mut c)| {
            debug!("Collecting garbage");

            if c.is_closed() {
                info!("{} connection collected {:?}", c.trace_id(), c.stats());
            }

            !c.is_closed()
        });
    }
}

fn handle_stream(
    conn: &mut quiche::Connection, stream: u64, buf: &[u8], root: &str,
) {
    if buf.len() > 4 && &buf[..4] == b"GET " {
        let uri = &buf[4..buf.len()];
        let uri = String::from_utf8(uri.to_vec()).unwrap();
        let uri = String::from(uri.lines().next().unwrap());
        let uri = std::path::Path::new(&uri);
        let mut path = std::path::PathBuf::from(root);

        for c in uri.components() {
            if let std::path::Component::Normal(v) = c {
                path.push(v)
            }
        }

        info!(
            "{} got GET request for {:?} on stream {}",
            conn.trace_id(),
            path,
            stream
        );

        let data = std::fs::read(path.as_path())
            .unwrap_or_else(|_| b"Not Found!\r\n".to_vec());

        info!(
            "{} sending response of size {} on stream {}",
            conn.trace_id(),
            data.len(),
            stream
        );

        if let Err(e) = conn.stream_send(stream, &data, true) {
            error!("{} stream send failed {:?}", conn.trace_id(), e);
        }
    }
}

fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> {
    let mut token = Vec::new();

    token.extend_from_slice(b"quiche");

    let addr = match src.ip() {
        std::net::IpAddr::V4(a) => a.octets().to_vec(),
        std::net::IpAddr::V6(a) => a.octets().to_vec(),
    };

    token.extend_from_slice(&addr);
    token.extend_from_slice(&hdr.dcid);

    token
}

fn validate_token<'a>(
    src: &net::SocketAddr, token: &'a [u8],
) -> Option<&'a [u8]> {
    if token.len() < 6 {
        return None;
    }

    if &token[..6] != b"quiche" {
        return None;
    }

    let token = &token[6..];

    let addr = match src.ip() {
        std::net::IpAddr::V4(a) => a.octets().to_vec(),
        std::net::IpAddr::V6(a) => a.octets().to_vec(),
    };

    if token.len() < addr.len() || &token[..addr.len()] != addr.as_slice() {
        return None;
    }

    let token = &token[addr.len()..];

    Some(&token[..])
}

fn hex_dump(buf: &[u8]) -> String {
    let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect();

    vec.join("")
}
<commit_msg>server: do change scid when retry is disabled<commit_after>// Copyright (C) 2018, Cloudflare, Inc.
// Copyright (C) 2018, Alessandro Ghedini
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#[macro_use]
extern crate log;

use std::net;

use std::collections::HashMap;

use ring::rand::*;

const LOCAL_CONN_ID_LEN: usize = 16;

const MAX_DATAGRAM_SIZE: usize = 1350;

const USAGE: &str = "Usage:
  server [options]
  server -h | --help

Options:
  --listen <addr>   Listen on the given IP:port [default: 127.0.0.1:4433]
  --cert <file>     TLS certificate path [default: examples/cert.crt]
  --key <file>      TLS certificate key path [default: examples/cert.key]
  --root <dir>      Root directory [default: examples/root/]
  --name <str>      Name of the server [default: quic.tech]
  --no-retry        Disable stateless retry.
  -h --help         Show this screen.
";

type ConnMap = HashMap<Vec<u8>, (net::SocketAddr, Box<quiche::Connection>)>;

fn main() -> Result<(), Box<std::error::Error>> {
    let mut buf = [0; 65535];
    let mut out = [0; MAX_DATAGRAM_SIZE];

    env_logger::init();

    let args = docopt::Docopt::new(USAGE)
        .and_then(|dopt| dopt.parse())
        .unwrap_or_else(|e| e.exit());

    let socket = net::UdpSocket::bind(args.get_str("--listen"))?;

    let poll = mio::Poll::new()?;
    let mut events = mio::Events::with_capacity(1024);

    let socket = mio::net::UdpSocket::from_socket(socket)?;
    poll.register(
        &socket,
        mio::Token(0),
        mio::Ready::readable(),
        mio::PollOpt::edge(),
    )?;

    let mut connections = ConnMap::new();

    let mut config = quiche::Config::new(quiche::VERSION_DRAFT18)?;

    config.load_cert_chain_from_pem_file(args.get_str("--cert"))?;
    config.load_priv_key_from_pem_file(args.get_str("--key"))?;

    config.set_application_protos(&[b"h3-18", b"hq-18", b"http/0.9"])?;

    config.set_idle_timeout(30);
    config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
    config.set_initial_max_data(10_000_000);
    config.set_initial_max_stream_data_bidi_local(1_000_000);
    config.set_initial_max_stream_data_bidi_remote(1_000_000);
    config.set_initial_max_stream_data_uni(1_000_000);
    config.set_initial_max_streams_bidi(100);
    config.set_initial_max_streams_uni(5);
    config.set_disable_migration(true);

    loop {
        // TODO: use event loop that properly supports timers
        let timeout = connections.values().filter_map(|(_, c)| c.timeout()).min();

        poll.poll(&mut events, timeout)?;

        'read: loop {
            if events.is_empty() {
                debug!("timed out");

                connections.values_mut().for_each(|(_, c)| c.on_timeout());

                break 'read;
            }

            let (len, src) = match socket.recv_from(&mut buf) {
                Ok(v) => v,

                Err(e) => {
                    if e.kind() == std::io::ErrorKind::WouldBlock {
                        debug!("recv() would block");
                        break 'read;
                    }

                    panic!("recv() failed: {:?}", e);
                },
            };

            debug!("got {} bytes", len);

            let pkt_buf = &mut buf[..len];

            let hdr = match quiche::Header::from_slice(pkt_buf, LOCAL_CONN_ID_LEN)
            {
                Ok(v) => v,

                Err(e) => {
                    error!("Parsing packet header failed: {:?}", e);
                    continue;
                },
            };

            trace!("got packet {:?}", hdr);

            if hdr.ty == quiche::Type::VersionNegotiation {
                error!("Version negotiation invalid on the server");
                continue;
            }

            let (_, conn) = if !connections.contains_key(&hdr.dcid) {
                if hdr.ty != quiche::Type::Initial {
                    error!("Packet is not Initial");
                    continue;
                }

                if hdr.version != quiche::VERSION_DRAFT18 {
                    warn!("Doing version negotiation");

                    let len = quiche::negotiate_version(
                        &hdr.scid, &hdr.dcid, &mut out,
                    )?;

                    let out = &out[..len];

                    socket.send_to(out, &src)?;
                    continue;
                }

                let mut scid = [0; LOCAL_CONN_ID_LEN];
                SystemRandom::new().fill(&mut scid[..])?;

                let mut odcid = None;

                if !args.get_bool("--no-retry") {
                    // Token is always present in Initial packets.
                    let token = hdr.token.as_ref().unwrap();

                    if token.is_empty() {
                        warn!("Doing stateless retry");

                        let new_token = mint_token(&hdr, &src);

                        let len = quiche::retry(
                            &hdr.scid, &hdr.dcid, &scid, &new_token, &mut out,
                        )?;

                        let out = &out[..len];

                        socket.send_to(out, &src)?;
                        continue;
                    }

                    odcid = validate_token(&src, token);

                    if odcid == None {
                        error!("Invalid address validation token");
                        continue;
                    }

                    scid.copy_from_slice(&hdr.dcid);
                }

                debug!(
                    "New connection: dcid={} scid={}",
                    hex_dump(&hdr.dcid),
                    hex_dump(&scid)
                );

                let conn = quiche::accept(&scid, odcid, &mut config)?;

                connections.insert(scid.to_vec(), (src, conn));

                connections.get_mut(&scid[..]).unwrap()
            } else {
                connections.get_mut(&hdr.dcid).unwrap()
            };

            // Process potentially coalesced packets.
            let read = match conn.recv(pkt_buf) {
                Ok(v) => v,

                Err(quiche::Error::Done) => {
                    debug!("{} done reading", conn.trace_id());
                    break;
                },

                Err(e) => {
                    error!("{} recv failed: {:?}", conn.trace_id(), e);
                    conn.close(false, e.to_wire(), b"fail")?;
                    break 'read;
                },
            };

            debug!("{} processed {} bytes", conn.trace_id(), read);

            let streams: Vec<u64> = conn.readable().collect();
            for s in streams {
                while let Ok((read, fin)) = conn.stream_recv(s, &mut buf) {
                    debug!("{} received {} bytes", conn.trace_id(), read);

                    let stream_buf = &buf[..read];

                    debug!(
                        "{} stream {} has {} bytes (fin? {})",
                        conn.trace_id(),
                        s,
                        stream_buf.len(),
                        fin
                    );

                    handle_stream(conn, s, stream_buf, args.get_str("--root"));
                }
            }
        }

        for (peer, conn) in connections.values_mut() {
            loop {
                let write = match conn.send(&mut out) {
                    Ok(v) => v,

                    Err(quiche::Error::Done) => {
                        debug!("{} done writing", conn.trace_id());
                        break;
                    },

                    Err(e) => {
                        error!("{} send failed: {:?}", conn.trace_id(), e);
                        conn.close(false, e.to_wire(), b"fail")?;
                        break;
                    },
                };

                // TODO: coalesce packets.
                socket.send_to(&out[..write], &peer)?;

                debug!("{} written {} bytes", conn.trace_id(), write);
            }
        }

        // Garbage collect closed connections.
        connections.retain(|_, (_, ref mut c)| {
            debug!("Collecting garbage");

            if c.is_closed() {
                info!("{} connection collected {:?}", c.trace_id(), c.stats());
            }

            !c.is_closed()
        });
    }
}

fn handle_stream(
    conn: &mut quiche::Connection, stream: u64, buf: &[u8], root: &str,
) {
    if buf.len() > 4 && &buf[..4] == b"GET " {
        let uri = &buf[4..buf.len()];
        let uri = String::from_utf8(uri.to_vec()).unwrap();
        let uri = String::from(uri.lines().next().unwrap());
        let uri = std::path::Path::new(&uri);
        let mut path = std::path::PathBuf::from(root);

        for c in uri.components() {
            if let std::path::Component::Normal(v) = c {
                path.push(v)
            }
        }

        info!(
            "{} got GET request for {:?} on stream {}",
            conn.trace_id(),
            path,
            stream
        );

        let data = std::fs::read(path.as_path())
            .unwrap_or_else(|_| b"Not Found!\r\n".to_vec());

        info!(
            "{} sending response of size {} on stream {}",
            conn.trace_id(),
            data.len(),
            stream
        );

        if let Err(e) = conn.stream_send(stream, &data, true) {
            error!("{} stream send failed {:?}", conn.trace_id(), e);
        }
    }
}

fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> {
    let mut token = Vec::new();

    token.extend_from_slice(b"quiche");

    let addr = match src.ip() {
        std::net::IpAddr::V4(a) => a.octets().to_vec(),
        std::net::IpAddr::V6(a) => a.octets().to_vec(),
    };

    token.extend_from_slice(&addr);
    token.extend_from_slice(&hdr.dcid);

    token
}

fn validate_token<'a>(
    src: &net::SocketAddr, token: &'a [u8],
) -> Option<&'a [u8]> {
    if token.len() < 6 {
        return None;
    }

    if &token[..6] != b"quiche" {
        return None;
    }

    let token = &token[6..];

    let addr = match src.ip() {
        std::net::IpAddr::V4(a) => a.octets().to_vec(),
        std::net::IpAddr::V6(a) => a.octets().to_vec(),
    };

    if token.len() < addr.len() || &token[..addr.len()] != addr.as_slice() {
        return None;
    }

    let token = &token[addr.len()..];

    Some(&token[..])
}

fn hex_dump(buf: &[u8]) -> String {
    let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect();

    vec.join("")
}
<|endoftext|>


<commit_before>extern crate syntect;
use syntect::parsing::SyntaxSet;
use syntect::highlighting::{ThemeSet, Style};
use syntect::util::as_24_bit_terminal_escaped;
use syntect::easy::HighlightFile;

use std::io::BufRead;

fn main() {
    let ss = SyntaxSet::load_defaults_newlines(); // note we load the version with newlines
    let ts = ThemeSet::load_defaults();

    let args: Vec<String> = std::env::args().collect();
    let src;
    let theme;
    let themeRef;

    if args.len() < 2 {
        println!("USAGE: ./syncat [THEME_FILE] SRC_FILE");
        return;
    } else if args.len() == 2 {
        src = &args[1];
        themeRef = &ts.themes["base16-ocean.dark"];
    } else {
        let theme_file = &args[1];
        src = &args[2];
        theme = ThemeSet::get_theme(theme_file).unwrap();
        themeRef = &theme;
    }

    let mut highlighter = HighlightFile::new(src, &ss, themeRef).unwrap();

    // We use read_line instead of `for line in highlighter.reader.lines()` because that
    // doesn't return strings with a `\n`, and including the `\n` gets us more robust highlighting.
    // See the documentation for `SyntaxSet::load_syntaxes`.
    // It also allows re-using the line buffer, which should be a tiny bit faster.
    let mut line = String::new();
    while highlighter.reader.read_line(&mut line).unwrap() > 0 {
        {
            let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line);
            print!("{}", as_24_bit_terminal_escaped(&regions[..], true));
        }
        line.clear();
    }
}
<commit_msg>syncat: factored theme loading into a separate function<commit_after>extern crate syntect;
use syntect::parsing::SyntaxSet;
use syntect::highlighting::{Theme, ThemeSet, Style};
use syntect::util::as_24_bit_terminal_escaped;
use syntect::easy::HighlightFile;

use std::io::BufRead;

fn load_theme(path: &String) -> Theme {
    // TODO: cache this
    ThemeSet::get_theme(path).unwrap()
}

fn main() {
    let ss = SyntaxSet::load_defaults_newlines(); // note we load the version with newlines
    let ts = ThemeSet::load_defaults();

    let args: Vec<String> = std::env::args().collect();
    let src;
    let theme;
    let themeRef;

    if args.len() < 2 {
        println!("USAGE: ./syncat [THEME_FILE] SRC_FILE");
        return;
    } else if args.len() == 2 {
        themeRef = &ts.themes["base16-ocean.dark"];
        src = &args[1];
    } else {
        theme = load_theme(&args[1]);
        themeRef = &theme;
        src = &args[2];
    }

    let mut highlighter = HighlightFile::new(src, &ss, themeRef).unwrap();

    // We use read_line instead of `for line in highlighter.reader.lines()` because that
    // doesn't return strings with a `\n`, and including the `\n` gets us more robust highlighting.
    // See the documentation for `SyntaxSet::load_syntaxes`.
    // It also allows re-using the line buffer, which should be a tiny bit faster.
    let mut line = String::new();
    while highlighter.reader.read_line(&mut line).unwrap() > 0 {
        {
            let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line);
            print!("{}", as_24_bit_terminal_escaped(&regions[..], true));
        }
        line.clear();
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
use std::io::Write;

pub struct HelpText<'a> {
    pub name : &'a str,
    pub version : &'a str,
    pub syntax : &'a str,
    pub summary : &'a str,
    pub long_help : &'a str,
    pub display_usage : bool
}

pub struct CoreOptions<'a> {
    options : getopts::Options,
    help_text : HelpText<'a>
}

impl<'a> CoreOptions<'a> {
    pub fn new(help_text: HelpText<'a>) -> Self {
        let mut ret = CoreOptions {
            options : getopts::Options::new(),
            help_text : help_text
        };
        ret.options
            .optflag("", "help", "print usage information")
            .optflag("", "version", "print name and version number");
        ret
    }
    pub fn optopt(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str) -> &mut CoreOptions<'a> {
        self.options.optopt(short_name, long_name, desc, hint);
        self
    }
    pub fn optflag(&mut self, short_name: &str, long_name: &str, desc: &str) -> &mut CoreOptions<'a> {
        self.options.optflag(short_name, long_name, desc);
        self
    }
    pub fn usage(&self, summary : &str) -> String {
        self.options.usage(summary)
    }
    pub fn parse(&mut self, args : Vec<String>) -> getopts::Matches {
        let matches = match self.options.parse(&args[1..]) {
            Ok(m) => { Some(m) },
            Err(f) => {
                crash!(1, "{}", f);
            }
        }.unwrap();
        if matches.opt_present("help") {
            let usage_str = if self.help_text.display_usage {
                    format!("\n {}\n\n Reference\n",
                        self.options.usage(self.help_text.summary)
                    ).replace("Options:", " Options:")
                } else { String::new() };
            print!("
 {0} {1}

 {0} {2}
{3}{4}
", self.help_text.name, self.help_text.version, self.help_text.syntax, usage_str, self.help_text.long_help);
            exit!(0);
        } else if matches.opt_present("version") {
            println!("{} {}", self.help_text.name, self.help_text.version);
            exit!(0);
        }
        matches
    }
}

#[macro_export]
macro_rules! new_coreopts {
    ($syntax: expr, $summary: expr, $long_help: expr) => (
        uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText {
            name: executable!(),
            version: env!("CARGO_PKG_VERSION"),
            syntax: $syntax,
            summary: $summary,
            long_help: $long_help,
            display_usage: true
        })
    );
    ($syntax: expr, $summary: expr, $long_help: expr, $display_usage: expr) => (
        uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText {
            name: executable!(),
            version: env!("CARGO_PKG_VERSION"),
            syntax: $syntax,
            summary: $summary,
            long_help: $long_help,
            display_usage: $display_usage
        })
    );
}
<commit_msg>uucore: fix utility name in coreopts parsing error messages<commit_after>extern crate getopts;
use std::io::Write;

pub struct HelpText<'a> {
    pub name : &'a str,
    pub version : &'a str,
    pub syntax : &'a str,
    pub summary : &'a str,
    pub long_help : &'a str,
    pub display_usage : bool
}

pub struct CoreOptions<'a> {
    options : getopts::Options,
    help_text : HelpText<'a>
}

impl<'a> CoreOptions<'a> {
    pub fn new(help_text: HelpText<'a>) -> Self {
        let mut ret = CoreOptions {
            options : getopts::Options::new(),
            help_text : help_text
        };
        ret.options
            .optflag("", "help", "print usage information")
            .optflag("", "version", "print name and version number");
        ret
    }
    pub fn optopt(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str) -> &mut CoreOptions<'a> {
        self.options.optopt(short_name, long_name, desc, hint);
        self
    }
    pub fn optflag(&mut self, short_name: &str, long_name: &str, desc: &str) -> &mut CoreOptions<'a> {
        self.options.optflag(short_name, long_name, desc);
        self
    }
    pub fn usage(&self, summary : &str) -> String {
        self.options.usage(summary)
    }
    pub fn parse(&mut self, args : Vec<String>) -> getopts::Matches {
        let matches = match self.options.parse(&args[1..]) {
            Ok(m) => { Some(m) },
            Err(f) => {
                pipe_write!(&mut ::std::io::stderr(), "{}: error: ", self.help_text.name);
                pipe_writeln!(&mut ::std::io::stderr(), "{}", f);
                ::std::process::exit(1);
            }
        }.unwrap();
        if matches.opt_present("help") {
            let usage_str = if self.help_text.display_usage {
                    format!("\n {}\n\n Reference\n",
                        self.options.usage(self.help_text.summary)
                    ).replace("Options:", " Options:")
                } else { String::new() };
            print!("
 {0} {1}

 {0} {2}
{3}{4}
", self.help_text.name, self.help_text.version, self.help_text.syntax, usage_str, self.help_text.long_help);
            exit!(0);
        } else if matches.opt_present("version") {
            println!("{} {}", self.help_text.name, self.help_text.version);
            exit!(0);
        }
        matches
    }
}

#[macro_export]
macro_rules! new_coreopts {
    ($syntax: expr, $summary: expr, $long_help: expr) => (
        uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText {
            name: executable!(),
            version: env!("CARGO_PKG_VERSION"),
            syntax: $syntax,
            summary: $summary,
            long_help: $long_help,
            display_usage: true
        })
    );
    ($syntax: expr, $summary: expr, $long_help: expr, $display_usage: expr) => (
        uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText {
            name: executable!(),
            version: env!("CARGO_PKG_VERSION"),
            syntax: $syntax,
            summary: $summary,
            long_help: $long_help,
            display_usage: $display_usage
        })
    );
}
<|endoftext|>


<commit_before>// Just instantiate some data structures to make sure we got all their foreign items covered.
// Requires full MIR on Windows.

#![feature(rustc_private)]

use std::sync;

extern crate libc;

fn main() {
    let m = sync::Mutex::new(0);
    {
        let _guard = m.lock();
        let try_lock_error = m.try_lock().unwrap_err();
        if let sync::TryLockError::Poisoned(e) = try_lock_error {
            panic!("{}", e);
        }
    }
    drop(m.try_lock().unwrap());
    drop(m);

    #[cfg(not(target_os = "windows"))] // TODO: implement RwLock on Windows
    {
        let rw = sync::RwLock::new(0);
        {
            let _read_guard = rw.read().unwrap();
            drop(rw.read().unwrap());
            drop(rw.try_read().unwrap());
            let try_lock_error = rw.try_write().unwrap_err();
            if let sync::TryLockError::Poisoned(e) = try_lock_error {
                panic!("{}", e);
            }
        }

        {
            let _write_guard = rw.write().unwrap();
            let try_lock_error = rw.try_read().unwrap_err();
            if let sync::TryLockError::Poisoned(e) = try_lock_error {
                panic!("{}", e);
            }
            let try_lock_error = rw.try_write().unwrap_err();
            if let sync::TryLockError::Poisoned(e) = try_lock_error {
                panic!("{}", e);
            }
        }

        // need to go a layer deeper and test the behavior of libc functions, because
        // std::sys::unix::rwlock::RWLock keeps track of write_locked and num_readers

        unsafe {
            let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
            assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, std::ptr::null_mut()), 0);
            assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
            assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
            assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
            assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
            assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
            assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
        }

        unsafe {
            let mut rw: libc::pthread_rwlock_t = std::mem::zeroed();
            assert_eq!(libc::pthread_rwlock_init(&mut rw as *mut _, std::ptr::null_mut()), 0);

            assert_eq!(libc::pthread_rwlock_rdlock(&mut rw as *mut _), 0);
            assert_eq!(libc::pthread_rwlock_rdlock(&mut rw as *mut _), 0);
            assert_eq!(libc::pthread_rwlock_unlock(&mut rw as *mut _), 0);
            assert_eq!(libc::pthread_rwlock_tryrdlock(&mut rw as *mut _), 0);
            assert_eq!(libc::pthread_rwlock_unlock(&mut rw as *mut _), 0);
            assert_eq!(libc::pthread_rwlock_trywrlock(&mut rw as *mut _), libc::EBUSY);
            assert_eq!(libc::pthread_rwlock_unlock(&mut rw as *mut _), 0);

            assert_eq!(libc::pthread_rwlock_wrlock(&mut rw as *mut _), 0);
            assert_eq!(libc::pthread_rwlock_tryrdlock(&mut rw as *mut _), libc::EBUSY);
            assert_eq!(libc::pthread_rwlock_trywrlock(&mut rw as *mut _), libc::EBUSY);
            assert_eq!(libc::pthread_rwlock_unlock(&mut rw as *mut _), 0);

            assert_eq!(libc::pthread_rwlock_destroy(&mut rw as *mut _), 0);
        }
    }
}
<commit_msg>Clean up test case<commit_after>// Just instantiate some data structures to make sure we got all their foreign items covered.
// Requires full MIR on Windows.

#![feature(rustc_private)]

use std::sync::{Mutex, RwLock, TryLockError};

extern crate libc;

fn main() {
    let m = Mutex::new(0);
    {
        let _guard = m.lock();
        assert!(m.try_lock().unwrap_err().would_block());
    }
    drop(m.try_lock().unwrap());
    drop(m);

    #[cfg(not(target_os = "windows"))] // TODO: implement RwLock on Windows
    {
        let rw = RwLock::new(0);
        {
            let _read_guard = rw.read().unwrap();
            drop(rw.read().unwrap());
            drop(rw.try_read().unwrap());
            assert!(rw.try_write().unwrap_err().would_block());
        }

        {
            let _write_guard = rw.write().unwrap();
            assert!(rw.try_read().unwrap_err().would_block());
            assert!(rw.try_write().unwrap_err().would_block());
        }

        // need to go a layer deeper and test the behavior of libc functions, because
        // std::sys::unix::rwlock::RWLock keeps track of write_locked and num_readers

        unsafe {
            let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
            assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, std::ptr::null_mut()), 0);
            assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
            assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
            assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
            assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
            assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
            assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
        }

        let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
        unsafe {
            assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0);
            assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0);
            assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
            assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), 0);
            assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
            assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
            assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);

            assert_eq!(libc::pthread_rwlock_wrlock(rw.get()), 0);
            assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY);
            assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
            assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);

            assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0);
        }
    }
}

trait TryLockErrorExt<T> {
    fn would_block(&self) -> bool;
}

impl<T> TryLockErrorExt<T> for TryLockError<T> {
    fn would_block(&self) -> bool {
        match self {
            TryLockError::WouldBlock => true,
            TryLockError::Poisoned(_) => false,
        }
    }
}
<|endoftext|>


<commit_before>#![feature(asm)]
// only-x86 only-x86_64

#[warn(clippy::inline_asm_x86_intel_syntax)]
mod warn_intel {
    pub(super) unsafe fn use_asm() {
        asm!("");
        asm!("", options());
        asm!("", options(nostack));
        asm!("", options(att_syntax));
        asm!("", options(nostack, att_syntax));
    }
}

#[warn(clippy::inline_asm_x86_att_syntax)]
mod warn_att {
    pub(super) unsafe fn use_asm() {
        asm!("");
        asm!("", options());
        asm!("", options(nostack));
        asm!("", options(att_syntax));
        asm!("", options(nostack, att_syntax));
    }
}

fn main() {
    unsafe {
        warn_att::use_asm();
        warn_intel::use_asm();
    }
}
<commit_msg>Update tests/ui/asm_syntax.rs<commit_after>#![feature(asm)]
// only-x86_64

#[warn(clippy::inline_asm_x86_intel_syntax)]
mod warn_intel {
    pub(super) unsafe fn use_asm() {
        asm!("");
        asm!("", options());
        asm!("", options(nostack));
        asm!("", options(att_syntax));
        asm!("", options(nostack, att_syntax));
    }
}

#[warn(clippy::inline_asm_x86_att_syntax)]
mod warn_att {
    pub(super) unsafe fn use_asm() {
        asm!("");
        asm!("", options());
        asm!("", options(nostack));
        asm!("", options(att_syntax));
        asm!("", options(nostack, att_syntax));
    }
}

fn main() {
    unsafe {
        warn_att::use_asm();
        warn_intel::use_asm();
    }
}
<|endoftext|>


<commit_before>extern crate rs6502;
extern crate sdl2;

mod input;
mod ship;

use std::io::{self, BufRead, Write};
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::Duration;

use rs6502::{Assembler, Cpu, Disassembler};

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::image::LoadTexture;
use sdl2::Sdl;

const FPS: u32 = 1000 / 60;

fn main() {
    let (tx, rx) = channel();

    input::handle(tx);

    let bytecode = assemble("level.asm");
    let mut cpu = init_cpu(&bytecode);

    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Hakka", 1280, 400)
        .position_centered()
        .build()
        .unwrap();

    let mut renderer = window.renderer()
        .accelerated()
        .build()
        .unwrap();

    let ship_texture = renderer.load_texture(Path::new("ship.png")).unwrap();

    let mut events = sdl_context.event_pump().unwrap();

    let mut ship = ship::Ship::new(ship_texture);

    let mut last_fps = 0;
    let mut monitor_enabled = false;
    let mut monitor_last = 0;

    'running: loop {
        cpu.step();

        if let Ok(input) = rx.try_recv() {
            let input = input.trim();
            if input == "exit" {
                break 'running;
            }

            if input == "list" {
                std::io::stdout().write(b"\n");
                std::io::stdout().write(b"-- Disassembly --\n");

                let mut disassembler = Disassembler::with_offset(0xC000);
                let asm = disassembler.disassemble(&bytecode[..]);

                std::io::stdout().write(asm.as_bytes());
                std::io::stdout().write(b"\nHAKKA> ");
                std::io::stdout().flush();
            }

            if input == "monitor" {
                monitor_enabled = !monitor_enabled;
            }

            if input.as_bytes().starts_with(b"memset") {
                let parts: Vec<&str> = input.split(" ").collect();
                if parts.len() < 3 {
                    println!("ERR: Requires 2 arguments. Example: memset 0x00 0x01 to store 0x01 \
                              in 0x00.");
                } else {
                    let dst = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16).unwrap();
                    let src = u8::from_str_radix(&parts[2].replace("0x", "")[..], 16).unwrap();

                    cpu.memory[dst as usize] = src;
                }
            }
        }

        ship.process(&cpu.memory[..]);

        for event in events.poll_iter() {
            match event {
                Event::Quit { .. } => break 'running,
                Event::KeyDown { keycode: Option::Some(Keycode::Right), .. } => {
                    cpu.memory[0x04] = 39
                }
                Event::KeyDown { keycode: Option::Some(Keycode::Left), .. } => {
                    cpu.memory[0x04] = 37
                }
                Event::KeyUp { keycode: Option::Some(Keycode::Right), .. } => cpu.memory[0x04] = 0,
                Event::KeyUp { keycode: Option::Some(Keycode::Left), .. } => cpu.memory[0x04] = 0,
                Event::KeyDown { keycode: Option::Some(Keycode::Escape), .. } => break 'running,
                _ => (),
            }
        }

        let now = sdl_context.timer().unwrap().ticks();
        let delta = now - last_fps;
        if delta < FPS {
            sdl_context.timer().unwrap().delay(FPS - delta);
        } else {
            if !cpu.flags.interrupt_disabled {
                // Render stuff here
                renderer.clear();
                ship.render(&mut renderer);
                renderer.present();
                last_fps = now;
            }
        }

        let delta = now - monitor_last;
        if delta > 1000 && monitor_enabled {
            for b in &cpu.memory[0x00..0xA] {
                print!("{:02X} ", *b);
            }
            println!("");
            monitor_last = now;
        }

        // thread::sleep(Duration::from_millis(10));
    }
}

fn assemble<P>(path: P) -> Vec<u8>
    where P: AsRef<Path>
{
    let mut assembler = Assembler::new();
    assembler.assemble_file("level.asm").unwrap()
}

fn init_cpu(bytecode: &[u8]) -> Cpu {
    let mut cpu = Cpu::new();
    cpu.load(&bytecode[..], None);
    cpu.flags.interrupt_disabled = false;

    cpu.memory[0x02] = 0x80;
    cpu.memory[0x03] = 0x00;
    cpu.memory[0x05] = 0x19;
    cpu.memory[0x06] = 0x00;

    cpu
}<commit_msg>Rename 'FPS' constant<commit_after>extern crate rs6502;
extern crate sdl2;

mod input;
mod ship;

use std::io::{self, BufRead, Write};
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::Duration;

use rs6502::{Assembler, Cpu, Disassembler};

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::image::LoadTexture;
use sdl2::Sdl;

const FPS_STEP: u32 = 1000 / 60;

fn main() {
    let (tx, rx) = channel();

    input::handle(tx);

    let bytecode = assemble("level.asm");
    let mut cpu = init_cpu(&bytecode);

    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Hakka", 1280, 400)
        .position_centered()
        .build()
        .unwrap();

    let mut renderer = window.renderer()
        .accelerated()
        .build()
        .unwrap();

    let ship_texture = renderer.load_texture(Path::new("ship.png")).unwrap();

    let mut events = sdl_context.event_pump().unwrap();

    let mut ship = ship::Ship::new(ship_texture);

    let mut last_fps = 0;
    let mut monitor_enabled = false;
    let mut monitor_last = 0;

    'running: loop {
        cpu.step();

        if let Ok(input) = rx.try_recv() {
            let input = input.trim();
            if input == "exit" {
                break 'running;
            }

            if input == "list" {
                std::io::stdout().write(b"\n");
                std::io::stdout().write(b"-- Disassembly --\n");

                let mut disassembler = Disassembler::with_offset(0xC000);
                let asm = disassembler.disassemble(&bytecode[..]);

                std::io::stdout().write(asm.as_bytes());
                std::io::stdout().write(b"\nHAKKA> ");
                std::io::stdout().flush();
            }

            if input == "monitor" {
                monitor_enabled = !monitor_enabled;
            }

            if input.as_bytes().starts_with(b"memset") {
                let parts: Vec<&str> = input.split(" ").collect();
                if parts.len() < 3 {
                    println!("ERR: Requires 2 arguments. Example: memset 0x00 0x01 to store 0x01 \
                              in 0x00.");
                } else {
                    let dst = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16).unwrap();
                    let src = u8::from_str_radix(&parts[2].replace("0x", "")[..], 16).unwrap();

                    cpu.memory[dst as usize] = src;
                }
            }
        }

        ship.process(&cpu.memory[..]);

        for event in events.poll_iter() {
            match event {
                Event::Quit { .. } => break 'running,
                Event::KeyDown { keycode: Option::Some(Keycode::Right), .. } => {
                    cpu.memory[0x04] = 39
                }
                Event::KeyDown { keycode: Option::Some(Keycode::Left), .. } => {
                    cpu.memory[0x04] = 37
                }
                Event::KeyUp { keycode: Option::Some(Keycode::Right), .. } => cpu.memory[0x04] = 0,
                Event::KeyUp { keycode: Option::Some(Keycode::Left), .. } => cpu.memory[0x04] = 0,
                Event::KeyDown { keycode: Option::Some(Keycode::Escape), .. } => break 'running,
                _ => (),
            }
        }

        let now = sdl_context.timer().unwrap().ticks();
        let delta = now - last_fps;
        if delta < FPS_STEP {
            sdl_context.timer().unwrap().delay(FPS_STEP - delta);
        } else {
            if !cpu.flags.interrupt_disabled {
                // Render stuff here
                renderer.clear();
                ship.render(&mut renderer);
                renderer.present();
                last_fps = now;
            }
        }

        let delta = now - monitor_last;
        if delta > 1000 && monitor_enabled {
            for b in &cpu.memory[0x00..0xA] {
                print!("{:02X} ", *b);
            }
            println!("");
            monitor_last = now;
        }

        // thread::sleep(Duration::from_millis(10));
    }
}

fn assemble<P>(path: P) -> Vec<u8>
    where P: AsRef<Path>
{
    let mut assembler = Assembler::new();
    assembler.assemble_file("level.asm").unwrap()
}

fn init_cpu(bytecode: &[u8]) -> Cpu {
    let mut cpu = Cpu::new();
    cpu.load(&bytecode[..], None);
    cpu.flags.interrupt_disabled = false;

    cpu.memory[0x02] = 0x80;
    cpu.memory[0x03] = 0x00;
    cpu.memory[0x05] = 0x19;
    cpu.memory[0x06] = 0x00;

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

//! Generic types for the handling of [images].
//!
//! [images]: https://drafts.csswg.org/css-images/#image-values

use Atom;
use cssparser::serialize_identifier;
use custom_properties::SpecifiedValue;
use std::fmt;
use style_traits::ToCss;
use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};

/// An [image].
///
/// [image]: https://drafts.csswg.org/css-images/#image-values
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Image<Gradient, MozImageRect, ImageUrl> {
    /// A `<url()>` image.
    Url(ImageUrl),
    /// A `<gradient>` image.  Gradients are rather large, and not nearly as
    /// common as urls, so we box them here to keep the size of this enum sane.
    Gradient(Box<Gradient>),
    /// A `-moz-image-rect` image.  Also fairly large and rare.
    Rect(Box<MozImageRect>),
    /// A `-moz-element(# <element-id>)`
    Element(Atom),
    /// A paint worklet image.
    /// https://drafts.css-houdini.org/css-paint-api/
    #[cfg(feature = "servo")]
    PaintWorklet(PaintWorklet),
}

// Can't just use derive(ToComputedValue) on Image, because when trying to do
// "impl<T> ToComputedValue for Box<T>" the Rust compiler complains that
// "impl<T> ToComputedValue for T where T: ComputedValueAsSpecified + Clone"
// aleady implements ToComputedValue for std::boxed::Box<_> and hence we have
// conflicting implementations.
impl<Gradient: ToComputedValue,
     MozImageRect: ToComputedValue,
     ImageUrl: ToComputedValue> ToComputedValue for Image<Gradient, MozImageRect, ImageUrl> {
    type ComputedValue = Image<<Gradient as ToComputedValue>::ComputedValue,
                               <MozImageRect as ToComputedValue>::ComputedValue,
                               <ImageUrl as ToComputedValue>::ComputedValue>;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            Image::Url(ref url) => Image::Url(url.to_computed_value(context)),
            Image::Gradient(ref gradient) =>
                Image::Gradient(Box::new(gradient.to_computed_value(context))),
            Image::Rect(ref rect) => Image::Rect(Box::new(rect.to_computed_value(context))),
            Image::Element(ref atom) => Image::Element(atom.to_computed_value(context)),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref worklet) => Image::PaintWorklet(worklet.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            Image::Url(ref url) => Image::Url(ImageUrl::from_computed_value(url)),
            Image::Gradient(ref boxed_gradient) =>
                Image::Gradient(Box::new(Gradient::from_computed_value(&*boxed_gradient))),
            Image::Rect(ref boxed_rect) =>
                Image::Rect(Box::new(MozImageRect::from_computed_value(&*boxed_rect))),
            Image::Element(ref atom) => Image::Element(Atom::from_computed_value(atom)),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref worklet) =>
                Image::PaintWorklet(PaintWorklet::from_computed_value(worklet)),
        }
    }
}

/// A CSS gradient.
/// https://drafts.csswg.org/css-images/#gradients
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> {
    /// Gradients can be linear or radial.
    pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
    /// The color stops and interpolation hints.
    pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
    /// True if this is a repeating gradient.
    #[compute(clone)]
    pub repeating: bool,
    /// Compatibility mode.
    #[compute(clone)]
    pub compat_mode: CompatMode,
}

#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
pub enum CompatMode {
    /// Modern syntax.
    Modern,
    /// `-webkit` prefix.
    WebKit,
    /// `-moz` prefix
    Moz,
}

/// A gradient kind.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> {
    /// A linear gradient.
    Linear(LineDirection),
    /// A radial gradient.
    Radial(EndingShape<Length, LengthOrPercentage>, Position, Option<Angle>),
}

/// A radial gradient's ending shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum EndingShape<Length, LengthOrPercentage> {
    /// A circular gradient.
    Circle(Circle<Length>),
    /// An elliptic gradient.
    Ellipse(Ellipse<LengthOrPercentage>),
}

/// A circle shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Circle<Length> {
    /// A circle radius.
    Radius(Length),
    /// A circle extent.
    Extent(ShapeExtent),
}

/// An ellipse shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Ellipse<LengthOrPercentage> {
    /// An ellipse pair of radii.
    Radii(LengthOrPercentage, LengthOrPercentage),
    /// An ellipse extent.
    Extent(ShapeExtent),
}

/// https://drafts.csswg.org/css-images/#typedef-extent-keyword
define_css_keyword_enum!(ShapeExtent:
    "closest-side" => ClosestSide,
    "farthest-side" => FarthestSide,
    "closest-corner" => ClosestCorner,
    "farthest-corner" => FarthestCorner,
    "contain" => Contain,
    "cover" => Cover
);
impl ComputedValueAsSpecified for ShapeExtent {}

/// A gradient item.
/// https://drafts.csswg.org/css-images-4/#color-stop-syntax
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
pub enum GradientItem<Color, LengthOrPercentage> {
    /// A color stop.
    ColorStop(ColorStop<Color, LengthOrPercentage>),
    /// An interpolation hint.
    InterpolationHint(LengthOrPercentage),
}

/// A color stop.
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
#[derive(Clone, Copy, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ColorStop<Color, LengthOrPercentage> {
    /// The color of this stop.
    pub color: Color,
    /// The position of this stop.
    pub position: Option<LengthOrPercentage>,
}

/// Specified values for a paint worklet.
/// https://drafts.css-houdini.org/css-paint-api/
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PaintWorklet {
    /// The name the worklet was registered with.
    pub name: Atom,
    /// The arguments for the worklet.
    /// TODO: store a parsed representation of the arguments.
    pub arguments: Vec<SpecifiedValue>,
}

impl ComputedValueAsSpecified for PaintWorklet {}

impl ToCss for PaintWorklet {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str("paint(")?;
        serialize_identifier(&*self.name.to_string(), dest)?;
        for argument in &self.arguments {
            dest.write_str(", ")?;
            argument.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

/// Values for `moz-image-rect`.
///
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[css(comma, function)]
#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
    pub url: MozImageRectUrl,
    pub top: NumberOrPercentage,
    pub right: NumberOrPercentage,
    pub bottom: NumberOrPercentage,
    pub left: NumberOrPercentage,
}

impl<G, R, U> fmt::Debug for Image<G, R, U>
    where G: fmt::Debug, R: fmt::Debug, U: fmt::Debug + ToCss
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Image::Url(ref url) => url.to_css(f),
            Image::Gradient(ref grad) => grad.fmt(f),
            Image::Rect(ref rect) => rect.fmt(f),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.fmt(f),
            Image::Element(ref selector) => {
                f.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), f)?;
                f.write_str(")")
            },
        }
    }
}

impl<G, R, U> ToCss for Image<G, R, U>
    where G: ToCss, R: ToCss, U: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Image::Url(ref url) => url.to_css(dest),
            Image::Gradient(ref gradient) => gradient.to_css(dest),
            Image::Rect(ref rect) => rect.to_css(dest),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
            Image::Element(ref selector) => {
                dest.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), dest)?;
                dest.write_str(")")
            },
        }
    }
}

impl<D, L, LoP, P, C, A> ToCss for Gradient<D, L, LoP, P, C, A>
    where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, A: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.compat_mode {
            CompatMode::WebKit => dest.write_str("-webkit-")?,
            CompatMode::Moz => dest.write_str("-moz-")?,
            _ => {},
        }

        if self.repeating {
            dest.write_str("repeating-")?;
        }
        dest.write_str(self.kind.label())?;
        dest.write_str("-gradient(")?;
        let mut skip_comma = match self.kind {
            GradientKind::Linear(ref direction)
                if direction.points_downwards(self.compat_mode) => true,
            GradientKind::Linear(ref direction) => {
                direction.to_css(dest, self.compat_mode)?;
                false
            },
            GradientKind::Radial(ref shape, ref position, ref angle) => {
                let omit_shape = match *shape {
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
                        true
                    },
                    _ => false,
                };
                if self.compat_mode == CompatMode::Modern {
                    if !omit_shape {
                        shape.to_css(dest)?;
                        dest.write_str(" ")?;
                    }
                    dest.write_str("at ")?;
                    position.to_css(dest)?;
                } else {
                    position.to_css(dest)?;
                    if let Some(ref a) = *angle {
                        dest.write_str(" ")?;
                        a.to_css(dest)?;
                    }
                    if !omit_shape {
                        dest.write_str(", ")?;
                        shape.to_css(dest)?;
                    }
                }
                false
            },
        };
        for item in &self.items {
            if !skip_comma {
                dest.write_str(", ")?;
            }
            skip_comma = false;
            item.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

impl<D, L, LoP, P, A> GradientKind<D, L, LoP, P, A> {
    fn label(&self) -> &str {
        match *self {
            GradientKind::Linear(..) => "linear",
            GradientKind::Radial(..) => "radial",
        }
    }
}

/// The direction of a linear gradient.
pub trait LineDirection {
    /// Whether this direction points towards, and thus can be omitted.
    fn points_downwards(&self, compat_mode: CompatMode) -> bool;

    /// Serialises this direction according to the compatibility mode.
    fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
        where W: fmt::Write;
}

impl<L> ToCss for Circle<L>
where
    L: ToCss,
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    where
        W: fmt::Write,
    {
        match *self {
            Circle::Extent(ShapeExtent::FarthestCorner) |
            Circle::Extent(ShapeExtent::Cover) => {
                dest.write_str("circle")
            },
            Circle::Extent(keyword) => {
                dest.write_str("circle ")?;
                keyword.to_css(dest)
            },
            Circle::Radius(ref length) => {
                length.to_css(dest)
            },
        }
    }
}

impl<C, L> fmt::Debug for ColorStop<C, L>
    where C: fmt::Debug, L: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.color)?;
        if let Some(ref pos) = self.position {
            write!(f, " {:?}", pos)?;
        }
        Ok(())
    }
}
<commit_msg>style: Kill CVAS in image.rs.<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/. */

//! Generic types for the handling of [images].
//!
//! [images]: https://drafts.csswg.org/css-images/#image-values

use Atom;
use cssparser::serialize_identifier;
use custom_properties;
use std::fmt;
use style_traits::ToCss;
use values::computed::{Context, ToComputedValue};

/// An [image].
///
/// [image]: https://drafts.csswg.org/css-images/#image-values
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Image<Gradient, MozImageRect, ImageUrl> {
    /// A `<url()>` image.
    Url(ImageUrl),
    /// A `<gradient>` image.  Gradients are rather large, and not nearly as
    /// common as urls, so we box them here to keep the size of this enum sane.
    Gradient(Box<Gradient>),
    /// A `-moz-image-rect` image.  Also fairly large and rare.
    Rect(Box<MozImageRect>),
    /// A `-moz-element(# <element-id>)`
    Element(Atom),
    /// A paint worklet image.
    /// https://drafts.css-houdini.org/css-paint-api/
    #[cfg(feature = "servo")]
    PaintWorklet(PaintWorklet),
}

// Can't just use derive(ToComputedValue) on Image, because when trying to do
// "impl<T> ToComputedValue for Box<T>" the Rust compiler complains that
// "impl<T> ToComputedValue for T where T: ComputedValueAsSpecified + Clone"
// aleady implements ToComputedValue for std::boxed::Box<_> and hence we have
// conflicting implementations.
impl<Gradient: ToComputedValue,
     MozImageRect: ToComputedValue,
     ImageUrl: ToComputedValue> ToComputedValue for Image<Gradient, MozImageRect, ImageUrl> {
    type ComputedValue = Image<<Gradient as ToComputedValue>::ComputedValue,
                               <MozImageRect as ToComputedValue>::ComputedValue,
                               <ImageUrl as ToComputedValue>::ComputedValue>;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            Image::Url(ref url) => Image::Url(url.to_computed_value(context)),
            Image::Gradient(ref gradient) =>
                Image::Gradient(Box::new(gradient.to_computed_value(context))),
            Image::Rect(ref rect) => Image::Rect(Box::new(rect.to_computed_value(context))),
            Image::Element(ref atom) => Image::Element(atom.to_computed_value(context)),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref worklet) => Image::PaintWorklet(worklet.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            Image::Url(ref url) => Image::Url(ImageUrl::from_computed_value(url)),
            Image::Gradient(ref boxed_gradient) =>
                Image::Gradient(Box::new(Gradient::from_computed_value(&*boxed_gradient))),
            Image::Rect(ref boxed_rect) =>
                Image::Rect(Box::new(MozImageRect::from_computed_value(&*boxed_rect))),
            Image::Element(ref atom) => Image::Element(Atom::from_computed_value(atom)),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref worklet) =>
                Image::PaintWorklet(PaintWorklet::from_computed_value(worklet)),
        }
    }
}

/// A CSS gradient.
/// https://drafts.csswg.org/css-images/#gradients
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> {
    /// Gradients can be linear or radial.
    pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
    /// The color stops and interpolation hints.
    pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
    /// True if this is a repeating gradient.
    #[compute(clone)]
    pub repeating: bool,
    /// Compatibility mode.
    #[compute(clone)]
    pub compat_mode: CompatMode,
}

#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
pub enum CompatMode {
    /// Modern syntax.
    Modern,
    /// `-webkit` prefix.
    WebKit,
    /// `-moz` prefix
    Moz,
}

/// A gradient kind.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> {
    /// A linear gradient.
    Linear(LineDirection),
    /// A radial gradient.
    Radial(EndingShape<Length, LengthOrPercentage>, Position, Option<Angle>),
}

/// A radial gradient's ending shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum EndingShape<Length, LengthOrPercentage> {
    /// A circular gradient.
    Circle(Circle<Length>),
    /// An elliptic gradient.
    Ellipse(Ellipse<LengthOrPercentage>),
}

/// A circle shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Circle<Length> {
    /// A circle radius.
    Radius(Length),
    /// A circle extent.
    Extent(ShapeExtent),
}

/// An ellipse shape.
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Ellipse<LengthOrPercentage> {
    /// An ellipse pair of radii.
    Radii(LengthOrPercentage, LengthOrPercentage),
    /// An ellipse extent.
    Extent(ShapeExtent),
}

/// https://drafts.csswg.org/css-images/#typedef-extent-keyword
define_css_keyword_enum!(ShapeExtent:
    "closest-side" => ClosestSide,
    "farthest-side" => FarthestSide,
    "closest-corner" => ClosestCorner,
    "farthest-corner" => FarthestCorner,
    "contain" => Contain,
    "cover" => Cover
);
add_impls_for_keyword_enum!(ShapeExtent);

/// A gradient item.
/// https://drafts.csswg.org/css-images-4/#color-stop-syntax
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
pub enum GradientItem<Color, LengthOrPercentage> {
    /// A color stop.
    ColorStop(ColorStop<Color, LengthOrPercentage>),
    /// An interpolation hint.
    InterpolationHint(LengthOrPercentage),
}

/// A color stop.
/// https://drafts.csswg.org/css-images/#typedef-color-stop-list
#[derive(Clone, Copy, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ColorStop<Color, LengthOrPercentage> {
    /// The color of this stop.
    pub color: Color,
    /// The position of this stop.
    pub position: Option<LengthOrPercentage>,
}

/// Specified values for a paint worklet.
/// https://drafts.css-houdini.org/css-paint-api/
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PaintWorklet {
    /// The name the worklet was registered with.
    pub name: Atom,
    /// The arguments for the worklet.
    /// TODO: store a parsed representation of the arguments.
    pub arguments: Vec<custom_properties::SpecifiedValue>,
}

trivial_to_computed_value!(PaintWorklet);

impl ToCss for PaintWorklet {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str("paint(")?;
        serialize_identifier(&*self.name.to_string(), dest)?;
        for argument in &self.arguments {
            dest.write_str(", ")?;
            argument.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

/// Values for `moz-image-rect`.
///
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[css(comma, function)]
#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
    pub url: MozImageRectUrl,
    pub top: NumberOrPercentage,
    pub right: NumberOrPercentage,
    pub bottom: NumberOrPercentage,
    pub left: NumberOrPercentage,
}

impl<G, R, U> fmt::Debug for Image<G, R, U>
    where G: fmt::Debug, R: fmt::Debug, U: fmt::Debug + ToCss
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Image::Url(ref url) => url.to_css(f),
            Image::Gradient(ref grad) => grad.fmt(f),
            Image::Rect(ref rect) => rect.fmt(f),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.fmt(f),
            Image::Element(ref selector) => {
                f.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), f)?;
                f.write_str(")")
            },
        }
    }
}

impl<G, R, U> ToCss for Image<G, R, U>
    where G: ToCss, R: ToCss, U: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Image::Url(ref url) => url.to_css(dest),
            Image::Gradient(ref gradient) => gradient.to_css(dest),
            Image::Rect(ref rect) => rect.to_css(dest),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
            Image::Element(ref selector) => {
                dest.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), dest)?;
                dest.write_str(")")
            },
        }
    }
}

impl<D, L, LoP, P, C, A> ToCss for Gradient<D, L, LoP, P, C, A>
    where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, A: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.compat_mode {
            CompatMode::WebKit => dest.write_str("-webkit-")?,
            CompatMode::Moz => dest.write_str("-moz-")?,
            _ => {},
        }

        if self.repeating {
            dest.write_str("repeating-")?;
        }
        dest.write_str(self.kind.label())?;
        dest.write_str("-gradient(")?;
        let mut skip_comma = match self.kind {
            GradientKind::Linear(ref direction)
                if direction.points_downwards(self.compat_mode) => true,
            GradientKind::Linear(ref direction) => {
                direction.to_css(dest, self.compat_mode)?;
                false
            },
            GradientKind::Radial(ref shape, ref position, ref angle) => {
                let omit_shape = match *shape {
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
                        true
                    },
                    _ => false,
                };
                if self.compat_mode == CompatMode::Modern {
                    if !omit_shape {
                        shape.to_css(dest)?;
                        dest.write_str(" ")?;
                    }
                    dest.write_str("at ")?;
                    position.to_css(dest)?;
                } else {
                    position.to_css(dest)?;
                    if let Some(ref a) = *angle {
                        dest.write_str(" ")?;
                        a.to_css(dest)?;
                    }
                    if !omit_shape {
                        dest.write_str(", ")?;
                        shape.to_css(dest)?;
                    }
                }
                false
            },
        };
        for item in &self.items {
            if !skip_comma {
                dest.write_str(", ")?;
            }
            skip_comma = false;
            item.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

impl<D, L, LoP, P, A> GradientKind<D, L, LoP, P, A> {
    fn label(&self) -> &str {
        match *self {
            GradientKind::Linear(..) => "linear",
            GradientKind::Radial(..) => "radial",
        }
    }
}

/// The direction of a linear gradient.
pub trait LineDirection {
    /// Whether this direction points towards, and thus can be omitted.
    fn points_downwards(&self, compat_mode: CompatMode) -> bool;

    /// Serialises this direction according to the compatibility mode.
    fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
        where W: fmt::Write;
}

impl<L> ToCss for Circle<L>
where
    L: ToCss,
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    where
        W: fmt::Write,
    {
        match *self {
            Circle::Extent(ShapeExtent::FarthestCorner) |
            Circle::Extent(ShapeExtent::Cover) => {
                dest.write_str("circle")
            },
            Circle::Extent(keyword) => {
                dest.write_str("circle ")?;
                keyword.to_css(dest)
            },
            Circle::Radius(ref length) => {
                length.to_css(dest)
            },
        }
    }
}

impl<C, L> fmt::Debug for ColorStop<C, L>
    where C: fmt::Debug, L: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.color)?;
        if let Some(ref pos) = self.position {
            write!(f, " {:?}", pos)?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use hir::db::HirDatabase;
use ra_syntax::{
    ast::{AstNode, BinExpr, BinOp}
};

use crate::{AssistCtx, Assist, AssistId};

pub(crate) fn flip_eq_operands(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let expr = ctx.node_at_offset::<BinExpr>()?;
    let allowed_ops = [BinOp::EqualityTest, BinOp::NegatedEqualityTest];
    let expr_op = expr.op()?;
    if ! allowed_ops.iter().any(|o| *o == expr_op) {
        return None;
    }
    let node = expr.syntax();
    let prev = node.first_child()?;
    let next = node.last_child()?;
    ctx.add_action(AssistId("flip_eq_operands"), "flip equality operands", |edit| {
        edit.target(node.range());
        edit.replace(prev.range(), next.text());
        edit.replace(next.range(), prev.text());
    });

    ctx.build()
}

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

    use crate::helpers::{check_assist, check_assist_target};

    #[test]
    fn flip_eq_operands_for_simple_stmt() {
        check_assist(
            flip_eq_operands,
            "fn f() { let res = 1 ==<|> 2; }",
            "fn f() { let res = 2 ==<|> 1; }",
        )
    }

    #[test]
    fn flip_neq_operands_for_simple_stmt() {
        check_assist(
            flip_eq_operands,
            "fn f() { let res = 1 !=<|> 2; }",
            "fn f() { let res = 2 !=<|> 1; }",
        )
    }

    #[test]
    fn flip_eq_operands_for_complex_stmt() {
        check_assist(
            flip_eq_operands,
            "fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
            "fn f() { let res = (2 + 2) ==<|> (1 + 1); }",
        )
    }

    #[test]
    fn flip_eq_operands_in_match_expr() {
        check_assist(
            flip_eq_operands,
            r#"
            fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
                match other.downcast_ref::<Self>() {
                    None => false,
                    Some(it) => it ==<|> self,
                }
            }
            "#,
            r#"
            fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
                match other.downcast_ref::<Self>() {
                    None => false,
                    Some(it) => self ==<|> it,
                }
            }
            "#,
        )
    }

    #[test]
    fn flip_eq_operands_target() {
        check_assist_target(flip_eq_operands, "fn f() { let res = 1 ==<|> 2; }", "1 == 2")
    }
}
<commit_msg>Minor formatting changes.<commit_after>use hir::db::HirDatabase;
use ra_syntax::{
    ast::{AstNode, BinExpr, BinOp}
};

use crate::{AssistCtx, Assist, AssistId};

pub(crate) fn flip_eq_operands(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let expr = ctx.node_at_offset::<BinExpr>()?;
    let allowed_ops = [BinOp::EqualityTest, BinOp::NegatedEqualityTest];
    let expr_op = expr.op()?;
    if !allowed_ops.iter().any(|o| *o == expr_op) {
        return None;
    }
    let node = expr.syntax();
    let prev = node.first_child()?;
    let next = node.last_child()?;
    ctx.add_action(AssistId("flip_eq_operands"), "flip equality operands", |edit| {
        edit.target(node.range());
        edit.replace(prev.range(), next.text());
        edit.replace(next.range(), prev.text());
    });

    ctx.build()
}

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

    use crate::helpers::{check_assist, check_assist_target};

    #[test]
    fn flip_eq_operands_for_simple_stmt() {
        check_assist(
            flip_eq_operands,
            "fn f() { let res = 1 ==<|> 2; }",
            "fn f() { let res = 2 ==<|> 1; }",
        )
    }

    #[test]
    fn flip_neq_operands_for_simple_stmt() {
        check_assist(
            flip_eq_operands,
            "fn f() { let res = 1 !=<|> 2; }",
            "fn f() { let res = 2 !=<|> 1; }",
        )
    }

    #[test]
    fn flip_eq_operands_for_complex_stmt() {
        check_assist(
            flip_eq_operands,
            "fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
            "fn f() { let res = (2 + 2) ==<|> (1 + 1); }",
        )
    }

    #[test]
    fn flip_eq_operands_in_match_expr() {
        check_assist(
            flip_eq_operands,
            r#"
            fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
                match other.downcast_ref::<Self>() {
                    None => false,
                    Some(it) => it ==<|> self,
                }
            }
            "#,
            r#"
            fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
                match other.downcast_ref::<Self>() {
                    None => false,
                    Some(it) => self ==<|> it,
                }
            }
            "#,
        )
    }

    #[test]
    fn flip_eq_operands_target() {
        check_assist_target(flip_eq_operands, "fn f() { let res = 1 ==<|> 2; }", "1 == 2")
    }
}
<|endoftext|>