<commit_before>use net2::TcpBuilder;
use net2::TcpStreamExt;
use std::fmt::Arguments;
use std::io::Read;
use std::io::Write;
use std::io;
use std::net::TcpStream;
use std::os::unix::io::{AsRawFd, RawFd};
use std::str;

use logger::Logger;
use logger::LogFile;
use wire::{Cmd, Msg};
use wire;

pub struct Conn {
    nick: String,
    hostname: String,
    realname: String,

    /// servername to be used in PING messages. Read from 002 RPL_YOURHOST. `None` until 002.
    host: Option<String>,

    serv_addr: String,

    /// The TCP connection to the server.
    stream: TcpStream,

    status: ConnStatus,

    serv_name: String,

    /// _Partial_ messages collected here until they make a complete message.
    buf: Vec<u8>,
}

/// How many ticks to wait before sending a ping to the server.
const PING_TICKS: u8 = 60;
/// How many ticks to wait after sending a ping to the server to consider a disconnect.
const PONG_TICKS: u8 = 60;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum ConnStatus {
    /// Need to introduce self
    Introduce,
    PingPong {
        /// Ticks passed since last time we've heard from the server.
        /// Reset on each message. After `PING_TICKS` ticks we send a PING message and move to
        /// `WaitPong` state.
        ticks_passed: u8,
    },
    WaitPong {
        /// Ticks passed since we sent a PING to the server.
        /// After a message move to `PingPong` state. On timeout we reset the connection.
        ticks_passed: u8,
    },
}

#[derive(Debug)]
pub enum ConnEv {
    Disconnected,
    Err(io::Error),
    Msg(Msg),
}

fn init_stream(serv_addr: &str) -> TcpStream {
    let stream = TcpBuilder::new_v4().unwrap().to_tcp_stream().unwrap();
    stream.set_nonblocking(true).unwrap();
    // This will fail with EINPROGRESS
    let _ = stream.connect(serv_addr);
    stream
}

impl Conn {
    pub fn new(serv_addr: &str, serv_name: &str, nick: &str, hostname: &str, realname: &str) -> Conn {
        Conn {
            nick: nick.to_owned(),
            hostname: hostname.to_owned(),
            realname: realname.to_owned(),
            host: None,
            serv_addr: serv_addr.to_owned(),
            stream: init_stream(serv_addr),
            status: ConnStatus::Introduce,
            serv_name: serv_name.to_owned(),
            buf: vec![],
        }
    }

    pub fn reconnect(&mut self) {
        self.stream = init_stream(&self.serv_addr);
        self.status = ConnStatus::Introduce;
    }

    /// Get the RawFd, to be used with select() or other I/O multiplexer.
    pub fn get_raw_fd(&self) -> RawFd {
        self.stream.as_raw_fd()
    }

    pub fn get_serv_name(&self) -> &str {
        &self.serv_name
    }

}

impl Conn {

    ////////////////////////////////////////////////////////////////////////////
    // Tick handling

    pub fn tick(&mut self, evs: &mut Vec<ConnEv>, mut debug_out: LogFile) {
        match self.status {
            ConnStatus::Introduce => {},
            ConnStatus::PingPong { ticks_passed } => {
                if ticks_passed + 1 == PING_TICKS {
                    match self.host {
                        None => {
                            debug_out.write_line(
                                format_args!("{}: Can't send PING, host unknown", self.serv_name));
                        }
                        Some(ref host_) => {
                            debug_out.write_line(
                                format_args!("{}: Ping timeout, sending PING", self.serv_name));
                            wire::ping(&mut self.stream, host_).unwrap();;
                        }
                    }
                    self.status = ConnStatus::WaitPong { ticks_passed: 0 };
                } else {
                    self.status = ConnStatus::PingPong { ticks_passed: ticks_passed + 1 };
                }
            }
            ConnStatus::WaitPong { ticks_passed } => {
                if ticks_passed + 1 == PONG_TICKS {
                    evs.push(ConnEv::Disconnected);
                    self.status = ConnStatus::Introduce;
                } else {
                    self.status = ConnStatus::WaitPong { ticks_passed: ticks_passed + 1 };
                }
            }
        }
    }

    fn reset_ticks(&mut self) {
        match self.status {
            ConnStatus::Introduce => {},
            _ => { self.status = ConnStatus::PingPong { ticks_passed: 0 }; }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Sending messages

    fn introduce(&mut self) {
        wire::user(&self.hostname, &self.realname, &mut self.stream).unwrap();
        wire::nick(&self.nick, &mut self.stream).unwrap();
    }

    ////////////////////////////////////////////////////////////////////////////
    // Receiving messages

    pub fn read_incoming_msg(&mut self, evs: &mut Vec<ConnEv>, logger: &mut Logger) {
        let mut read_buf: [u8; 512] = [0; 512];

        // Handle disconnects
        match self.stream.read(&mut read_buf) {
            Err(err) => {
                evs.push(ConnEv::Err(err));
            }
            Ok(bytes_read) => {
                logger.get_debug_logs().write_line(
                    format_args!("{} read {:?} bytes", self.serv_name, bytes_read));
                self.reset_ticks();
                self.add_to_msg_buf(&read_buf[ 0 .. bytes_read ]);
                self.handle_msgs(evs, logger);
                if bytes_read == 0 {
                    evs.push(ConnEv::Disconnected);
                }
            }
        }
    }

    fn add_to_msg_buf(&mut self, slice: &[u8]) {
        // Some invisible ASCII characters causing glitches on some terminals,
        // we filter those out here.
        self.buf.extend(slice.iter().filter(|c| **c != 0x1 /* SOH */ ||
                                                **c != 0x2 /* STX */ ||
                                                **c != 0x0 /* NUL */ ||
                                                **c != 0x4 /* EOT */ ));
    }

    fn handle_msgs(&mut self, evs: &mut Vec<ConnEv>, logger: &mut Logger) {
        while let Some(msg) = Msg::read(&mut self.buf, Some(logger.get_raw_serv_logs(&self.serv_name))) {
            self.handle_msg(msg, evs, logger);
        }
    }

    fn handle_msg(&mut self, msg: Msg, evs: &mut Vec<ConnEv>, logger: &mut Logger) {
        if let &Msg { cmd: Cmd::PING { ref server }, .. } = &msg {
            wire::pong(server, &mut self.stream).unwrap();
        }

        if let ConnStatus::Introduce = self.status {
            self.introduce();
            self.status = ConnStatus::PingPong { ticks_passed: 0 };
        }

        if let &Msg { cmd: Cmd::Reply { num: 002, ref params }, .. } = &msg {
            // 002    RPL_YOURHOST
            //        "Your host is <servername>, running version <ver>"

            // An example <servername>: cherryh.freenode.net[149.56.134.238/8001]

            match parse_servername(params) {
                None => {
                    logger.get_debug_logs().write_line(
                        format_args!("{} Can't parse hostname from params: {:?}",
                                     self.serv_name, params));
                }
                Some(host) => {
                    logger.get_debug_logs().write_line(
                        format_args!("{} host: {}", self.serv_name, host));
                    self.host = Some(host);
                }
            }
        }

        evs.push(ConnEv::Msg(msg));
    }
}

macro_rules! try_opt {
    ($expr:expr) => (match $expr {
        Option::Some(val) => val,
        Option::None => {
            return Option::None
        }
    })
}

/// Try to parse servername in a 002 RPL_YOURHOST reply
fn parse_servername(params: &[String]) -> Option<String> {
    let msg = try_opt!(params.get(1).or(params.get(0)));
    let slice1 = &msg[13..];
    let servername_ends =
        try_opt!(wire::find_byte(slice1.as_bytes(), b'[')
                 .or(wire::find_byte(slice1.as_bytes(), b',')));
    Some((&slice1[..servername_ends]).to_owned())
}

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

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

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.stream.write_all(buf)
    }

    fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> {
        self.stream.write_fmt(fmt)
    }

    fn by_ref(&mut self) -> &mut Conn {
        self
    }
}

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

    #[test]
    fn test_parse_servername_1() {
        let args = vec!["tiny_test".to_owned(),
                        "Your host is adams.freenode.net[94.125.182.252/8001], \
                         running version ircd-seven-1.1.4".to_owned()];
        assert_eq!(parse_servername(&args), Some("adams.freenode.net".to_owned()));
    }

    #[test]
    fn test_parse_servername_2() {
        let args =
            vec!["tiny_test".to_owned(),
                 "Your host is belew.mozilla.org, running version InspIRCd-2.0".to_owned()];
        assert_eq!(parse_servername(&args), Some("belew.mozilla.org".to_owned()));
    }
}
<commit_msg>Remove a redundant log line<commit_after>use net2::TcpBuilder;
use net2::TcpStreamExt;
use std::fmt::Arguments;
use std::io::Read;
use std::io::Write;
use std::io;
use std::net::TcpStream;
use std::os::unix::io::{AsRawFd, RawFd};
use std::str;

use logger::Logger;
use logger::LogFile;
use wire::{Cmd, Msg};
use wire;

pub struct Conn {
    nick: String,
    hostname: String,
    realname: String,

    /// servername to be used in PING messages. Read from 002 RPL_YOURHOST. `None` until 002.
    host: Option<String>,

    serv_addr: String,

    /// The TCP connection to the server.
    stream: TcpStream,

    status: ConnStatus,

    serv_name: String,

    /// _Partial_ messages collected here until they make a complete message.
    buf: Vec<u8>,
}

/// How many ticks to wait before sending a ping to the server.
const PING_TICKS: u8 = 60;
/// How many ticks to wait after sending a ping to the server to consider a disconnect.
const PONG_TICKS: u8 = 60;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum ConnStatus {
    /// Need to introduce self
    Introduce,
    PingPong {
        /// Ticks passed since last time we've heard from the server.
        /// Reset on each message. After `PING_TICKS` ticks we send a PING message and move to
        /// `WaitPong` state.
        ticks_passed: u8,
    },
    WaitPong {
        /// Ticks passed since we sent a PING to the server.
        /// After a message move to `PingPong` state. On timeout we reset the connection.
        ticks_passed: u8,
    },
}

#[derive(Debug)]
pub enum ConnEv {
    Disconnected,
    Err(io::Error),
    Msg(Msg),
}

fn init_stream(serv_addr: &str) -> TcpStream {
    let stream = TcpBuilder::new_v4().unwrap().to_tcp_stream().unwrap();
    stream.set_nonblocking(true).unwrap();
    // This will fail with EINPROGRESS
    let _ = stream.connect(serv_addr);
    stream
}

impl Conn {
    pub fn new(serv_addr: &str, serv_name: &str, nick: &str, hostname: &str, realname: &str) -> Conn {
        Conn {
            nick: nick.to_owned(),
            hostname: hostname.to_owned(),
            realname: realname.to_owned(),
            host: None,
            serv_addr: serv_addr.to_owned(),
            stream: init_stream(serv_addr),
            status: ConnStatus::Introduce,
            serv_name: serv_name.to_owned(),
            buf: vec![],
        }
    }

    pub fn reconnect(&mut self) {
        self.stream = init_stream(&self.serv_addr);
        self.status = ConnStatus::Introduce;
    }

    /// Get the RawFd, to be used with select() or other I/O multiplexer.
    pub fn get_raw_fd(&self) -> RawFd {
        self.stream.as_raw_fd()
    }

    pub fn get_serv_name(&self) -> &str {
        &self.serv_name
    }

}

impl Conn {

    ////////////////////////////////////////////////////////////////////////////
    // Tick handling

    pub fn tick(&mut self, evs: &mut Vec<ConnEv>, mut debug_out: LogFile) {
        match self.status {
            ConnStatus::Introduce => {},
            ConnStatus::PingPong { ticks_passed } => {
                if ticks_passed + 1 == PING_TICKS {
                    match self.host {
                        None => {
                            debug_out.write_line(
                                format_args!("{}: Can't send PING, host unknown", self.serv_name));
                        }
                        Some(ref host_) => {
                            debug_out.write_line(
                                format_args!("{}: Ping timeout, sending PING", self.serv_name));
                            wire::ping(&mut self.stream, host_).unwrap();;
                        }
                    }
                    self.status = ConnStatus::WaitPong { ticks_passed: 0 };
                } else {
                    self.status = ConnStatus::PingPong { ticks_passed: ticks_passed + 1 };
                }
            }
            ConnStatus::WaitPong { ticks_passed } => {
                if ticks_passed + 1 == PONG_TICKS {
                    evs.push(ConnEv::Disconnected);
                    self.status = ConnStatus::Introduce;
                } else {
                    self.status = ConnStatus::WaitPong { ticks_passed: ticks_passed + 1 };
                }
            }
        }
    }

    fn reset_ticks(&mut self) {
        match self.status {
            ConnStatus::Introduce => {},
            _ => { self.status = ConnStatus::PingPong { ticks_passed: 0 }; }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Sending messages

    fn introduce(&mut self) {
        wire::user(&self.hostname, &self.realname, &mut self.stream).unwrap();
        wire::nick(&self.nick, &mut self.stream).unwrap();
    }

    ////////////////////////////////////////////////////////////////////////////
    // Receiving messages

    pub fn read_incoming_msg(&mut self, evs: &mut Vec<ConnEv>, logger: &mut Logger) {
        let mut read_buf: [u8; 512] = [0; 512];

        // Handle disconnects
        match self.stream.read(&mut read_buf) {
            Err(err) => {
                evs.push(ConnEv::Err(err));
            }
            Ok(bytes_read) => {
                self.reset_ticks();
                self.add_to_msg_buf(&read_buf[ 0 .. bytes_read ]);
                self.handle_msgs(evs, logger);
                if bytes_read == 0 {
                    evs.push(ConnEv::Disconnected);
                }
            }
        }
    }

    fn add_to_msg_buf(&mut self, slice: &[u8]) {
        // Some invisible ASCII characters causing glitches on some terminals,
        // we filter those out here.
        self.buf.extend(slice.iter().filter(|c| **c != 0x1 /* SOH */ ||
                                                **c != 0x2 /* STX */ ||
                                                **c != 0x0 /* NUL */ ||
                                                **c != 0x4 /* EOT */ ));
    }

    fn handle_msgs(&mut self, evs: &mut Vec<ConnEv>, logger: &mut Logger) {
        while let Some(msg) = Msg::read(&mut self.buf, Some(logger.get_raw_serv_logs(&self.serv_name))) {
            self.handle_msg(msg, evs, logger);
        }
    }

    fn handle_msg(&mut self, msg: Msg, evs: &mut Vec<ConnEv>, logger: &mut Logger) {
        if let &Msg { cmd: Cmd::PING { ref server }, .. } = &msg {
            wire::pong(server, &mut self.stream).unwrap();
        }

        if let ConnStatus::Introduce = self.status {
            self.introduce();
            self.status = ConnStatus::PingPong { ticks_passed: 0 };
        }

        if let &Msg { cmd: Cmd::Reply { num: 002, ref params }, .. } = &msg {
            // 002    RPL_YOURHOST
            //        "Your host is <servername>, running version <ver>"

            // An example <servername>: cherryh.freenode.net[149.56.134.238/8001]

            match parse_servername(params) {
                None => {
                    logger.get_debug_logs().write_line(
                        format_args!("{} Can't parse hostname from params: {:?}",
                                     self.serv_name, params));
                }
                Some(host) => {
                    logger.get_debug_logs().write_line(
                        format_args!("{} host: {}", self.serv_name, host));
                    self.host = Some(host);
                }
            }
        }

        evs.push(ConnEv::Msg(msg));
    }
}

macro_rules! try_opt {
    ($expr:expr) => (match $expr {
        Option::Some(val) => val,
        Option::None => {
            return Option::None
        }
    })
}

/// Try to parse servername in a 002 RPL_YOURHOST reply
fn parse_servername(params: &[String]) -> Option<String> {
    let msg = try_opt!(params.get(1).or(params.get(0)));
    let slice1 = &msg[13..];
    let servername_ends =
        try_opt!(wire::find_byte(slice1.as_bytes(), b'[')
                 .or(wire::find_byte(slice1.as_bytes(), b',')));
    Some((&slice1[..servername_ends]).to_owned())
}

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

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

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.stream.write_all(buf)
    }

    fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> {
        self.stream.write_fmt(fmt)
    }

    fn by_ref(&mut self) -> &mut Conn {
        self
    }
}

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

    #[test]
    fn test_parse_servername_1() {
        let args = vec!["tiny_test".to_owned(),
                        "Your host is adams.freenode.net[94.125.182.252/8001], \
                         running version ircd-seven-1.1.4".to_owned()];
        assert_eq!(parse_servername(&args), Some("adams.freenode.net".to_owned()));
    }

    #[test]
    fn test_parse_servername_2() {
        let args =
            vec!["tiny_test".to_owned(),
                 "Your host is belew.mozilla.org, running version InspIRCd-2.0".to_owned()];
        assert_eq!(parse_servername(&args), Some("belew.mozilla.org".to_owned()));
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::fs::File;
use std::io::prelude::*;
use toml::{self, Parser, Table, Value};

use env::Env;
use file_saver::save_to_file;
use nameutil::crate_name;
use version::Version;

pub fn generate(env: &Env) {
    println!("manipulating sys Cargo.toml for {}", env.config.library_name);

    let path = env.config.target_path.join("Cargo.toml");

    let mut toml_str = String::new();
    if let Ok(mut file) = File::open(&path) {
        file.read_to_string(&mut toml_str).unwrap();
    }
    let empty = toml_str.trim().is_empty();
    let mut parser = Parser::new(&toml_str);
    let mut root_table = parser.parse().unwrap_or_else(BTreeMap::new);

    if empty {
        fill_empty(&mut root_table, env);
    }
    fill_in(&mut root_table, env);

    save_to_file(&path, env.config.make_backup,
        |w| w.write_all(toml::encode_str(&root_table).as_bytes()));
}

fn fill_empty(root: &mut Table, env: &Env) {
    let name = format!("{}_sys", crate_name(&env.config.library_name));
    let package_name = name.replace("_", "-");

    {
        let package = upsert_table(root, "package");
        set_string(package, "name", package_name);
        set_string(package, "version", "0.2.0");
    }

    {
        let lib = upsert_table(root, "lib");
        set_string(lib, "name", name);
    }

    let deps = upsert_table(root, "dependencies");
    for ext_lib in &env.config.external_libraries {
        let ext_package = format!("{}_sys", crate_name(ext_lib))
            .replace("_", "-");
        let dep = upsert_table(deps, &*ext_package);
        set_string(dep, "path", format!("../{}", ext_package));
        set_string(dep, "version", "0.2.0");
    }
}

fn fill_in(root: &mut Table, env: &Env) {
    {
        let package = upsert_table(root, "package");
        set_string(package, "build", "build.rs");
        set_string(package, "links", crate_name(&env.config.library_name));
        //set_string(package, "version", "0.2.0");
    }

    {
        let deps = upsert_table(root, "dependencies");
        set_string(deps, "bitflags", "0.3");
        set_string(deps, "libc", "0.1");
    }

    {
        let build_deps = upsert_table(root, "build-dependencies");
        set_string(build_deps, "pkg-config", "0.3.5");
    }

    {
        let features = upsert_table(root, "features");
        features.clear();
        let versions = env.namespaces.main().versions.iter()
            .filter(|&&v| v > env.config.min_cfg_version);
        versions.fold(None::<Version>, |prev, &version| {
            let prev_array: Vec<Value> = prev.iter()
                .map(|v| Value::String(v.to_string()))
                .collect();
            features.insert(version.to_string(), Value::Array(prev_array));
            Some(version)
        });
    }
}

fn set_string<S: Into<String>>(table: &mut Table, name: &str, new_value: S) {
    if let Some(v) = table.get_mut(name) {
        *v = Value::String(new_value.into());
        return;
    }
    table.insert(name.into(), Value::String(new_value.into()));
}

fn upsert_table<'a, S: Into<String>>(parent: &'a mut Table, name: S) -> &'a mut Table {
    if let &mut Value::Table(ref mut table) = parent.entry(name.into())
            .or_insert_with(|| Value::Table(BTreeMap::new())) {
        table
    }
    else {
        unreachable!()
    }
}
<commit_msg>sys: Bump `bitflags` and `libc` versions<commit_after>use std::collections::BTreeMap;
use std::fs::File;
use std::io::prelude::*;
use toml::{self, Parser, Table, Value};

use env::Env;
use file_saver::save_to_file;
use nameutil::crate_name;
use version::Version;

pub fn generate(env: &Env) {
    println!("manipulating sys Cargo.toml for {}", env.config.library_name);

    let path = env.config.target_path.join("Cargo.toml");

    let mut toml_str = String::new();
    if let Ok(mut file) = File::open(&path) {
        file.read_to_string(&mut toml_str).unwrap();
    }
    let empty = toml_str.trim().is_empty();
    let mut parser = Parser::new(&toml_str);
    let mut root_table = parser.parse().unwrap_or_else(BTreeMap::new);

    if empty {
        fill_empty(&mut root_table, env);
    }
    fill_in(&mut root_table, env);

    save_to_file(&path, env.config.make_backup,
        |w| w.write_all(toml::encode_str(&root_table).as_bytes()));
}

fn fill_empty(root: &mut Table, env: &Env) {
    let name = format!("{}_sys", crate_name(&env.config.library_name));
    let package_name = name.replace("_", "-");

    {
        let package = upsert_table(root, "package");
        set_string(package, "name", package_name);
        set_string(package, "version", "0.2.0");
    }

    {
        let lib = upsert_table(root, "lib");
        set_string(lib, "name", name);
    }

    let deps = upsert_table(root, "dependencies");
    for ext_lib in &env.config.external_libraries {
        let ext_package = format!("{}_sys", crate_name(ext_lib))
            .replace("_", "-");
        let dep = upsert_table(deps, &*ext_package);
        set_string(dep, "path", format!("../{}", ext_package));
        set_string(dep, "version", "0.2.0");
    }
}

fn fill_in(root: &mut Table, env: &Env) {
    {
        let package = upsert_table(root, "package");
        set_string(package, "build", "build.rs");
        set_string(package, "links", crate_name(&env.config.library_name));
        //set_string(package, "version", "0.2.0");
    }

    {
        let deps = upsert_table(root, "dependencies");
        set_string(deps, "bitflags", "0.4");
        set_string(deps, "libc", "0.2");
    }

    {
        let build_deps = upsert_table(root, "build-dependencies");
        set_string(build_deps, "pkg-config", "0.3.5");
    }

    {
        let features = upsert_table(root, "features");
        features.clear();
        let versions = env.namespaces.main().versions.iter()
            .filter(|&&v| v > env.config.min_cfg_version);
        versions.fold(None::<Version>, |prev, &version| {
            let prev_array: Vec<Value> = prev.iter()
                .map(|v| Value::String(v.to_string()))
                .collect();
            features.insert(version.to_string(), Value::Array(prev_array));
            Some(version)
        });
    }
}

fn set_string<S: Into<String>>(table: &mut Table, name: &str, new_value: S) {
    if let Some(v) = table.get_mut(name) {
        *v = Value::String(new_value.into());
        return;
    }
    table.insert(name.into(), Value::String(new_value.into()));
}

fn upsert_table<'a, S: Into<String>>(parent: &'a mut Table, name: S) -> &'a mut Table {
    if let &mut Value::Table(ref mut table) = parent.entry(name.into())
            .or_insert_with(|| Value::Table(BTreeMap::new())) {
        table
    }
    else {
        unreachable!()
    }
}
<|endoftext|>


<commit_before>use super::collect_versions;
use crate::{config::Config, env::Env, file_saver::save_to_file, nameutil, version::Version};
use log::info;
use std::{collections::HashMap, fs::File, io::prelude::*};
use toml::{self, value::Table, Value};

pub fn generate(env: &Env) -> String {
    info!("Generating sys Cargo.toml for {}", env.config.library_name);

    let path = env.config.target_path.join("Cargo.toml");

    let mut toml_str = String::new();
    if let Ok(mut file) = File::open(&path) {
        file.read_to_string(&mut toml_str).unwrap();
    }
    let empty = toml_str.trim().is_empty();
    let mut root_table = toml::from_str(&toml_str).unwrap_or_else(|_| Table::new());
    let crate_name = get_crate_name(&env.config, &root_table);

    if empty {
        fill_empty(&mut root_table, env, &crate_name);
    }
    fill_in(&mut root_table, env);

    save_to_file(&path, env.config.make_backup, |w| {
        w.write_all(toml::to_string(&root_table).unwrap().as_bytes())
    });

    crate_name
}

fn fill_empty(root: &mut Table, env: &Env, crate_name: &str) {
    let package_name = nameutil::exported_crate_name(crate_name);

    {
        let package = upsert_table(root, "package");
        set_string(package, "name", package_name);
        set_string(package, "version", "0.0.1");
        set_string(
            package,
            "links",
            nameutil::shared_libs_to_links(&env.namespaces.main().shared_libs),
        );
        set_string(package, "edition", "2018");
    }

    {
        let lib = upsert_table(root, "lib");
        set_string(lib, "name", crate_name);
    }

    let deps = upsert_table(root, "dependencies");
    for ext_lib in &env.config.external_libraries {
        let ext_package = if ext_lib.crate_name == "cairo" {
            format!("{}-sys-rs", ext_lib.crate_name)
        } else if ext_lib.crate_name == "gdk_pixbuf" {
            "gdk-pixbuf-sys".into()
        } else {
            format!("{}-sys", ext_lib.crate_name)
        };
        let dep = upsert_table(deps, &*ext_package);
        if ext_lib.crate_name == "cairo" {
            set_string(dep, "git", "https://github.com/gtk-rs/cairo");
        } else if ext_package.starts_with("sourceview") {
            set_string(dep, "git", "https://github.com/gtk-rs/sourceview");
        } else {
            set_string(dep, "git", "https://github.com/gtk-rs/sys");
        }
    }
}

fn fill_in(root: &mut Table, env: &Env) {
    {
        let package = upsert_table(root, "package");
        set_string(package, "build", "build.rs");
        //set_string(package, "version", "0.2.0");
    }

    {
        let deps = upsert_table(root, "dependencies");
        set_string(deps, "libc", "0.2");
    }

    {
        let build_deps = upsert_table(root, "build-dependencies");
        set_string(build_deps, "system-deps", "2.0");
    }

    {
        let dev_deps = upsert_table(root, "dev-dependencies");
        set_string(dev_deps, "shell-words", "1.0.0");
        set_string(dev_deps, "tempfile", "3");
        unset(dev_deps, "tempdir");
    }

    {
        let features = upsert_table(root, "features");
        let versions = collect_versions(env);
        versions.keys().fold(None::<Version>, |prev, &version| {
            let prev_array: Vec<Value> =
                get_feature_dependencies(version, prev, &env.config.feature_dependencies)
                    .iter()
                    .map(|s| Value::String(s.clone()))
                    .collect();
            features.insert(version.to_feature(), Value::Array(prev_array));
            Some(version)
        });
        features.insert("dox".to_string(), Value::Array(Vec::new()));
    }

    {
        let meta = upsert_table(root, "package");
        let meta = upsert_table(meta, "metadata");
        let meta = upsert_table(meta, "system-deps");

        let ns = env.namespaces.main();
        let lib_name = ns.package_name.as_ref().unwrap();

        let meta = upsert_table(meta, nameutil::lib_name_to_toml(lib_name));
        set_string(meta, "name", lib_name);
        set_string(meta, "version", env.config.min_cfg_version.to_string());

        // Old version API
        unset(meta, "feature-versions");

        collect_versions(env)
            .iter()
            .filter(|(&v, _)| v > env.config.min_cfg_version)
            .for_each(|(v, lib_version)| {
                let version_section = upsert_table(meta, &v.to_feature());
                set_string(version_section, "version", lib_version.to_string());
            });
    }

    {
        // Small trick to prevent having double quotes around it since toml doesn't like having '.'
        let docs_rs_metadata = upsert_table(root, "package");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "metadata");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "docs");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "rs");
        let mut docs_rs_features = env.config.docs_rs_features.clone();
        docs_rs_features.push("dox".to_owned());
        docs_rs_metadata.insert(
            "features".to_string(),
            Value::Array(
                docs_rs_features
                    .into_iter()
                    .map(Value::String)
                    .collect::<Vec<_>>(),
            ),
        );
    }
}

fn get_feature_dependencies(
    version: Version,
    prev_version: Option<Version>,
    feature_dependencies: &HashMap<Version, Vec<String>>,
) -> Vec<String> {
    let mut vec = Vec::with_capacity(10);
    if let Some(v) = prev_version {
        vec.push(v.to_feature());
    }
    if let Some(dependencies) = feature_dependencies.get(&version) {
        vec.extend_from_slice(dependencies);
    }
    vec
}

/// Returns the name of crate being currently generated.
fn get_crate_name(config: &Config, root: &Table) -> String {
    if let Some(&Value::Table(ref lib)) = root.get("lib") {
        if let Some(&Value::String(ref lib_name)) = lib.get("name") {
            //Converting don't needed as library target names cannot contain hyphens
            return lib_name.to_owned();
        }
    }
    if let Some(&Value::Table(ref package)) = root.get("package") {
        if let Some(&Value::String(ref package_name)) = package.get("name") {
            return nameutil::crate_name(package_name);
        }
    }
    return format!("{}_sys", nameutil::crate_name(&config.library_name));
}

fn set_string<S: Into<String>>(table: &mut Table, name: &str, new_value: S) {
    if let Some(v) = table.get_mut(name) {
        *v = Value::String(new_value.into());
        return;
    }
    table.insert(name.into(), Value::String(new_value.into()));
}

fn unset(table: &mut Table, name: &str) {
    table.remove(name);
}

fn upsert_table<S: Into<String>>(parent: &mut Table, name: S) -> &mut Table {
    if let Value::Table(ref mut table) = *parent
        .entry(name.into())
        .or_insert_with(|| Value::Table(toml::map::Map::new()))
    {
        table
    } else {
        unreachable!()
    }
}
<commit_msg>cargo_toml: Do not overwrite library name for unversioned system-deps<commit_after>use super::collect_versions;
use crate::{config::Config, env::Env, file_saver::save_to_file, nameutil, version::Version};
use log::info;
use std::{collections::HashMap, fs::File, io::prelude::*};
use toml::{self, value::Table, Value};

pub fn generate(env: &Env) -> String {
    info!("Generating sys Cargo.toml for {}", env.config.library_name);

    let path = env.config.target_path.join("Cargo.toml");

    let mut toml_str = String::new();
    if let Ok(mut file) = File::open(&path) {
        file.read_to_string(&mut toml_str).unwrap();
    }
    let empty = toml_str.trim().is_empty();
    let mut root_table = toml::from_str(&toml_str).unwrap_or_else(|_| Table::new());
    let crate_name = get_crate_name(&env.config, &root_table);

    if empty {
        fill_empty(&mut root_table, env, &crate_name);
    }
    fill_in(&mut root_table, env);

    save_to_file(&path, env.config.make_backup, |w| {
        w.write_all(toml::to_string(&root_table).unwrap().as_bytes())
    });

    crate_name
}

fn fill_empty(root: &mut Table, env: &Env, crate_name: &str) {
    let package_name = nameutil::exported_crate_name(crate_name);

    {
        let package = upsert_table(root, "package");
        set_string(package, "name", package_name);
        set_string(package, "version", "0.0.1");
        set_string(
            package,
            "links",
            nameutil::shared_libs_to_links(&env.namespaces.main().shared_libs),
        );
        set_string(package, "edition", "2018");
    }

    {
        let lib = upsert_table(root, "lib");
        set_string(lib, "name", crate_name);
    }

    let deps = upsert_table(root, "dependencies");
    for ext_lib in &env.config.external_libraries {
        let ext_package = if ext_lib.crate_name == "cairo" {
            format!("{}-sys-rs", ext_lib.crate_name)
        } else if ext_lib.crate_name == "gdk_pixbuf" {
            "gdk-pixbuf-sys".into()
        } else {
            format!("{}-sys", ext_lib.crate_name)
        };
        let dep = upsert_table(deps, &*ext_package);
        if ext_lib.crate_name == "cairo" {
            set_string(dep, "git", "https://github.com/gtk-rs/cairo");
        } else if ext_package.starts_with("sourceview") {
            set_string(dep, "git", "https://github.com/gtk-rs/sourceview");
        } else {
            set_string(dep, "git", "https://github.com/gtk-rs/sys");
        }
    }
}

fn fill_in(root: &mut Table, env: &Env) {
    {
        let package = upsert_table(root, "package");
        set_string(package, "build", "build.rs");
        //set_string(package, "version", "0.2.0");
    }

    {
        let deps = upsert_table(root, "dependencies");
        set_string(deps, "libc", "0.2");
    }

    {
        let build_deps = upsert_table(root, "build-dependencies");
        set_string(build_deps, "system-deps", "2.0");
    }

    {
        let dev_deps = upsert_table(root, "dev-dependencies");
        set_string(dev_deps, "shell-words", "1.0.0");
        set_string(dev_deps, "tempfile", "3");
        unset(dev_deps, "tempdir");
    }

    {
        let features = upsert_table(root, "features");
        let versions = collect_versions(env);
        versions.keys().fold(None::<Version>, |prev, &version| {
            let prev_array: Vec<Value> =
                get_feature_dependencies(version, prev, &env.config.feature_dependencies)
                    .iter()
                    .map(|s| Value::String(s.clone()))
                    .collect();
            features.insert(version.to_feature(), Value::Array(prev_array));
            Some(version)
        });
        features.insert("dox".to_string(), Value::Array(Vec::new()));
    }

    {
        let meta = upsert_table(root, "package");
        let meta = upsert_table(meta, "metadata");
        let meta = upsert_table(meta, "system-deps");

        let ns = env.namespaces.main();
        let lib_name = ns.package_name.as_ref().unwrap();

        let meta = upsert_table(meta, nameutil::lib_name_to_toml(lib_name));
        meta.get_mut("name")
            .get_or_insert(&mut Value::String(lib_name.to_owned()));
        set_string(meta, "version", env.config.min_cfg_version.to_string());

        // Old version API
        unset(meta, "feature-versions");

        collect_versions(env)
            .iter()
            .filter(|(&v, _)| v > env.config.min_cfg_version)
            .for_each(|(v, lib_version)| {
                let version_section = upsert_table(meta, &v.to_feature());
                set_string(version_section, "version", lib_version.to_string());
            });
    }

    {
        // Small trick to prevent having double quotes around it since toml doesn't like having '.'
        let docs_rs_metadata = upsert_table(root, "package");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "metadata");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "docs");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "rs");
        let mut docs_rs_features = env.config.docs_rs_features.clone();
        docs_rs_features.push("dox".to_owned());
        docs_rs_metadata.insert(
            "features".to_string(),
            Value::Array(
                docs_rs_features
                    .into_iter()
                    .map(Value::String)
                    .collect::<Vec<_>>(),
            ),
        );
    }
}

fn get_feature_dependencies(
    version: Version,
    prev_version: Option<Version>,
    feature_dependencies: &HashMap<Version, Vec<String>>,
) -> Vec<String> {
    let mut vec = Vec::with_capacity(10);
    if let Some(v) = prev_version {
        vec.push(v.to_feature());
    }
    if let Some(dependencies) = feature_dependencies.get(&version) {
        vec.extend_from_slice(dependencies);
    }
    vec
}

/// Returns the name of crate being currently generated.
fn get_crate_name(config: &Config, root: &Table) -> String {
    if let Some(&Value::Table(ref lib)) = root.get("lib") {
        if let Some(&Value::String(ref lib_name)) = lib.get("name") {
            //Converting don't needed as library target names cannot contain hyphens
            return lib_name.to_owned();
        }
    }
    if let Some(&Value::Table(ref package)) = root.get("package") {
        if let Some(&Value::String(ref package_name)) = package.get("name") {
            return nameutil::crate_name(package_name);
        }
    }
    return format!("{}_sys", nameutil::crate_name(&config.library_name));
}

fn set_string<S: Into<String>>(table: &mut Table, name: &str, new_value: S) {
    if let Some(v) = table.get_mut(name) {
        *v = Value::String(new_value.into());
        return;
    }
    table.insert(name.into(), Value::String(new_value.into()));
}

fn unset(table: &mut Table, name: &str) {
    table.remove(name);
}

fn upsert_table<S: Into<String>>(parent: &mut Table, name: S) -> &mut Table {
    if let Value::Table(ref mut table) = *parent
        .entry(name.into())
        .or_insert_with(|| Value::Table(toml::map::Map::new()))
    {
        table
    } else {
        unreachable!()
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use std::collections::hash_map::HashMap;
use std::io::BufReader;
use std::io::prelude::*;
use std::process::ChildStdin;
use std::process::Command;
use std::process::Stdio;
use std::sync::mpsc;
use std::thread;

use serde_json;
use serde_json::Value;

use update::Update;
use view::View;

pub struct Core {
    stdin: ChildStdin,
    pub update_rx: mpsc::Receiver<Value>,
    rpc_rx: mpsc::Receiver<(u64,Value)>, // ! A simple piping works only for synchronous calls.
    rpc_index: u64,
    current_view: String,
    views: HashMap<String, View>,
}

impl Core {
    pub fn new(executable: &str, file: &str) -> Core {
        // spawn the core process
        let process = Command::new(executable)
                                .arg(file)
                                .stdout(Stdio::piped())
                                .stdin(Stdio::piped())
                                .stderr(Stdio::piped())
                                .env("RUST_BACKTRACE", "1")
                                .spawn()
                                .unwrap_or_else(|e| { panic!("failed to execute core: {}", e) });

        let (update_tx, update_rx) = mpsc::channel();
        let (rpc_tx, rpc_rx) = mpsc::channel();
        let stdout = process.stdout.unwrap();
        thread::spawn(move || {
            for line in BufReader::new(stdout).lines() {
                if let Ok(data) = serde_json::from_slice::<Value>(line.unwrap().as_bytes()) {
                    let req = data.as_object().unwrap();
                    info!("<<< {:?}", req);
                    if let (Some(id), Some(result)) = (req.get("id"), req.get("result")) {
                        rpc_tx.send((id.as_u64().unwrap(), result.clone())).unwrap();
                        info!(">>> {:?}", (id.as_u64().unwrap(), result.clone()));
                    } else if let (Some(method), Some(params)) = (req.get("method"), req.get("params")) {
                        let meth = method.as_str().unwrap();
                        if meth == "set_style" || meth == "scroll_to" || meth == "update" {
                            let request = json!([method.clone(), params.clone()]);
                            update_tx.send(request).unwrap();
                        } else {
                            panic!("Unknown method {:?}.", method.as_str().unwrap());
                        }
                    } else {
                        panic!("Could not parse the core output: {:?}", req);
                    }
                }
            }
        });

        let stderr = process.stderr.unwrap();
        thread::spawn(move || {
            let buf_reader = BufReader::new(stderr);
            for line in buf_reader.lines() {
                if let Ok(line) = line {
                    error!("[core] {}", line);
                }
            }
        });

        let stdin = process.stdin.unwrap();

        let mut core = Core {
            stdin: stdin,
            update_rx: update_rx,
            rpc_rx: rpc_rx,
            rpc_index: 0,
            current_view: "".into(),
            views: HashMap::new(),
        };
        let view_id = core.new_view(Some(file.to_string())).as_str().unwrap().to_string();
        let view = View::new(file.to_string());
        core.views.insert(view_id.clone(), view);
        core.current_view = view_id;
        core
    }

    pub fn update(&mut self, update: &Update) {
        let mut view = self.view();
        view.update(update);
        self.views.insert(self.current_view.clone(), view);
    }

    pub fn view(&self) -> View {
        self.views.clone().get(&self.current_view).unwrap().clone()
    }

    /// Build and send a JSON RPC request, returning the associated request ID to pair it with
    /// the response
    fn request(&mut self, method: &str, params: Value) -> u64 {
        self.rpc_index += 1;
        let message = json!({
            "id": self.rpc_index,
            "method": method,
            "params": params,
        });
        self.send(&message);
        self.rpc_index
    }

    /// Build and send a JSON RPC notification. No synchronous response is expected, so
    /// there is no ID.
    fn notify(&mut self, method: &str, params: Value) {
        let message = json!({
            "method": method,
            "params": params,
        });
        self.send(&message);
    }

    /// Serialize JSON object and send it to the server
    fn send(&mut self, message: &Value) {
        let mut str_msg = serde_json::ser::to_string(&message).unwrap();
        str_msg.push('\n');
        self.stdin.write(str_msg.as_bytes()).unwrap();
    }

    fn call_sync(&mut self, method: &str, params: Value) -> Value {
        let i = self.request(method, params);
        let (id,result) = self.rpc_rx.recv().unwrap();
        assert_eq!(i, id);
        result
    }

    fn call_edit(&mut self, method: &str, params: Option<Value>) {
        let msg = json!({
            "method": method,
            "view_id": &self.current_view,
            "params": params.unwrap_or(json!([])),
        });
        info!(">>> {:?}", msg);
        self.notify("edit", msg);
    }

    fn call_edit_sync(&mut self, method: &str, params: Option<Value>) -> Value{
        let msg = json!({
            "method": method,
            "view_id": &self.current_view,
            "params": params.unwrap_or(json!([])),
        });
        self.call_sync("edit", msg)
    }

    pub fn new_view(&mut self, file_path: Option<String>) -> Value {
        let mut msg: Value;
        if let Some(file_path) = file_path {
            msg = json!({"file_path": file_path});
        } else {
            msg = json!({});
        }
        self.call_sync("new_view", msg)
    }

    pub fn save(&mut self) {
        let views = self.views.clone();
        let save_params = json!({
            "view_id": &self.current_view.clone(),
            "file_path": views.get(&self.current_view).unwrap().filepath.clone(),
        });
        self.request("save", save_params);
    }

    pub fn open(&mut self, filename: &str) {
        self.call_edit("open", Some(json!({"filename": filename})));
    }

    pub fn left(&mut self) { self.call_edit("move_left", None); }
    pub fn left_sel(&mut self) { self.call_edit("move_left_and_modify_selection", None); }

    pub fn right(&mut self) { self.call_edit("move_right", None); }
    pub fn right_sel(&mut self) { self.call_edit("move_right_and_modify_selection", None); }

    pub fn up(&mut self) { self.call_edit("move_up", None); }
    pub fn up_sel(&mut self) { self.call_edit("move_up_and_modify_selection", None); }

    pub fn down(&mut self) { self.call_edit("move_down", None); }
    pub fn down_sel(&mut self) { self.call_edit("move_down_and_modify_selection", None); }

    pub fn del(&mut self) { self.call_edit("delete_backward", None); }

    pub fn page_up(&mut self) { self.call_edit("page_up", None); }
    pub fn page_up_sel(&mut self) { self.call_edit("page_up_and_modify_selection", None); }

    pub fn page_down(&mut self) { self.call_edit("page_down", None); }
    pub fn page_down_sel(&mut self) { self.call_edit("page_down_and_modify_selection", None); }

    pub fn insert_newline(&mut self) { self.call_edit("insert_newline", None); }

    pub fn f1(&mut self) { self.call_edit("debug_rewrap", None); }

    pub fn f2(&mut self) { self.call_edit("debug_test_fg_spans", None); }

    pub fn char(&mut self, ch: char) {
        self.call_edit("insert", Some(json!({"chars": ch})));
    }

    pub fn scroll(&mut self, start: u64, end: u64) {
        self.call_edit("scroll", Some(json!([start, end])));
    }

    pub fn click(&mut self, line: u64, column: u64) {
        self.call_edit("click", Some(json!([line, column, 0, 1])));
    }
    pub fn drag(&mut self, line: u64, column: u64) {
        self.call_edit("drag", Some(json!([line, column, 0, 1])));
    }

    pub fn copy(&mut self) -> String {
        self.call_edit_sync("copy", None).as_str().map(|x|x.into()).unwrap()
    }
    pub fn cut(&mut self) -> String {
        self.call_edit_sync("cut", None).as_str().map(|x|x.into()).unwrap()
    }
    pub fn paste(&mut self, s: String) {
        self.call_edit("insert", Some(json!({"chars": s})));
    }

    #[allow(dead_code)]
    pub fn test(&mut self) {
        self.render_lines(0, 10);
    }

    pub fn render_lines(&mut self, _start: u64, _end: u64) {
        unimplemented!()
        // self.rpc_index += 1;
        // println!("render_lines");
        // let value = ArrayBuilder::new()
        //     .push("rpc")
        //     .push_object(|builder| builder
        //         .insert("index", self.rpc_index)
        //         .insert_array("request", |builder| builder
        //             .push("render_lines")
        //             .push_object(|builder| builder
        //                 .insert("first_line", _start)
        //                 .insert("last_line", _end)
        //             )
        //         )
        //     ).unwrap();
        // self.write(value);
    }

    pub fn render_lines_sync(&mut self, _start: u64, _end: u64) -> Value {
        unimplemented!()
        // self.render_lines(_start, _end);
        // let value = self.rpc_rx.recv().unwrap();
        // let object = value.as_object().unwrap();
        // assert_eq!(self.rpc_index, object.get("index").unwrap().as_u64().unwrap());
        // object.get("result").unwrap().clone()
    }
}
<commit_msg>clippy: get_unwrap<commit_after>#![allow(dead_code)]
use std::collections::hash_map::HashMap;
use std::io::BufReader;
use std::io::prelude::*;
use std::process::ChildStdin;
use std::process::Command;
use std::process::Stdio;
use std::sync::mpsc;
use std::thread;

use serde_json;
use serde_json::Value;

use update::Update;
use view::View;

pub struct Core {
    stdin: ChildStdin,
    pub update_rx: mpsc::Receiver<Value>,
    rpc_rx: mpsc::Receiver<(u64,Value)>, // ! A simple piping works only for synchronous calls.
    rpc_index: u64,
    current_view: String,
    views: HashMap<String, View>,
}

impl Core {
    pub fn new(executable: &str, file: &str) -> Core {
        // spawn the core process
        let process = Command::new(executable)
                                .arg(file)
                                .stdout(Stdio::piped())
                                .stdin(Stdio::piped())
                                .stderr(Stdio::piped())
                                .env("RUST_BACKTRACE", "1")
                                .spawn()
                                .unwrap_or_else(|e| { panic!("failed to execute core: {}", e) });

        let (update_tx, update_rx) = mpsc::channel();
        let (rpc_tx, rpc_rx) = mpsc::channel();
        let stdout = process.stdout.unwrap();
        thread::spawn(move || {
            for line in BufReader::new(stdout).lines() {
                if let Ok(data) = serde_json::from_slice::<Value>(line.unwrap().as_bytes()) {
                    let req = data.as_object().unwrap();
                    info!("<<< {:?}", req);
                    if let (Some(id), Some(result)) = (req.get("id"), req.get("result")) {
                        rpc_tx.send((id.as_u64().unwrap(), result.clone())).unwrap();
                        info!(">>> {:?}", (id.as_u64().unwrap(), result.clone()));
                    } else if let (Some(method), Some(params)) = (req.get("method"), req.get("params")) {
                        let meth = method.as_str().unwrap();
                        if meth == "set_style" || meth == "scroll_to" || meth == "update" {
                            let request = json!([method.clone(), params.clone()]);
                            update_tx.send(request).unwrap();
                        } else {
                            panic!("Unknown method {:?}.", method.as_str().unwrap());
                        }
                    } else {
                        panic!("Could not parse the core output: {:?}", req);
                    }
                }
            }
        });

        let stderr = process.stderr.unwrap();
        thread::spawn(move || {
            let buf_reader = BufReader::new(stderr);
            for line in buf_reader.lines() {
                if let Ok(line) = line {
                    error!("[core] {}", line);
                }
            }
        });

        let stdin = process.stdin.unwrap();

        let mut core = Core {
            stdin: stdin,
            update_rx: update_rx,
            rpc_rx: rpc_rx,
            rpc_index: 0,
            current_view: "".into(),
            views: HashMap::new(),
        };
        let view_id = core.new_view(Some(file.to_string())).as_str().unwrap().to_string();
        let view = View::new(file.to_string());
        core.views.insert(view_id.clone(), view);
        core.current_view = view_id;
        core
    }

    pub fn update(&mut self, update: &Update) {
        let mut view = self.view();
        view.update(update);
        self.views.insert(self.current_view.clone(), view);
    }

    pub fn view(&self) -> View {
        self.views.clone().get(&self.current_view).unwrap().clone()
    }

    /// Build and send a JSON RPC request, returning the associated request ID to pair it with
    /// the response
    fn request(&mut self, method: &str, params: Value) -> u64 {
        self.rpc_index += 1;
        let message = json!({
            "id": self.rpc_index,
            "method": method,
            "params": params,
        });
        self.send(&message);
        self.rpc_index
    }

    /// Build and send a JSON RPC notification. No synchronous response is expected, so
    /// there is no ID.
    fn notify(&mut self, method: &str, params: Value) {
        let message = json!({
            "method": method,
            "params": params,
        });
        self.send(&message);
    }

    /// Serialize JSON object and send it to the server
    fn send(&mut self, message: &Value) {
        let mut str_msg = serde_json::ser::to_string(&message).unwrap();
        str_msg.push('\n');
        self.stdin.write(str_msg.as_bytes()).unwrap();
    }

    fn call_sync(&mut self, method: &str, params: Value) -> Value {
        let i = self.request(method, params);
        let (id,result) = self.rpc_rx.recv().unwrap();
        assert_eq!(i, id);
        result
    }

    fn call_edit(&mut self, method: &str, params: Option<Value>) {
        let msg = json!({
            "method": method,
            "view_id": &self.current_view,
            "params": params.unwrap_or(json!([])),
        });
        info!(">>> {:?}", msg);
        self.notify("edit", msg);
    }

    fn call_edit_sync(&mut self, method: &str, params: Option<Value>) -> Value{
        let msg = json!({
            "method": method,
            "view_id": &self.current_view,
            "params": params.unwrap_or(json!([])),
        });
        self.call_sync("edit", msg)
    }

    pub fn new_view(&mut self, file_path: Option<String>) -> Value {
        let mut msg: Value;
        if let Some(file_path) = file_path {
            msg = json!({"file_path": file_path});
        } else {
            msg = json!({});
        }
        self.call_sync("new_view", msg)
    }

    pub fn save(&mut self) {
        let views = self.views.clone();
        let save_params = json!({
            "view_id": &self.current_view.clone(),
            "file_path": &views[&self.current_view].filepath.clone(),
        });
        self.request("save", save_params);
    }

    pub fn open(&mut self, filename: &str) {
        self.call_edit("open", Some(json!({"filename": filename})));
    }

    pub fn left(&mut self) { self.call_edit("move_left", None); }
    pub fn left_sel(&mut self) { self.call_edit("move_left_and_modify_selection", None); }

    pub fn right(&mut self) { self.call_edit("move_right", None); }
    pub fn right_sel(&mut self) { self.call_edit("move_right_and_modify_selection", None); }

    pub fn up(&mut self) { self.call_edit("move_up", None); }
    pub fn up_sel(&mut self) { self.call_edit("move_up_and_modify_selection", None); }

    pub fn down(&mut self) { self.call_edit("move_down", None); }
    pub fn down_sel(&mut self) { self.call_edit("move_down_and_modify_selection", None); }

    pub fn del(&mut self) { self.call_edit("delete_backward", None); }

    pub fn page_up(&mut self) { self.call_edit("page_up", None); }
    pub fn page_up_sel(&mut self) { self.call_edit("page_up_and_modify_selection", None); }

    pub fn page_down(&mut self) { self.call_edit("page_down", None); }
    pub fn page_down_sel(&mut self) { self.call_edit("page_down_and_modify_selection", None); }

    pub fn insert_newline(&mut self) { self.call_edit("insert_newline", None); }

    pub fn f1(&mut self) { self.call_edit("debug_rewrap", None); }

    pub fn f2(&mut self) { self.call_edit("debug_test_fg_spans", None); }

    pub fn char(&mut self, ch: char) {
        self.call_edit("insert", Some(json!({"chars": ch})));
    }

    pub fn scroll(&mut self, start: u64, end: u64) {
        self.call_edit("scroll", Some(json!([start, end])));
    }

    pub fn click(&mut self, line: u64, column: u64) {
        self.call_edit("click", Some(json!([line, column, 0, 1])));
    }
    pub fn drag(&mut self, line: u64, column: u64) {
        self.call_edit("drag", Some(json!([line, column, 0, 1])));
    }

    pub fn copy(&mut self) -> String {
        self.call_edit_sync("copy", None).as_str().map(|x|x.into()).unwrap()
    }
    pub fn cut(&mut self) -> String {
        self.call_edit_sync("cut", None).as_str().map(|x|x.into()).unwrap()
    }
    pub fn paste(&mut self, s: String) {
        self.call_edit("insert", Some(json!({"chars": s})));
    }

    #[allow(dead_code)]
    pub fn test(&mut self) {
        self.render_lines(0, 10);
    }

    pub fn render_lines(&mut self, _start: u64, _end: u64) {
        unimplemented!()
        // self.rpc_index += 1;
        // println!("render_lines");
        // let value = ArrayBuilder::new()
        //     .push("rpc")
        //     .push_object(|builder| builder
        //         .insert("index", self.rpc_index)
        //         .insert_array("request", |builder| builder
        //             .push("render_lines")
        //             .push_object(|builder| builder
        //                 .insert("first_line", _start)
        //                 .insert("last_line", _end)
        //             )
        //         )
        //     ).unwrap();
        // self.write(value);
    }

    pub fn render_lines_sync(&mut self, _start: u64, _end: u64) -> Value {
        unimplemented!()
        // self.render_lines(_start, _end);
        // let value = self.rpc_rx.recv().unwrap();
        // let object = value.as_object().unwrap();
        // assert_eq!(self.rpc_index, object.get("index").unwrap().as_u64().unwrap());
        // object.get("result").unwrap().clone()
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::rc::Rc;

#[derive(Debug, PartialEq)]
pub enum Token
{
    Lparen,
    Rparen,
    Quote,
    Number(f64),
    Ident(String),
    String(String),
    Error(ParseError),
    End,    // end of string
}

#[derive(Debug, Clone)]
pub enum Value
{
    Nil,
    Number(f64),
    Symbol(Rc<String>),
    String(Rc<String>),
    Builtin(Rc<BuiltinFn>),
    List(List),
}

impl fmt::Display for Value
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            Value::Nil => write!(f, "nil"),
            Value::Number(ref val) => write!(f, "{}", val),
            Value::Symbol(ref val) => write!(f, "{}", val),
            Value::String(ref val) => write!(f, "\"{}\"", val),
            Value::Builtin(ref val) => write!(f, "#builtin:{}", val.name),
            Value::List(ref lst) => match *lst {
                List::Node(ref val) => write!(f, "({})", val),
                List::End => write!(f, "()"),
            }
        }
    }
}

pub struct BuiltinFn
{
    pub name: &'static str,
    pub do_eval: bool,
    pub call: Box<Fn(&List, &mut Scope) -> Result<Value, RuntimeError>>,
}

impl BuiltinFn
{
    pub fn new<F>(n: &'static str, de: bool, f: F) -> Rc<BuiltinFn>
        where F: Fn(&List, &mut Scope) -> Result<Value, RuntimeError> + 'static
    {
        Rc::new(BuiltinFn{ name: n, do_eval: de, call: Box::new(f) })
    }
}

impl fmt::Debug for BuiltinFn
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        write!(f, "BuiltinFn({})", self.name)
    }
}

#[derive(Debug, Clone)]
pub enum List
{
    Node(Rc<Cons>),
    End,
}

#[derive(Debug, Clone)]
pub struct Cons
{
    pub car: Value,
    pub cdr: List,
}

impl fmt::Display for Cons
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            Cons{ ref car, cdr: List::Node(ref next) } => write!(f, "{} {}", car, next),
            Cons{ ref car, cdr: List::End } => write!(f, "{}", car),
        }
    }
}

pub trait Scope
{
    fn get(&self, key: &str) -> Option<Value>;
    fn set(&mut self, key: &str, val: Value);
}

#[derive(Debug, PartialEq)]
pub enum ParseError
{
    UnclosedString,
    InvalidNumber,
    UnclosedList,
    UnexpectedRparen,
    NoQuoteArg,
    EndOfStream,
}

impl fmt::Display for ParseError
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            ParseError::UnclosedString => write!(f, "Unclosed string"),
            ParseError::InvalidNumber => write!(f, "Invalid number literal"),
            ParseError::UnclosedList => write!(f, "Unclosed list"),
            ParseError::UnexpectedRparen => write!(f, "Unexpected ')'"),
            ParseError::NoQuoteArg => write!(f, "Missing quote argument"),
            ParseError::EndOfStream => write!(f, "End of stream"),
        }
    }
}

#[derive(Debug)]
pub enum RuntimeError
{
    UnkSymbol(Rc<String>),
    InvalidCall(&'static str),
    InvalidArgNum(u32),
    InvalidArgType(&'static str, &'static str),
    Unimplemented,
}

impl fmt::Display for RuntimeError
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            RuntimeError::UnkSymbol(ref s) => write!(f, "Unbound variable: {}", s),
            RuntimeError::InvalidCall(t) => write!(f, "Invalid call on a {} value", t),
            RuntimeError::InvalidArgNum(n) => write!(f, "Incorrect number or arguments (Expected {})", n),
            RuntimeError::InvalidArgType(a, b) => write!(f, "Invalid argument: expected {}, but found {}", a, b),
            RuntimeError::Unimplemented => write!(f, "Unimplemented"),
        }
    }
}
<commit_msg>implement Display for List<commit_after>use std::fmt;
use std::rc::Rc;

#[derive(Debug, PartialEq)]
pub enum Token
{
    Lparen,
    Rparen,
    Quote,
    Number(f64),
    Ident(String),
    String(String),
    Error(ParseError),
    End,    // end of string
}

#[derive(Debug, Clone)]
pub enum Value
{
    Nil,
    Number(f64),
    Symbol(Rc<String>),
    String(Rc<String>),
    Builtin(Rc<BuiltinFn>),
    List(List),
}

impl fmt::Display for Value
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            Value::Nil => write!(f, "nil"),
            Value::Number(ref val) => write!(f, "{}", val),
            Value::Symbol(ref val) => write!(f, "{}", val),
            Value::String(ref val) => write!(f, "\"{}\"", val),
            Value::Builtin(ref val) => write!(f, "#builtin:{}", val.name),
            Value::List(ref val) => write!(f, "{}", val),
        }
    }
}

pub struct BuiltinFn
{
    pub name: &'static str,
    pub do_eval: bool,
    pub call: Box<Fn(&List, &mut Scope) -> Result<Value, RuntimeError>>,
}

impl BuiltinFn
{
    pub fn new<F>(n: &'static str, de: bool, f: F) -> Rc<BuiltinFn>
        where F: Fn(&List, &mut Scope) -> Result<Value, RuntimeError> + 'static
    {
        Rc::new(BuiltinFn{ name: n, do_eval: de, call: Box::new(f) })
    }
}

impl fmt::Debug for BuiltinFn
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        write!(f, "BuiltinFn({})", self.name)
    }
}

#[derive(Debug, Clone)]
pub enum List
{
    Node(Rc<Cons>),
    End,
}

impl fmt::Display for List
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            List::Node(ref val) => write!(f, "({})", val),
            List::End => write!(f, "()"),
        }
    }
}

#[derive(Debug, Clone)]
pub struct Cons
{
    pub car: Value,
    pub cdr: List,
}

impl fmt::Display for Cons
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            Cons{ ref car, cdr: List::Node(ref next) } => write!(f, "{} {}", car, next),
            Cons{ ref car, cdr: List::End } => write!(f, "{}", car),
        }
    }
}

pub trait Scope
{
    fn get(&self, key: &str) -> Option<Value>;
    fn set(&mut self, key: &str, val: Value);
}

#[derive(Debug, PartialEq)]
pub enum ParseError
{
    UnclosedString,
    InvalidNumber,
    UnclosedList,
    UnexpectedRparen,
    NoQuoteArg,
    EndOfStream,
}

impl fmt::Display for ParseError
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            ParseError::UnclosedString => write!(f, "Unclosed string"),
            ParseError::InvalidNumber => write!(f, "Invalid number literal"),
            ParseError::UnclosedList => write!(f, "Unclosed list"),
            ParseError::UnexpectedRparen => write!(f, "Unexpected ')'"),
            ParseError::NoQuoteArg => write!(f, "Missing quote argument"),
            ParseError::EndOfStream => write!(f, "End of stream"),
        }
    }
}

#[derive(Debug)]
pub enum RuntimeError
{
    UnkSymbol(Rc<String>),
    InvalidCall(&'static str),
    InvalidArgNum(u32),
    InvalidArgType(&'static str, &'static str),
    Unimplemented,
}

impl fmt::Display for RuntimeError
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        match *self {
            RuntimeError::UnkSymbol(ref s) => write!(f, "Unbound variable: {}", s),
            RuntimeError::InvalidCall(t) => write!(f, "Invalid call on a {} value", t),
            RuntimeError::InvalidArgNum(n) => write!(f, "Incorrect number or arguments (Expected {})", n),
            RuntimeError::InvalidArgType(a, b) => write!(f, "Invalid argument: expected {}, but found {}", a, b),
            RuntimeError::Unimplemented => write!(f, "Unimplemented"),
        }
    }
}
<|endoftext|>


<commit_before>
// Std
use std::io::Write;

// Internal
use app::parser::Parser;

pub struct PowerShellGen<'a, 'b>
    where 'a: 'b
{
    p: &'b Parser<'a, 'b>,
}

impl<'a, 'b> PowerShellGen<'a, 'b> {
    pub fn new(p: &'b Parser<'a, 'b>) -> Self { PowerShellGen { p: p } }

    pub fn generate_to<W: Write>(&self, buf: &mut W) {
        let bin_name = self.p.meta.bin_name.as_ref().unwrap();

        let (subcommands_detection_cases, subcommands_cases) = generate_inner(self.p, "");

        let mut bin_names = vec![bin_name.to_string(), format!("./{0}", bin_name)];
        if cfg!(windows) {
            bin_names.push(format!("{0}.exe", bin_name));
            bin_names.push(format!(r".\{0}", bin_name));
            bin_names.push(format!(r".\{0}.exe", bin_name));
            bin_names.push(format!(r"./{0}.exe", bin_name));
        }

        let bin_names = bin_names.iter().fold(String::new(), |previous, current| {
            format!("{0}, '{1}'", previous, current)
        });
        let bin_names = bin_names.trim_left_matches(", ");

        let result = format!(r#"
@({bin_names}) | %{{
    Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {{
        param($wordToComplete, $commandAst, $cursorPosition)

        $command = '_{bin_name}'
        $commandAst.CommandElements |
            Select-Object -Skip 1 |
            %{{
                switch ($_.ToString()) {{
{subcommands_detection_cases}
                }}
            }}

        $completions = @()

        switch ($command) {{
{subcommands_cases}
        }}

        $completions |
            ?{{ $_ -like "$wordToComplete*" }} |
            Sort-Object |
            %{{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }}
    }}
}}
"#,
            bin_names = bin_names,
            bin_name = bin_name,
            subcommands_detection_cases = subcommands_detection_cases,
            subcommands_cases = subcommands_cases
        );

        w!(buf, result.as_bytes());
    }
}

fn generate_inner<'a, 'b>(p: &Parser<'a, 'b>, previous_command_name: &str) -> (String, String) {
    debugln!("PowerShellGen::generate_inner;");
    let command_name = format!("{}_{}", previous_command_name, &p.meta.name);

    let mut subcommands_detection_cases = if previous_command_name == "" {
        String::new()
    } else {
        format!(r"
                    '{0}' {{
                        $command += '_{0}'
                        break
                    }}
",
                &p.meta.name)
    };

    let mut completions = String::new();
    for subcommand in &p.subcommands {
        completions.push_str(&format!("'{}', ", &subcommand.p.meta.name));
    }
    for short in shorts!(p) {
        completions.push_str(&format!("'-{}', ", short));
    }
    for long in longs!(p) {
        completions.push_str(&format!("'--{}', ", long));
    }

    let mut subcommands_cases = format!(r"
            '{}' {{
                $completions = @({})
            }}
",
                                        &command_name,
                                        completions.trim_right_matches(", "));

    for subcommand in &p.subcommands {
        let (subcommand_subcommands_detection_cases, subcommand_subcommands_cases) =
            generate_inner(&subcommand.p, &command_name);
        subcommands_detection_cases.push_str(&subcommand_subcommands_detection_cases);
        subcommands_cases.push_str(&subcommand_subcommands_cases);
    }

    (subcommands_detection_cases, subcommands_cases)
}
<commit_msg>fix(PowerShell Completions): fixes a bug where powershells completions cant be used if no subcommands are defined<commit_after>
// Std
use std::io::Write;

// Internal
use app::parser::Parser;
use INTERNAL_ERROR_MSG;

pub struct PowerShellGen<'a, 'b>
    where 'a: 'b
{
    p: &'b Parser<'a, 'b>,
}

impl<'a, 'b> PowerShellGen<'a, 'b> {
    pub fn new(p: &'b Parser<'a, 'b>) -> Self { PowerShellGen { p: p } }

    pub fn generate_to<W: Write>(&self, buf: &mut W) {
        let bin_name = self.p.meta.bin_name.as_ref().unwrap();

        let (subcommands_detection_cases, subcommands_cases) = generate_inner(self.p, "");

        let mut bin_names = vec![bin_name.to_string(), format!("./{0}", bin_name)];
        if cfg!(windows) {
            bin_names.push(format!("{0}.exe", bin_name));
            bin_names.push(format!(r".\{0}", bin_name));
            bin_names.push(format!(r".\{0}.exe", bin_name));
            bin_names.push(format!(r"./{0}.exe", bin_name));
        }

        let bin_names = bin_names.iter().fold(String::new(), |previous, current| {
            format!("{0}, '{1}'", previous, current)
        });
        let bin_names = bin_names.trim_left_matches(", ");

        let result = format!(r#"
@({bin_names}) | %{{
    Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {{
        param($wordToComplete, $commandAst, $cursorPosition)

        $command = '_{bin_name}'
        $commandAst.CommandElements |
            Select-Object -Skip 1 |
            %{{
                switch ($_.ToString()) {{
{subcommands_detection_cases}
                    default {{
                        break
                    }}
                }}
            }}

        $completions = @()

        switch ($command) {{
{subcommands_cases}
        }}

        $completions |
            ?{{ $_ -like "$wordToComplete*" }} |
            Sort-Object |
            %{{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }}
    }}
}}
"#,
            bin_names = bin_names,
            bin_name = bin_name,
            subcommands_detection_cases = subcommands_detection_cases,
            subcommands_cases = subcommands_cases
        );

        w!(buf, result.as_bytes());
    }
}

fn generate_inner<'a, 'b>(p: &Parser<'a, 'b>, previous_command_name: &str) -> (String, String) {
    debugln!("PowerShellGen::generate_inner;");
    let command_name = if previous_command_name.is_empty() {
        format!("{}_{}", previous_command_name, &p.meta.bin_name.as_ref().expect(INTERNAL_ERROR_MSG))
    } else {
        format!("{}_{}", previous_command_name, &p.meta.name)
    };

    let mut subcommands_detection_cases = if previous_command_name == "" {
        String::new()
    } else {
        format!(r"
                    '{0}' {{
                        $command += '_{0}'
                        break
                    }}
",
                &p.meta.name)
    };

    let mut completions = String::new();
    for subcommand in &p.subcommands {
        completions.push_str(&format!("'{}', ", &subcommand.p.meta.name));
    }
    for short in shorts!(p) {
        completions.push_str(&format!("'-{}', ", short));
    }
    for long in longs!(p) {
        completions.push_str(&format!("'--{}', ", long));
    }

    let mut subcommands_cases = format!(r"
            '{}' {{
                $completions = @({})
            }}
",
                                        &command_name,
                                        completions.trim_right_matches(", "));

    for subcommand in &p.subcommands {
        let (subcommand_subcommands_detection_cases, subcommand_subcommands_cases) =
            generate_inner(&subcommand.p, &command_name);
        subcommands_detection_cases.push_str(&subcommand_subcommands_detection_cases);
        subcommands_cases.push_str(&subcommand_subcommands_cases);
    }

    (subcommands_detection_cases, subcommands_cases)
}
<|endoftext|>


<commit_before>use crate::{private, Mapping, Value};
use std::fmt;
use std::ops;

/// A type that can be used to index into a `serde_yaml::Value`. See the `get`
/// and `get_mut` methods of `Value`.
///
/// This trait is sealed and cannot be implemented for types outside of
/// `serde_yaml`.
pub trait Index: private::Sealed {
    /// Return None if the key is not already in the sequence or object.
    #[doc(hidden)]
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>;

    /// Return None if the key is not already in the sequence or object.
    #[doc(hidden)]
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value>;

    /// Panic if sequence index out of bounds. If key is not already in the object,
    /// insert it with a value of null. Panic if Value is a type that cannot be
    /// indexed into, except if Value is null then it can be treated as an empty
    /// object.
    #[doc(hidden)]
    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value;
}

impl Index for usize {
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        match v.untag_ref() {
            Value::Sequence(vec) => vec.get(*self),
            Value::Mapping(vec) => vec.get(&Value::Number((*self).into())),
            _ => None,
        }
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        match v.untag_mut() {
            Value::Sequence(vec) => vec.get_mut(*self),
            Value::Mapping(vec) => vec.get_mut(&Value::Number((*self).into())),
            _ => None,
        }
    }
    fn index_or_insert<'v>(&self, mut v: &'v mut Value) -> &'v mut Value {
        loop {
            match v {
                Value::Sequence(vec) => {
                    let len = vec.len();
                    return vec.get_mut(*self).unwrap_or_else(|| {
                        panic!(
                            "cannot access index {} of YAML sequence of length {}",
                            self, len
                        )
                    });
                }
                Value::Mapping(map) => {
                    let n = Value::Number((*self).into());
                    return map.entry(n).or_insert(Value::Null);
                }
                Value::Tagged(tagged) => v = &mut tagged.value,
                _ => panic!("cannot access index {} of YAML {}", self, Type(v)),
            }
        }
    }
}

impl Index for Value {
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        match v.untag_ref() {
            Value::Mapping(map) => map.get(self),
            _ => None,
        }
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        match v.untag_mut() {
            Value::Mapping(map) => map.get_mut(self),
            _ => None,
        }
    }
    fn index_or_insert<'v>(&self, mut v: &'v mut Value) -> &'v mut Value {
        if let Value::Null = *v {
            let mut map = Mapping::new();
            map.insert(self.clone(), Value::Null);
            *v = Value::Mapping(map);
        }
        loop {
            match v {
                Value::Mapping(map) => return map.entry(self.clone()).or_insert(Value::Null),
                Value::Tagged(tagged) => v = &mut tagged.value,
                _ => panic!("cannot access key {:?} in YAML {}", self, Type(v)),
            }
        }
    }
}

impl Index for str {
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        Value::String(self.into()).index_into(v)
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        Value::String(self.into()).index_into_mut(v)
    }
    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
        Value::String(self.into()).index_or_insert(v)
    }
}

impl Index for String {
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        Value::String(self.clone()).index_into(v)
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        Value::String(self.clone()).index_into_mut(v)
    }
    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
        Value::String(self.clone()).index_or_insert(v)
    }
}

impl<'a, T> Index for &'a T
where
    T: ?Sized + Index,
{
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        (**self).index_into(v)
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        (**self).index_into_mut(v)
    }
    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
        (**self).index_or_insert(v)
    }
}

/// Used in panic messages.
struct Type<'a>(&'a Value);

impl<'a> fmt::Display for Type<'a> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Value::Null => formatter.write_str("null"),
            Value::Bool(_) => formatter.write_str("boolean"),
            Value::Number(_) => formatter.write_str("number"),
            Value::String(_) => formatter.write_str("string"),
            Value::Sequence(_) => formatter.write_str("sequence"),
            Value::Mapping(_) => formatter.write_str("mapping"),
            Value::Tagged(_) => unreachable!(),
        }
    }
}

// The usual semantics of Index is to panic on invalid indexing.
//
// That said, the usual semantics are for things like `Vec` and `BTreeMap` which
// have different use cases than Value. If you are working with a Vec, you know
// that you are working with a Vec and you can get the len of the Vec and make
// sure your indices are within bounds. The Value use cases are more
// loosey-goosey. You got some YAML from an endpoint and you want to pull values
// out of it. Outside of this Index impl, you already have the option of using
// `value.as_sequence()` and working with the Vec directly, or matching on
// `Value::Sequence` and getting the Vec directly. The Index impl means you can
// skip that and index directly into the thing using a concise syntax. You don't
// have to check the type, you don't have to check the len, it is all about what
// you expect the Value to look like.
//
// Basically the use cases that would be well served by panicking here are
// better served by using one of the other approaches: `get` and `get_mut`,
// `as_sequence`, or match. The value of this impl is that it adds a way of
// working with Value that is not well served by the existing approaches:
// concise and careless and sometimes that is exactly what you want.
impl<I> ops::Index<I> for Value
where
    I: Index,
{
    type Output = Value;

    /// Index into a `serde_yaml::Value` using the syntax `value[0]` or
    /// `value["k"]`.
    ///
    /// Returns `Value::Null` if the type of `self` does not match the type of
    /// the index, for example if the index is a string and `self` is a sequence
    /// or a number. Also returns `Value::Null` if the given key does not exist
    /// in the map or the given index is not within the bounds of the sequence.
    ///
    /// For retrieving deeply nested values, you should have a look at the
    /// `Value::pointer` method.
    ///
    /// # Examples
    ///
    /// ```
    /// # use serde_yaml::Value;
    /// #
    /// # fn main() -> serde_yaml::Result<()> {
    /// let data: serde_yaml::Value = serde_yaml::from_str(r#"{ x: { y: [z, zz] } }"#)?;
    ///
    /// assert_eq!(data["x"]["y"], serde_yaml::from_str::<Value>(r#"["z", "zz"]"#).unwrap());
    /// assert_eq!(data["x"]["y"][0], serde_yaml::from_str::<Value>(r#""z""#).unwrap());
    ///
    /// assert_eq!(data["a"], serde_yaml::from_str::<Value>(r#"null"#).unwrap()); // returns null for undefined values
    /// assert_eq!(data["a"]["b"], serde_yaml::from_str::<Value>(r#"null"#).unwrap()); // does not panic
    /// # Ok(())
    /// # }
    /// ```
    fn index(&self, index: I) -> &Value {
        static NULL: Value = Value::Null;
        index.index_into(self).unwrap_or(&NULL)
    }
}

impl<I> ops::IndexMut<I> for Value
where
    I: Index,
{
    /// Write into a `serde_yaml::Value` using the syntax `value[0] = ...` or
    /// `value["k"] = ...`.
    ///
    /// If the index is a number, the value must be a sequence of length bigger
    /// than the index. Indexing into a value that is not a sequence or a
    /// sequence that is too small will panic.
    ///
    /// If the index is a string, the value must be an object or null which is
    /// treated like an empty object. If the key is not already present in the
    /// object, it will be inserted with a value of null. Indexing into a value
    /// that is neither an object nor null will panic.
    ///
    /// # Examples
    ///
    /// ```
    /// # fn main() -> serde_yaml::Result<()> {
    /// let mut data: serde_yaml::Value = serde_yaml::from_str(r#"{x: 0}"#)?;
    ///
    /// // replace an existing key
    /// data["x"] = serde_yaml::from_str(r#"1"#)?;
    ///
    /// // insert a new key
    /// data["y"] = serde_yaml::from_str(r#"[false, false, false]"#)?;
    ///
    /// // replace a value in a sequence
    /// data["y"][0] = serde_yaml::from_str(r#"true"#)?;
    ///
    /// // inserted a deeply nested key
    /// data["a"]["b"]["c"]["d"] = serde_yaml::from_str(r#"true"#)?;
    ///
    /// println!("{:?}", data);
    /// # Ok(())
    /// # }
    /// ```
    fn index_mut(&mut self, index: I) -> &mut Value {
        index.index_or_insert(self)
    }
}
<commit_msg>Eliminate double clone from index_or_insert<commit_after>use crate::mapping::Entry;
use crate::{private, Mapping, Value};
use std::fmt;
use std::ops;

/// A type that can be used to index into a `serde_yaml::Value`. See the `get`
/// and `get_mut` methods of `Value`.
///
/// This trait is sealed and cannot be implemented for types outside of
/// `serde_yaml`.
pub trait Index: private::Sealed {
    /// Return None if the key is not already in the sequence or object.
    #[doc(hidden)]
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>;

    /// Return None if the key is not already in the sequence or object.
    #[doc(hidden)]
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value>;

    /// Panic if sequence index out of bounds. If key is not already in the object,
    /// insert it with a value of null. Panic if Value is a type that cannot be
    /// indexed into, except if Value is null then it can be treated as an empty
    /// object.
    #[doc(hidden)]
    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value;
}

impl Index for usize {
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        match v.untag_ref() {
            Value::Sequence(vec) => vec.get(*self),
            Value::Mapping(vec) => vec.get(&Value::Number((*self).into())),
            _ => None,
        }
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        match v.untag_mut() {
            Value::Sequence(vec) => vec.get_mut(*self),
            Value::Mapping(vec) => vec.get_mut(&Value::Number((*self).into())),
            _ => None,
        }
    }
    fn index_or_insert<'v>(&self, mut v: &'v mut Value) -> &'v mut Value {
        loop {
            match v {
                Value::Sequence(vec) => {
                    let len = vec.len();
                    return vec.get_mut(*self).unwrap_or_else(|| {
                        panic!(
                            "cannot access index {} of YAML sequence of length {}",
                            self, len
                        )
                    });
                }
                Value::Mapping(map) => {
                    let n = Value::Number((*self).into());
                    return map.entry(n).or_insert(Value::Null);
                }
                Value::Tagged(tagged) => v = &mut tagged.value,
                _ => panic!("cannot access index {} of YAML {}", self, Type(v)),
            }
        }
    }
}

impl Index for Value {
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        match v.untag_ref() {
            Value::Mapping(map) => map.get(self),
            _ => None,
        }
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        match v.untag_mut() {
            Value::Mapping(map) => map.get_mut(self),
            _ => None,
        }
    }
    fn index_or_insert<'v>(&self, mut v: &'v mut Value) -> &'v mut Value {
        if let Value::Null = *v {
            *v = Value::Mapping(Mapping::new());
            return match v {
                Value::Mapping(map) => match map.entry(self.clone()) {
                    Entry::Vacant(entry) => entry.insert(Value::Null),
                    Entry::Occupied(_) => unreachable!(),
                },
                _ => unreachable!(),
            };
        }
        loop {
            match v {
                Value::Mapping(map) => return map.entry(self.clone()).or_insert(Value::Null),
                Value::Tagged(tagged) => v = &mut tagged.value,
                _ => panic!("cannot access key {:?} in YAML {}", self, Type(v)),
            }
        }
    }
}

impl Index for str {
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        Value::String(self.into()).index_into(v)
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        Value::String(self.into()).index_into_mut(v)
    }
    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
        Value::String(self.into()).index_or_insert(v)
    }
}

impl Index for String {
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        Value::String(self.clone()).index_into(v)
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        Value::String(self.clone()).index_into_mut(v)
    }
    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
        Value::String(self.clone()).index_or_insert(v)
    }
}

impl<'a, T> Index for &'a T
where
    T: ?Sized + Index,
{
    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
        (**self).index_into(v)
    }
    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
        (**self).index_into_mut(v)
    }
    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
        (**self).index_or_insert(v)
    }
}

/// Used in panic messages.
struct Type<'a>(&'a Value);

impl<'a> fmt::Display for Type<'a> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Value::Null => formatter.write_str("null"),
            Value::Bool(_) => formatter.write_str("boolean"),
            Value::Number(_) => formatter.write_str("number"),
            Value::String(_) => formatter.write_str("string"),
            Value::Sequence(_) => formatter.write_str("sequence"),
            Value::Mapping(_) => formatter.write_str("mapping"),
            Value::Tagged(_) => unreachable!(),
        }
    }
}

// The usual semantics of Index is to panic on invalid indexing.
//
// That said, the usual semantics are for things like `Vec` and `BTreeMap` which
// have different use cases than Value. If you are working with a Vec, you know
// that you are working with a Vec and you can get the len of the Vec and make
// sure your indices are within bounds. The Value use cases are more
// loosey-goosey. You got some YAML from an endpoint and you want to pull values
// out of it. Outside of this Index impl, you already have the option of using
// `value.as_sequence()` and working with the Vec directly, or matching on
// `Value::Sequence` and getting the Vec directly. The Index impl means you can
// skip that and index directly into the thing using a concise syntax. You don't
// have to check the type, you don't have to check the len, it is all about what
// you expect the Value to look like.
//
// Basically the use cases that would be well served by panicking here are
// better served by using one of the other approaches: `get` and `get_mut`,
// `as_sequence`, or match. The value of this impl is that it adds a way of
// working with Value that is not well served by the existing approaches:
// concise and careless and sometimes that is exactly what you want.
impl<I> ops::Index<I> for Value
where
    I: Index,
{
    type Output = Value;

    /// Index into a `serde_yaml::Value` using the syntax `value[0]` or
    /// `value["k"]`.
    ///
    /// Returns `Value::Null` if the type of `self` does not match the type of
    /// the index, for example if the index is a string and `self` is a sequence
    /// or a number. Also returns `Value::Null` if the given key does not exist
    /// in the map or the given index is not within the bounds of the sequence.
    ///
    /// For retrieving deeply nested values, you should have a look at the
    /// `Value::pointer` method.
    ///
    /// # Examples
    ///
    /// ```
    /// # use serde_yaml::Value;
    /// #
    /// # fn main() -> serde_yaml::Result<()> {
    /// let data: serde_yaml::Value = serde_yaml::from_str(r#"{ x: { y: [z, zz] } }"#)?;
    ///
    /// assert_eq!(data["x"]["y"], serde_yaml::from_str::<Value>(r#"["z", "zz"]"#).unwrap());
    /// assert_eq!(data["x"]["y"][0], serde_yaml::from_str::<Value>(r#""z""#).unwrap());
    ///
    /// assert_eq!(data["a"], serde_yaml::from_str::<Value>(r#"null"#).unwrap()); // returns null for undefined values
    /// assert_eq!(data["a"]["b"], serde_yaml::from_str::<Value>(r#"null"#).unwrap()); // does not panic
    /// # Ok(())
    /// # }
    /// ```
    fn index(&self, index: I) -> &Value {
        static NULL: Value = Value::Null;
        index.index_into(self).unwrap_or(&NULL)
    }
}

impl<I> ops::IndexMut<I> for Value
where
    I: Index,
{
    /// Write into a `serde_yaml::Value` using the syntax `value[0] = ...` or
    /// `value["k"] = ...`.
    ///
    /// If the index is a number, the value must be a sequence of length bigger
    /// than the index. Indexing into a value that is not a sequence or a
    /// sequence that is too small will panic.
    ///
    /// If the index is a string, the value must be an object or null which is
    /// treated like an empty object. If the key is not already present in the
    /// object, it will be inserted with a value of null. Indexing into a value
    /// that is neither an object nor null will panic.
    ///
    /// # Examples
    ///
    /// ```
    /// # fn main() -> serde_yaml::Result<()> {
    /// let mut data: serde_yaml::Value = serde_yaml::from_str(r#"{x: 0}"#)?;
    ///
    /// // replace an existing key
    /// data["x"] = serde_yaml::from_str(r#"1"#)?;
    ///
    /// // insert a new key
    /// data["y"] = serde_yaml::from_str(r#"[false, false, false]"#)?;
    ///
    /// // replace a value in a sequence
    /// data["y"][0] = serde_yaml::from_str(r#"true"#)?;
    ///
    /// // inserted a deeply nested key
    /// data["a"]["b"]["c"]["d"] = serde_yaml::from_str(r#"true"#)?;
    ///
    /// println!("{:?}", data);
    /// # Ok(())
    /// # }
    /// ```
    fn index_mut(&mut self, index: I) -> &mut Value {
        index.index_or_insert(self)
    }
}
<|endoftext|>