<commit_before>extern crate zmq;
extern crate time;

use self::zmq::PollItem;
use self::zmq::Socket as ZSocket;

use errors::common::CommonError;
use errors::pool::PoolError;
use services::pool::events::*;
use services::pool::types::*;
use std::collections::HashMap;
use std::cell::RefCell;
use super::time::Duration;
use utils::sequence::SequenceUtils;
use time::Tm;

pub trait Networker {
    fn new() -> Self;
    fn fetch_events(&self, poll_items: &[PollItem]) -> Vec<PoolEvent>;
    fn process_event(&mut self, pe: Option<NetworkerEvent>) -> Option<RequestEvent>;
    fn get_timeout(&self) -> ((String, String), i64);
    fn get_poll_items(&self) -> Vec<PollItem>;
}

pub struct ZMQNetworker {
    req_id_mappings: HashMap<String, i32>,
    pool_connections: HashMap<i32, PoolConnection>,
    nodes: Vec<RemoteNode>,
}

const POOL_CON_ACTIVE_TO: i64 = 5;

impl Networker for ZMQNetworker {
    fn new() -> Self {
        ZMQNetworker {
            req_id_mappings: HashMap::new(),
            pool_connections: HashMap::new(),
            nodes: Vec::new(),
        }
    }

    fn fetch_events(&self, poll_items: &[PollItem]) -> Vec<PoolEvent> {
        let mut cnt = 0;
        self.pool_connections.iter().map(|(_, pc)| {
            let ocnt = cnt;
            cnt = cnt + pc.nodes.len();
            pc.fetch_events(&poll_items[ocnt..cnt])
        }).flat_map(|v| v.into_iter()).collect()
    }

    fn process_event(&mut self, pe: Option<NetworkerEvent>) -> Option<RequestEvent> {
        match pe.clone() {
            Some(NetworkerEvent::SendAllRequest(_, req_id)) | Some(NetworkerEvent::SendOneRequest(_, req_id)) | Some(NetworkerEvent::Resend(req_id)) => {
                trace!("current mappings: {:?}", self.req_id_mappings);
                let num = self.req_id_mappings.get(&req_id).map(|i| i.clone()).or_else(|| {
                    self.req_id_mappings.values()
                        .fold(HashMap::new(), |mut acc, pc_id| {
                            *acc.entry(pc_id).or_insert(0) += 1;
                            acc
                        }).iter()
                        .filter(|&(pc_id, cnt)|
                            cnt < &5 && self.pool_connections.get(pc_id).map(|pc| pc.is_active()).unwrap_or(false))
                        .last().map(|(pc_id, _)| **pc_id)
                });
                match num {
                    Some(idx) => {
                        trace!("send request in existing conn");
                        self.pool_connections.get(&idx).map(|pc| {
                            pc.send_request(pe);
                        });
                        self.req_id_mappings.insert(req_id.clone(), idx);
                    }
                    None => {
                        trace!("send request in new conn");
                        let pc_id = SequenceUtils::get_next_id();
                        let pc = PoolConnection::new(self.nodes.clone());
                        pc.send_request(pe);
                        self.pool_connections.insert(pc_id, pc);
                        self.req_id_mappings.insert(req_id.clone(), pc_id);
                    }
                }
                None
            }
            Some(NetworkerEvent::NodesStateUpdated(nodes)) => {
                self.nodes = nodes;
                None
            }
            Some(NetworkerEvent::ExtendTimeout(req_id, node_alias)) => {
                self.req_id_mappings.get(&req_id).map(
                    |idx| {self.pool_connections.get(idx).map(
                        |pc| {pc.extend_timeout(&req_id, &node_alias);}
                    );}
                );
                None
            }
            Some(NetworkerEvent::CleanTimeout(req_id, node_alias)) => {
                let idx_pc_to_delete = self.req_id_mappings.get(&req_id).and_then(
                    |idx| {
                        let delete = self.pool_connections.get(idx).map(
                            |pc| {
                                pc.clean_timeout(&req_id, node_alias);
                                pc.is_orphaned()
                            }
                        ).unwrap_or(false);

                        if delete {
                            Some(idx)
                        } else {
                            None
                        }
                    }
                );
                if let Some(idx) = idx_pc_to_delete {
                    self.pool_connections.remove(idx);
                }
                None
            }
            Some(NetworkerEvent::Timeout) => {
                let pc_to_delete: Vec<i32> = self.pool_connections.iter()
                    .filter(|(_, v)| v.is_orphaned())
                    .map(|(k, _)| *k)
                    .collect();
                pc_to_delete.iter().for_each(|idx| {
                    self.pool_connections.remove(idx);
                });
                None
            }
            _ => None
        }
    }

    fn get_timeout(&self) -> ((String, String), i64) {
        self.pool_connections.values()
            .map(PoolConnection::get_timeout)
            .min_by(|&(_, val1), &(_, val2)| val1.cmp(&val2))
            .unwrap_or((("".to_string(), "".to_string()), ::std::i64::MAX))
    }

    fn get_poll_items(&self) -> Vec<PollItem> {
        self.pool_connections.iter()
            .flat_map(|(_, pool)| pool.get_poll_items()).collect()
    }
}

pub struct PoolConnection {
    nodes: Vec<RemoteNode>,
    sockets: Vec<Option<ZSocket>>,
    ctx: zmq::Context,
    key_pair: zmq::CurveKeyPair,
    resend: RefCell<HashMap<String, (usize, String)>>,
    timeouts: RefCell<HashMap<(String, String), Tm>>,
    time_created: time::Tm,
}

impl PoolConnection {
    fn new(nodes: Vec<RemoteNode>) -> Self {
        let mut sockets: Vec<Option<ZSocket>> = Vec::new();
        // FIXME restore for _ in 0..nodes.len() { sockets.push(None); }
        // FIXME should be lazy:
        let ctx = zmq::Context::new();
        let key_pair = zmq::CurveKeyPair::new().expect("FIXME");
        for node in &nodes {
            sockets.push(Some(node.connect(&ctx, &key_pair).expect("FIXME")))
        }

        PoolConnection {
            nodes,
            sockets,
            ctx,
            key_pair,
            resend: RefCell::new(HashMap::new()),
            time_created: time::now(),
            timeouts: RefCell::new(HashMap::new()),
        }
    }

    fn fetch_events(&self, poll_items: &[zmq::PollItem]) -> Vec<PoolEvent> {
        let mut vec = Vec::new();
        let mut pi_idx = 0;
        let len = self.nodes.len();
        assert_eq!(len, self.sockets.len());
        for i in 0..len {
            if let (&Some(ref s), rn) = (&self.sockets[i], &self.nodes[i]) {
                if poll_items[pi_idx].is_readable() {
                    if let Ok(Ok(str)) = s.recv_string(zmq::DONTWAIT) {
                        vec.push(PoolEvent::NodeReply(
                            str,
                            rn.name.clone(),
                        ))
                    }
                }
                pi_idx += 1;
            }
        }
        vec
    }

    fn get_poll_items(&self) -> Vec<PollItem> {
        self.sockets.iter()
            .flat_map(|zs: &Option<ZSocket>| zs.as_ref().map(|zs| zs.as_poll_item(zmq::POLLIN)))
            .collect()
    }

    fn get_timeout(&self) -> ((String, String), i64) {
        if let Some((&(ref req_id, ref node_alias), timeout)) = self.timeouts.borrow().iter()
            .map(|(key, value)| (key, (Duration::seconds(10) - (time::now() - *value)).num_milliseconds()))
            .min_by(|&(_, ref val1), &(_, ref val2)| val1.cmp(&val2)){
            ((req_id.to_string(), node_alias.to_string()), timeout)
        } else {
            (("".to_string(), "".to_string()), POOL_CON_ACTIVE_TO * 1000)
        }
    }

    fn is_active(&self) -> bool {
        trace!("time worked: {:?}", time::now() - self.time_created);
        time::now() - self.time_created < Duration::seconds(POOL_CON_ACTIVE_TO)
    }

    fn send_request(&self, pe: Option<NetworkerEvent>) {
        match pe {
            Some(NetworkerEvent::SendOneRequest(msg, req_id)) => {
                let socket: &ZSocket = self.sockets[0].as_ref().expect("FIXME");
                trace!("send single {:?}", msg);
                socket.send_str(&msg, zmq::DONTWAIT).expect("FIXME");
                trace!("sent");
                self.timeouts.borrow_mut().insert((req_id.clone(), self.nodes[0].name.clone()), time::now());
                self.resend.borrow_mut().insert(req_id, (0, msg));
            }
            Some(NetworkerEvent::SendAllRequest(msg, req_id)) => {
                self.sockets.iter().enumerate().for_each(|(i, socket)| {
                    socket.as_ref().expect("FIXME").send_str(&msg, zmq::DONTWAIT).expect("FIXME");
                    self.timeouts.borrow_mut().insert((req_id.clone(), self.nodes[i].name.clone()), time::now());
                });
            }
            Some(NetworkerEvent::Resend(req_id)) => {
                if let Some(&mut (ref mut cnt, ref req)) = self.resend.borrow_mut().get_mut(&req_id) {
                    *cnt = *cnt + 1;
                    //TODO: FIXME: We can collect consensus just walking through if we are not collecting node aliases on the upper layer.
                    let socket: &ZSocket = self.sockets[*cnt % self.sockets.len()].as_ref().expect("FIXME");
                    socket.send_str(&req, zmq::DONTWAIT).expect("FIXME");
                    self.timeouts.borrow_mut().insert((req_id.clone(), self.nodes[*cnt].name.clone()), time::now());
                }
            }
            _ => ()
        }
    }

    fn extend_timeout(&self, req_id: &str, node_alias: &str) {
        if let Some(timeout) = self.timeouts.borrow_mut().get_mut(&(req_id.to_string(), node_alias.to_string())) {
            *timeout = time::now();
        }
    }

    fn clean_timeout(&self, req_id: &str, node_alias: Option<String>) {
        match node_alias {
            Some(node_alias) => {
                self.timeouts.borrow_mut().remove(&(req_id.to_string(), node_alias));
            }
            None => {
                let keys_to_remove: Vec<(String, String)> = self.timeouts.borrow().keys()
                    .cloned().filter(|&(ref req_id_timeout, _)| req_id == req_id_timeout).collect();
                keys_to_remove.iter().for_each(|key| {self.timeouts.borrow_mut().remove(key);});
            }
        }
    }

    fn has_active_requests(&self) -> bool {
        !self.timeouts.borrow().is_empty()
    }

    fn is_orphaned(&self) -> bool {
        !self.is_active() && !self.has_active_requests()
    }
}

impl RemoteNode {
    fn connect(&self, ctx: &zmq::Context, key_pair: &zmq::CurveKeyPair) -> Result<ZSocket, PoolError> {
        let s = ctx.socket(zmq::SocketType::DEALER)?;
        s.set_identity(key_pair.public_key.as_bytes())?;
        s.set_curve_secretkey(&key_pair.secret_key)?;
        s.set_curve_publickey(&key_pair.public_key)?;
        s.set_curve_serverkey(
            zmq::z85_encode(self.public_key.as_slice())
                .map_err(|err| { CommonError::InvalidStructure(format!("Can't encode server key as z85: {:?}", err)) })?
                .as_str())?;
        s.set_linger(0)?; //TODO set correct timeout
        s.connect(&self.zaddr)?;
        Ok(s)
    }
}

#[cfg(test)]
pub struct MockNetworker {}

#[cfg(test)]
impl Networker for MockNetworker {
    fn new() -> Self {
        MockNetworker {}
    }

    fn fetch_events(&self, poll_items: &[zmq::PollItem]) -> Vec<PoolEvent> {
        unimplemented!()
    }

    fn process_event(&mut self, pe: Option<NetworkerEvent>) -> Option<RequestEvent> {
        None
    }

    fn get_timeout(&self) -> ((String, String), i64) {
        unimplemented!()
    }

    fn get_poll_items(&self) -> Vec<PollItem> {
        unimplemented!()
    }
}

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

    #[test]
    pub fn networker_new_works() {
        ZMQNetworker::new();
    }

    #[test]
    pub fn networker_process_event_works() {
        let mut networker = ZMQNetworker::new();
        networker.process_event(None);
    }
}<commit_msg>Lazy open sockets for PoolConnection<commit_after>extern crate zmq;
extern crate time;

use self::zmq::PollItem;
use self::zmq::Socket as ZSocket;

use errors::common::CommonError;
use errors::pool::PoolError;
use services::pool::events::*;
use services::pool::types::*;
use std::collections::HashMap;
use std::cell::RefCell;
use super::time::Duration;
use utils::sequence::SequenceUtils;
use time::Tm;

pub trait Networker {
    fn new() -> Self;
    fn fetch_events(&self, poll_items: &[PollItem]) -> Vec<PoolEvent>;
    fn process_event(&mut self, pe: Option<NetworkerEvent>) -> Option<RequestEvent>;
    fn get_timeout(&self) -> ((String, String), i64);
    fn get_poll_items(&self) -> Vec<PollItem>;
}

pub struct ZMQNetworker {
    req_id_mappings: HashMap<String, i32>,
    pool_connections: HashMap<i32, PoolConnection>,
    nodes: Vec<RemoteNode>,
}

const POOL_CON_ACTIVE_TO: i64 = 5;

impl Networker for ZMQNetworker {
    fn new() -> Self {
        ZMQNetworker {
            req_id_mappings: HashMap::new(),
            pool_connections: HashMap::new(),
            nodes: Vec::new(),
        }
    }

    fn fetch_events(&self, poll_items: &[PollItem]) -> Vec<PoolEvent> {
        let mut cnt = 0;
        self.pool_connections.iter().map(|(_, pc)| {
            let ocnt = cnt;
            cnt = cnt + pc.sockets.iter().filter(|s| s.is_some()).count();
            pc.fetch_events(&poll_items[ocnt..cnt])
        }).flat_map(|v| v.into_iter()).collect()
    }

    fn process_event(&mut self, pe: Option<NetworkerEvent>) -> Option<RequestEvent> {
        match pe.clone() {
            Some(NetworkerEvent::SendAllRequest(_, req_id)) | Some(NetworkerEvent::SendOneRequest(_, req_id)) | Some(NetworkerEvent::Resend(req_id)) => {
                trace!("current mappings: {:?}", self.req_id_mappings);
                let num = self.req_id_mappings.get(&req_id).map(|i| i.clone()).or_else(|| {
                    self.req_id_mappings.values()
                        .fold(HashMap::new(), |mut acc, pc_id| {
                            *acc.entry(pc_id).or_insert(0) += 1;
                            acc
                        }).iter()
                        .filter(|&(pc_id, cnt)|
                            cnt < &5 && self.pool_connections.get(pc_id).map(|pc| pc.is_active()).unwrap_or(false))
                        .last().map(|(pc_id, _)| **pc_id)
                });
                match num {
                    Some(idx) => {
                        trace!("send request in existing conn");
                        self.pool_connections.get_mut(&idx).map(|pc| {
                            pc.send_request(pe);
                        });
                        self.req_id_mappings.insert(req_id.clone(), idx);
                    }
                    None => {
                        trace!("send request in new conn");
                        let pc_id = SequenceUtils::get_next_id();
                        let mut pc = PoolConnection::new(self.nodes.clone());
                        pc.send_request(pe);
                        self.pool_connections.insert(pc_id, pc);
                        self.req_id_mappings.insert(req_id.clone(), pc_id);
                    }
                }
                None
            }
            Some(NetworkerEvent::NodesStateUpdated(nodes)) => {
                self.nodes = nodes;
                None
            }
            Some(NetworkerEvent::ExtendTimeout(req_id, node_alias)) => {
                self.req_id_mappings.get(&req_id).map(
                    |idx| {self.pool_connections.get(idx).map(
                        |pc| {pc.extend_timeout(&req_id, &node_alias);}
                    );}
                );
                None
            }
            Some(NetworkerEvent::CleanTimeout(req_id, node_alias)) => {
                let idx_pc_to_delete = self.req_id_mappings.get(&req_id).and_then(
                    |idx| {
                        let delete = self.pool_connections.get(idx).map(
                            |pc| {
                                pc.clean_timeout(&req_id, node_alias);
                                pc.is_orphaned()
                            }
                        ).unwrap_or(false);

                        if delete {
                            Some(idx)
                        } else {
                            None
                        }
                    }
                );
                if let Some(idx) = idx_pc_to_delete {
                    self.pool_connections.remove(idx);
                }
                None
            }
            Some(NetworkerEvent::Timeout) => {
                let pc_to_delete: Vec<i32> = self.pool_connections.iter()
                    .filter(|(_, v)| v.is_orphaned())
                    .map(|(k, _)| *k)
                    .collect();
                pc_to_delete.iter().for_each(|idx| {
                    self.pool_connections.remove(idx);
                });
                None
            }
            _ => None
        }
    }

    fn get_timeout(&self) -> ((String, String), i64) {
        self.pool_connections.values()
            .map(PoolConnection::get_timeout)
            .min_by(|&(_, val1), &(_, val2)| val1.cmp(&val2))
            .unwrap_or((("".to_string(), "".to_string()), ::std::i64::MAX))
    }

    fn get_poll_items(&self) -> Vec<PollItem> {
        self.pool_connections.iter()
            .flat_map(|(_, pool)| pool.get_poll_items()).collect()
    }
}

pub struct PoolConnection {
    nodes: Vec<RemoteNode>,
    sockets: Vec<Option<ZSocket>>,
    ctx: zmq::Context,
    key_pair: zmq::CurveKeyPair,
    resend: RefCell<HashMap<String, (usize, String)>>,
    timeouts: RefCell<HashMap<(String, String), Tm>>,
    time_created: time::Tm,
}

impl PoolConnection {
    fn new(nodes: Vec<RemoteNode>) -> Self {
        //TODO shuffle nodes
        let mut sockets: Vec<Option<ZSocket>> = Vec::new();
        for _ in 0..nodes.len() { sockets.push(None); }

        PoolConnection {
            nodes,
            sockets,
            ctx: zmq::Context::new(),
            key_pair: zmq::CurveKeyPair::new().expect("FIXME"),
            resend: RefCell::new(HashMap::new()),
            time_created: time::now(),
            timeouts: RefCell::new(HashMap::new()),
        }
    }

    fn fetch_events(&self, poll_items: &[zmq::PollItem]) -> Vec<PoolEvent> {
        let mut vec = Vec::new();
        let mut pi_idx = 0;
        let len = self.nodes.len();
        assert_eq!(len, self.sockets.len());
        for i in 0..len {
            if let (&Some(ref s), rn) = (&self.sockets[i], &self.nodes[i]) {
                if poll_items[pi_idx].is_readable() {
                    if let Ok(Ok(str)) = s.recv_string(zmq::DONTWAIT) {
                        vec.push(PoolEvent::NodeReply(
                            str,
                            rn.name.clone(),
                        ))
                    }
                }
                pi_idx += 1;
            }
        }
        vec
    }

    fn get_poll_items(&self) -> Vec<PollItem> {
        self.sockets.iter()
            .flat_map(|zs: &Option<ZSocket>| zs.as_ref().map(|zs| zs.as_poll_item(zmq::POLLIN)))
            .collect()
    }

    fn get_timeout(&self) -> ((String, String), i64) {
        if let Some((&(ref req_id, ref node_alias), timeout)) = self.timeouts.borrow().iter()
            .map(|(key, value)| (key, (Duration::seconds(10) - (time::now() - *value)).num_milliseconds()))
            .min_by(|&(_, ref val1), &(_, ref val2)| val1.cmp(&val2)){
            ((req_id.to_string(), node_alias.to_string()), timeout)
        } else {
            (("".to_string(), "".to_string()), POOL_CON_ACTIVE_TO * 1000)
        }
    }

    fn is_active(&self) -> bool {
        trace!("time worked: {:?}", time::now() - self.time_created);
        time::now() - self.time_created < Duration::seconds(POOL_CON_ACTIVE_TO)
    }

    fn send_request(&mut self, pe: Option<NetworkerEvent>) {
        trace!("send_request >> pe: {:?}", pe);
        match pe {
            Some(NetworkerEvent::SendOneRequest(msg, req_id)) => {
                self._send_msg_to_one_node(0, req_id.clone(), msg.clone());
                self.resend.borrow_mut().insert(req_id, (0, msg));
            }
            Some(NetworkerEvent::SendAllRequest(msg, req_id)) => {
                (0..self.nodes.len()).for_each(|idx|
                    self._send_msg_to_one_node(idx, req_id.clone(), msg.clone()))
            }
            Some(NetworkerEvent::Resend(req_id)) => {
                let resend = if let Some(&mut (ref mut cnt, ref req)) = self.resend.borrow_mut().get_mut(&req_id) {
                    *cnt = *cnt + 1;
                    //TODO: FIXME: We can collect consensus just walking through if we are not collecting node aliases on the upper layer.
                    Some((*cnt % self.nodes.len(), req.clone()))
                } else {
                    None
                };
                if let Some((idx, req)) = resend {
                    self._send_msg_to_one_node(idx, req_id, req);
                }
            }
            _ => ()
        }
        trace!("send_request <<");
    }

    fn extend_timeout(&self, req_id: &str, node_alias: &str) {
        if let Some(timeout) = self.timeouts.borrow_mut().get_mut(&(req_id.to_string(), node_alias.to_string())) {
            *timeout = time::now();
        }
    }

    fn clean_timeout(&self, req_id: &str, node_alias: Option<String>) {
        match node_alias {
            Some(node_alias) => {
                self.timeouts.borrow_mut().remove(&(req_id.to_string(), node_alias));
            }
            None => {
                let keys_to_remove: Vec<(String, String)> = self.timeouts.borrow().keys()
                    .cloned().filter(|&(ref req_id_timeout, _)| req_id == req_id_timeout).collect();
                keys_to_remove.iter().for_each(|key| {self.timeouts.borrow_mut().remove(key);});
            }
        }
    }

    fn has_active_requests(&self) -> bool {
        !self.timeouts.borrow().is_empty()
    }

    fn is_orphaned(&self) -> bool {
        !self.is_active() && !self.has_active_requests()
    }

    fn _send_msg_to_one_node(&mut self, idx: usize, req_id: String, req: String) {
        trace!("_send_msg_to_one_node >> idx {}, req_id {}, req {}", idx, req_id, req);
        {
            let s = self._get_socket(idx);
            s.send_str(&req, zmq::DONTWAIT).expect("FIXME");
        }
        self.timeouts.borrow_mut().insert((req_id, self.nodes[idx].name.clone()), time::now());
        trace!("_send_msg_to_one_node <<");
    }

    fn _get_socket(&mut self, idx: usize) -> &ZSocket {
        if self.sockets[idx].is_none() {
            debug!("_get_socket: open new socket for node {}", idx);
            let s: ZSocket = self.nodes[idx].connect(&self.ctx, &self.key_pair).expect("FIXME");
            self.sockets[idx] = Some(s)
        }
        self.sockets[idx].as_ref().unwrap()
    }
}

impl RemoteNode {
    fn connect(&self, ctx: &zmq::Context, key_pair: &zmq::CurveKeyPair) -> Result<ZSocket, PoolError> {
        let s = ctx.socket(zmq::SocketType::DEALER)?;
        s.set_identity(key_pair.public_key.as_bytes())?;
        s.set_curve_secretkey(&key_pair.secret_key)?;
        s.set_curve_publickey(&key_pair.public_key)?;
        s.set_curve_serverkey(
            zmq::z85_encode(self.public_key.as_slice())
                .map_err(|err| { CommonError::InvalidStructure(format!("Can't encode server key as z85: {:?}", err)) })?
                .as_str())?;
        s.set_linger(0)?; //TODO set correct timeout
        s.connect(&self.zaddr)?;
        Ok(s)
    }
}

#[cfg(test)]
pub struct MockNetworker {}

#[cfg(test)]
impl Networker for MockNetworker {
    fn new() -> Self {
        MockNetworker {}
    }

    fn fetch_events(&self, poll_items: &[zmq::PollItem]) -> Vec<PoolEvent> {
        unimplemented!()
    }

    fn process_event(&mut self, pe: Option<NetworkerEvent>) -> Option<RequestEvent> {
        None
    }

    fn get_timeout(&self) -> ((String, String), i64) {
        unimplemented!()
    }

    fn get_poll_items(&self) -> Vec<PollItem> {
        unimplemented!()
    }
}

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

    #[test]
    pub fn networker_new_works() {
        ZMQNetworker::new();
    }

    #[test]
    pub fn networker_process_event_works() {
        let mut networker = ZMQNetworker::new();
        networker.process_event(None);
    }
}<|endoftext|>


<commit_before>extern crate heck;
extern crate structopt;
#[macro_use]
extern crate structopt_derive;
extern crate syn;

mod c;
mod csharp;
mod emscripten;
mod java;
mod jni_cpp;
mod kotlin;
mod node;
mod python;
mod ruby;
mod swift;
mod typescript;
mod wasm;

use std::fs::{create_dir_all, remove_dir_all, File};
use std::io::{BufWriter, Read, Result};
use std::path::PathBuf;
use std::rc::Rc;
use structopt::StructOpt;
use syn::{parse_file, FnArg, Item, ItemFn, Lit, Meta, Pat, ReturnType, Type as SynType, Visibility};

#[derive(StructOpt)]
#[structopt(about = "Generates bindings for livesplit-core")]
pub struct Opt {
    #[structopt(long = "ruby-lib-path", help = "The path of the library for the Ruby bindings",
                default_value = "../liblivesplit_core.so")]
    ruby_lib_path: String,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TypeKind {
    Value,
    Ref,
    RefMut,
}

#[derive(Debug)]
pub struct Type {
    kind: TypeKind,
    is_custom: bool,
    is_nullable: bool,
    name: String,
}

#[derive(Debug)]
pub struct Function {
    name: String,
    class: String,
    method: String,
    inputs: Vec<(String, Type)>,
    output: Type,
    comments: Vec<String>,
    class_comments: Rc<Vec<String>>,
}

impl Function {
    fn is_static(&self) -> bool {
        if let Some(&(ref name, _)) = self.inputs.get(0) {
            name != "this"
        } else {
            true
        }
    }

    fn has_return_type(&self) -> bool {
        self.output.name != "()"
    }
}

#[derive(Debug, Default)]
pub struct Class {
    comments: Rc<Vec<String>>,
    static_fns: Vec<Function>,
    shared_fns: Vec<Function>,
    mut_fns: Vec<Function>,
    own_fns: Vec<Function>,
}

fn get_type(ty: &SynType) -> Type {
    match *ty {
        SynType::Ptr(ref ptr) => {
            let mut ty = get_type(&ptr.elem);
            ty.kind = if ptr.mutability.is_some() {
                TypeKind::RefMut
            } else {
                TypeKind::Ref
            };
            ty
        }
        SynType::Path(ref path) => {
            if let Some(segment) = path.path.segments.iter().last() {
                let mut name = segment.ident.to_string();
                let is_nullable = if name.starts_with("Nullable") {
                    name = name["Nullable".len()..].to_string();
                    true
                } else {
                    false
                };

                if name.starts_with("Owned") {
                    name = name["Owned".len()..].to_string();
                }
                if name == "TimingMethod" {
                    name = String::from("u8");
                } else if name == "TimerPhase" {
                    name = String::from("u8");
                }
                let is_custom = match &name as &str {
                    "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "()" | "bool"
                    | "c_char" | "usize" | "isize" | "f32" | "f64" | "Json" => false,
                    _ => true,
                };
                Type {
                    kind: TypeKind::Value,
                    is_custom,
                    is_nullable,
                    name,
                }
            } else {
                panic!("Weird path")
            }
        }
        _ => panic!("Weird type"),
    }
}

fn main() {
    let opt = Opt::from_args();

    let mut contents = String::new();
    File::open("../src/lib.rs")
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();

    let file = parse_file(&contents).unwrap();

    let mut functions = Vec::new();

    for item in &file.items {
        if let &Item::Mod(ref module) = item {
            contents.clear();
            File::open(format!("../src/{}.rs", module.ident))
                .unwrap()
                .read_to_string(&mut contents)
                .unwrap();
            let file = parse_file(&contents).unwrap();

            let class_comments = Rc::new(
                file.attrs
                    .iter()
                    .filter_map(|a| match a.interpret_meta() {
                        Some(Meta::NameValue(v)) => Some(v),
                        _ => None,
                    })
                    .filter(|m| m.ident.as_ref() == "doc")
                    .filter_map(|m| match m.lit {
                        Lit::Str(s) => Some(s.value()[3..].trim().to_string()),
                        _ => None,
                    })
                    .collect::<Vec<_>>(),
            );

            for item in &file.items {
                if let &Item::Fn(ItemFn {
                    vis: Visibility::Public(_),
                    ref abi,
                    ref attrs,
                    ref decl,
                    ref ident,
                    ..
                }) = item
                {
                    if abi.as_ref()
                        .and_then(|a| a.name.as_ref())
                        .map_or(false, |n| n.value() == "C")
                        && attrs
                            .iter()
                            .filter_map(|a| a.interpret_meta())
                            .filter_map(|m| match m {
                                Meta::Word(w) => Some(w),
                                _ => None,
                            })
                            .any(|w| w.as_ref() == "no_mangle")
                    {
                        let comments = attrs
                            .iter()
                            .filter_map(|a| match a.interpret_meta() {
                                Some(Meta::NameValue(v)) => Some(v),
                                _ => None,
                            })
                            .filter(|m| m.ident.as_ref() == "doc")
                            .filter_map(|m| match m.lit {
                                Lit::Str(s) => Some(s.value()[3..].trim().to_string()),
                                _ => None,
                            })
                            .collect();

                        let output = if let ReturnType::Type(_, ref ty) = decl.output {
                            get_type(ty)
                        } else {
                            Type {
                                kind: TypeKind::Value,
                                is_custom: false,
                                is_nullable: false,
                                name: String::from("()"),
                            }
                        };

                        let inputs = decl.inputs
                            .iter()
                            .map(|i| {
                                let c = match i {
                                    &FnArg::Captured(ref c) => c,
                                    _ => panic!("Found a weird fn argument"),
                                };
                                let name = if let Pat::Ident(ref ident) = c.pat {
                                    ident.ident.to_string()
                                } else {
                                    String::from("parameter")
                                };
                                (name, get_type(&c.ty))
                            })
                            .collect();

                        let name = ident.to_string();
                        let class;
                        let method;
                        {
                            let mut splits = name.splitn(2, '_');
                            class = splits.next().unwrap().to_string();
                            method = splits.next().unwrap().to_string();
                        }

                        functions.push(Function {
                            name,
                            class,
                            method,
                            output,
                            inputs,
                            comments,
                            class_comments: class_comments.clone(),
                        });
                    }
                }
            }
        }
    }

    write_files(&fns_to_classes(functions), &opt).unwrap();
}

use std::collections::BTreeMap;

fn fns_to_classes(functions: Vec<Function>) -> BTreeMap<String, Class> {
    let mut classes = BTreeMap::new();

    for function in functions {
        let class = classes
            .entry(function.class.clone())
            .or_insert_with(Class::default);

        class.comments = function.class_comments.clone();

        let kind = if let Some(&(ref name, ref ty)) = function.inputs.get(0) {
            if name == "this" {
                Some(ty.kind)
            } else {
                None
            }
        } else {
            None
        };
        match kind {
            Some(TypeKind::Value) => class.own_fns.push(function),
            Some(TypeKind::Ref) => class.shared_fns.push(function),
            Some(TypeKind::RefMut) => class.mut_fns.push(function),
            None => class.static_fns.push(function),
        }
    }

    classes
}

fn write_files(classes: &BTreeMap<String, Class>, opt: &Opt) -> Result<()> {
    let mut path = PathBuf::from("..");
    path.push("bindings");

    remove_dir_all(&path).ok();
    create_dir_all(&path)?;

    path.push("emscripten");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        emscripten::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        emscripten::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("node");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        node::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        node::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("wasm");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        wasm::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        wasm::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("LiveSplitCore.cs");
    csharp::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("java");
    create_dir_all(&path)?;
    java::write(&path, classes)?;
    path.pop();

    path.push("kotlin");
    create_dir_all(&path)?;
    kotlin::write(&path, classes)?;
    path.pop();

    path.push("LiveSplitCore.rb");
    ruby::write(BufWriter::new(File::create(&path)?), classes, opt)?;
    path.pop();

    path.push("livesplit_core.h");
    c::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("livesplit_core.py");
    python::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("swift");
    create_dir_all(&path)?;
    swift::write(&path, classes)?;
    path.pop();

    Ok(())
}
<commit_msg>Update the Binding Generator<commit_after>extern crate heck;
extern crate structopt;
#[macro_use]
extern crate structopt_derive;
extern crate syn;

mod c;
mod csharp;
mod emscripten;
mod java;
mod jni_cpp;
mod kotlin;
mod node;
mod python;
mod ruby;
mod swift;
mod typescript;
mod wasm;

use std::fs::{create_dir_all, remove_dir_all, File};
use std::io::{BufWriter, Read, Result};
use std::path::PathBuf;
use std::rc::Rc;
use structopt::StructOpt;
use syn::{parse_file, FnArg, Item, ItemFn, Lit, Meta, Pat, ReturnType, Type as SynType, Visibility};

#[derive(StructOpt)]
#[structopt(about = "Generates bindings for livesplit-core")]
pub struct Opt {
    #[structopt(long = "ruby-lib-path", help = "The path of the library for the Ruby bindings",
                default_value = "../liblivesplit_core.so")]
    ruby_lib_path: String,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TypeKind {
    Value,
    Ref,
    RefMut,
}

#[derive(Debug)]
pub struct Type {
    kind: TypeKind,
    is_custom: bool,
    is_nullable: bool,
    name: String,
}

#[derive(Debug)]
pub struct Function {
    name: String,
    class: String,
    method: String,
    inputs: Vec<(String, Type)>,
    output: Type,
    comments: Vec<String>,
    class_comments: Rc<Vec<String>>,
}

impl Function {
    fn is_static(&self) -> bool {
        if let Some(&(ref name, _)) = self.inputs.get(0) {
            name != "this"
        } else {
            true
        }
    }

    fn has_return_type(&self) -> bool {
        self.output.name != "()"
    }
}

#[derive(Debug, Default)]
pub struct Class {
    comments: Rc<Vec<String>>,
    static_fns: Vec<Function>,
    shared_fns: Vec<Function>,
    mut_fns: Vec<Function>,
    own_fns: Vec<Function>,
}

fn get_type(ty: &SynType) -> Type {
    match *ty {
        SynType::Reference(ref reference) => {
            let mut ty = get_type(&reference.elem);
            ty.kind = if reference.mutability.is_some() {
                TypeKind::RefMut
            } else {
                TypeKind::Ref
            };
            ty
        }
        SynType::Ptr(ref ptr) => {
            let mut ty = get_type(&ptr.elem);
            ty.kind = if ptr.mutability.is_some() {
                TypeKind::RefMut
            } else {
                TypeKind::Ref
            };
            ty
        }
        SynType::Path(ref path) => {
            if let Some(segment) = path.path.segments.iter().last() {
                let mut name = segment.ident.to_string();
                let is_nullable = if name.starts_with("Nullable") {
                    name = name["Nullable".len()..].to_string();
                    true
                } else {
                    false
                };

                if name.starts_with("Owned") {
                    name = name["Owned".len()..].to_string();
                }
                if name == "TimingMethod" {
                    name = String::from("u8");
                } else if name == "TimerPhase" {
                    name = String::from("u8");
                }
                let is_custom = match &name as &str {
                    "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "()" | "bool"
                    | "c_char" | "usize" | "isize" | "f32" | "f64" | "Json" => false,
                    _ => true,
                };
                Type {
                    kind: TypeKind::Value,
                    is_custom,
                    is_nullable,
                    name,
                }
            } else {
                panic!("Weird path")
            }
        }
        _ => panic!("Weird type"),
    }
}

fn main() {
    let opt = Opt::from_args();

    let mut contents = String::new();
    File::open("../src/lib.rs")
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();

    let file = parse_file(&contents).unwrap();

    let mut functions = Vec::new();

    for item in &file.items {
        if let &Item::Mod(ref module) = item {
            contents.clear();
            File::open(format!("../src/{}.rs", module.ident))
                .unwrap()
                .read_to_string(&mut contents)
                .unwrap();
            let file = parse_file(&contents).unwrap();

            let class_comments = Rc::new(
                file.attrs
                    .iter()
                    .filter_map(|a| match a.interpret_meta() {
                        Some(Meta::NameValue(v)) => Some(v),
                        _ => None,
                    })
                    .filter(|m| m.ident.as_ref() == "doc")
                    .filter_map(|m| match m.lit {
                        Lit::Str(s) => Some(s.value()[3..].trim().to_string()),
                        _ => None,
                    })
                    .collect::<Vec<_>>(),
            );

            for item in &file.items {
                if let &Item::Fn(ItemFn {
                    vis: Visibility::Public(_),
                    ref abi,
                    ref attrs,
                    ref decl,
                    ref ident,
                    ..
                }) = item
                {
                    if abi.as_ref()
                        .and_then(|a| a.name.as_ref())
                        .map_or(false, |n| n.value() == "C")
                        && attrs
                            .iter()
                            .filter_map(|a| a.interpret_meta())
                            .filter_map(|m| match m {
                                Meta::Word(w) => Some(w),
                                _ => None,
                            })
                            .any(|w| w.as_ref() == "no_mangle")
                    {
                        let comments = attrs
                            .iter()
                            .filter_map(|a| match a.interpret_meta() {
                                Some(Meta::NameValue(v)) => Some(v),
                                _ => None,
                            })
                            .filter(|m| m.ident.as_ref() == "doc")
                            .filter_map(|m| match m.lit {
                                Lit::Str(s) => Some(s.value()[3..].trim().to_string()),
                                _ => None,
                            })
                            .collect();

                        let output = if let ReturnType::Type(_, ref ty) = decl.output {
                            get_type(ty)
                        } else {
                            Type {
                                kind: TypeKind::Value,
                                is_custom: false,
                                is_nullable: false,
                                name: String::from("()"),
                            }
                        };

                        let inputs = decl.inputs
                            .iter()
                            .map(|i| {
                                let c = match i {
                                    &FnArg::Captured(ref c) => c,
                                    _ => panic!("Found a weird fn argument"),
                                };
                                let name = if let Pat::Ident(ref ident) = c.pat {
                                    ident.ident.to_string()
                                } else {
                                    String::from("parameter")
                                };
                                (name, get_type(&c.ty))
                            })
                            .collect();

                        let name = ident.to_string();
                        let class;
                        let method;
                        {
                            let mut splits = name.splitn(2, '_');
                            class = splits.next().unwrap().to_string();
                            method = splits.next().unwrap().to_string();
                        }

                        functions.push(Function {
                            name,
                            class,
                            method,
                            output,
                            inputs,
                            comments,
                            class_comments: class_comments.clone(),
                        });
                    }
                }
            }
        }
    }

    write_files(&fns_to_classes(functions), &opt).unwrap();
}

use std::collections::BTreeMap;

fn fns_to_classes(functions: Vec<Function>) -> BTreeMap<String, Class> {
    let mut classes = BTreeMap::new();

    for function in functions {
        let class = classes
            .entry(function.class.clone())
            .or_insert_with(Class::default);

        class.comments = function.class_comments.clone();

        let kind = if let Some(&(ref name, ref ty)) = function.inputs.get(0) {
            if name == "this" {
                Some(ty.kind)
            } else {
                None
            }
        } else {
            None
        };
        match kind {
            Some(TypeKind::Value) => class.own_fns.push(function),
            Some(TypeKind::Ref) => class.shared_fns.push(function),
            Some(TypeKind::RefMut) => class.mut_fns.push(function),
            None => class.static_fns.push(function),
        }
    }

    classes
}

fn write_files(classes: &BTreeMap<String, Class>, opt: &Opt) -> Result<()> {
    let mut path = PathBuf::from("..");
    path.push("bindings");

    remove_dir_all(&path).ok();
    create_dir_all(&path)?;

    path.push("emscripten");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        emscripten::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        emscripten::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("node");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        node::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        node::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("wasm");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        wasm::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        wasm::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("LiveSplitCore.cs");
    csharp::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("java");
    create_dir_all(&path)?;
    java::write(&path, classes)?;
    path.pop();

    path.push("kotlin");
    create_dir_all(&path)?;
    kotlin::write(&path, classes)?;
    path.pop();

    path.push("LiveSplitCore.rb");
    ruby::write(BufWriter::new(File::create(&path)?), classes, opt)?;
    path.pop();

    path.push("livesplit_core.h");
    c::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("livesplit_core.py");
    python::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("swift");
    create_dir_all(&path)?;
    swift::write(&path, classes)?;
    path.pop();

    Ok(())
}
<|endoftext|>


<commit_before>//! Syntax Tree library used throughout the rust analyzer.
//!
//! Properties:
//!   - easy and fast incremental re-parsing
//!   - graceful handling of errors
//!   - full-fidelity representation (*any* text can be precisely represented as
//!     a syntax tree)
//!
//! For more information, see the [RFC]. Current implementation is inspired by
//! the [Swift] one.
//!
//! The most interesting modules here are `syntax_node` (which defines concrete
//! syntax tree) and `ast` (which defines abstract syntax tree on top of the
//! CST). The actual parser live in a separate `ra_parser` crate, though the
//! lexer lives in this crate.
//!
//! See `api_walkthrough` test in this file for a quick API tour!
//!
//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>

#[allow(unused)]
macro_rules! eprintln {
    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}

mod syntax_node;
mod syntax_error;
mod parsing;
mod validation;
mod ptr;
#[cfg(test)]
mod tests;

pub mod algo;
pub mod ast;
#[doc(hidden)]
pub mod fuzz;

use std::{marker::PhantomData, sync::Arc};

use ra_text_edit::Indel;
use stdx::format_to;

use crate::syntax_node::GreenNode;

pub use crate::{
    algo::InsertPosition,
    ast::{AstNode, AstToken},
    parsing::{lex_single_syntax_kind, lex_single_valid_syntax_kind, tokenize, Token},
    ptr::{AstPtr, SyntaxNodePtr},
    syntax_error::SyntaxError,
    syntax_node::{
        Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder,
    },
};
pub use ra_parser::{SyntaxKind, T};
pub use rowan::{SmolStr, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent};

/// `Parse` is the result of the parsing: a syntax tree and a collection of
/// errors.
///
/// Note that we always produce a syntax tree, even for completely invalid
/// files.
#[derive(Debug, PartialEq, Eq)]
pub struct Parse<T> {
    green: GreenNode,
    errors: Arc<Vec<SyntaxError>>,
    _ty: PhantomData<fn() -> T>,
}

impl<T> Clone for Parse<T> {
    fn clone(&self) -> Parse<T> {
        Parse { green: self.green.clone(), errors: self.errors.clone(), _ty: PhantomData }
    }
}

impl<T> Parse<T> {
    fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
        Parse { green, errors: Arc::new(errors), _ty: PhantomData }
    }

    pub fn syntax_node(&self) -> SyntaxNode {
        SyntaxNode::new_root(self.green.clone())
    }
}

impl<T: AstNode> Parse<T> {
    pub fn to_syntax(self) -> Parse<SyntaxNode> {
        Parse { green: self.green, errors: self.errors, _ty: PhantomData }
    }

    pub fn tree(&self) -> T {
        T::cast(self.syntax_node()).unwrap()
    }

    pub fn errors(&self) -> &[SyntaxError] {
        &*self.errors
    }

    pub fn ok(self) -> Result<T, Arc<Vec<SyntaxError>>> {
        if self.errors.is_empty() {
            Ok(self.tree())
        } else {
            Err(self.errors)
        }
    }
}

impl Parse<SyntaxNode> {
    pub fn cast<N: AstNode>(self) -> Option<Parse<N>> {
        if N::cast(self.syntax_node()).is_some() {
            Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData })
        } else {
            None
        }
    }
}

impl Parse<SourceFile> {
    pub fn debug_dump(&self) -> String {
        let mut buf = format!("{:#?}", self.tree().syntax());
        for err in self.errors.iter() {
            format_to!(buf, "error {:?}: {}\n", err.range(), err);
        }
        buf
    }

    pub fn reparse(&self, indel: &Indel) -> Parse<SourceFile> {
        self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel))
    }

    fn incremental_reparse(&self, indel: &Indel) -> Option<Parse<SourceFile>> {
        // FIXME: validation errors are not handled here
        parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map(
            |(green_node, errors, _reparsed_range)| Parse {
                green: green_node,
                errors: Arc::new(errors),
                _ty: PhantomData,
            },
        )
    }

    fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> {
        let mut text = self.tree().syntax().text().to_string();
        indel.apply(&mut text);
        SourceFile::parse(&text)
    }
}

/// `SourceFile` represents a parse tree for a single Rust file.
pub use crate::ast::SourceFile;

impl SourceFile {
    pub fn parse(text: &str) -> Parse<SourceFile> {
        let (green, mut errors) = parsing::parse_text(text);
        let root = SyntaxNode::new_root(green.clone());

        if cfg!(debug_assertions) {
            validation::validate_block_structure(&root);
        }

        errors.extend(validation::validate(&root));

        assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
        Parse { green, errors: Arc::new(errors), _ty: PhantomData }
    }
}

/// Matches a `SyntaxNode` against an `ast` type.
///
/// # Example:
///
/// ```ignore
/// match_ast! {
///     match node {
///         ast::CallExpr(it) => { ... },
///         ast::MethodCallExpr(it) => { ... },
///         ast::MacroCall(it) => { ... },
///         _ => None,
///     }
/// }
/// ```
#[macro_export]
macro_rules! match_ast {
    (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };

    (match ($node:expr) {
        $( ast::$ast:ident($it:ident) => $res:expr, )*
        _ => $catch_all:expr $(,)?
    }) => {{
        $( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
        { $catch_all }
    }};
}

/// This test does not assert anything and instead just shows off the crate's
/// API.
#[test]
fn api_walkthrough() {
    use ast::{ModuleItemOwner, NameOwner};

    let source_code = "
        fn foo() {
            1 + 1
        }
    ";
    // `SourceFile` is the main entry point.
    //
    // The `parse` method returns a `Parse` -- a pair of syntax tree and a list
    // of errors. That is, syntax tree is constructed even in presence of errors.
    let parse = SourceFile::parse(source_code);
    assert!(parse.errors().is_empty());

    // The `tree` method returns an owned syntax node of type `SourceFile`.
    // Owned nodes are cheap: inside, they are `Rc` handles to the underling data.
    let file: SourceFile = parse.tree();

    // `SourceFile` is the root of the syntax tree. We can iterate file's items.
    // Let's fetch the `foo` function.
    let mut func = None;
    for item in file.items() {
        match item {
            ast::ModuleItem::FnDef(f) => func = Some(f),
            _ => unreachable!(),
        }
    }
    let func: ast::FnDef = func.unwrap();

    // Each AST node has a bunch of getters for children. All getters return
    // `Option`s though, to account for incomplete code. Some getters are common
    // for several kinds of node. In this case, a trait like `ast::NameOwner`
    // usually exists. By convention, all ast types should be used with `ast::`
    // qualifier.
    let name: Option<ast::Name> = func.name();
    let name = name.unwrap();
    assert_eq!(name.text(), "foo");

    // Let's get the `1 + 1` expression!
    let body: ast::BlockExpr = func.body().unwrap();
    let expr: ast::Expr = body.expr().unwrap();

    // Enums are used to group related ast nodes together, and can be used for
    // matching. However, because there are no public fields, it's possible to
    // match only the top level enum: that is the price we pay for increased API
    // flexibility
    let bin_expr: &ast::BinExpr = match &expr {
        ast::Expr::BinExpr(e) => e,
        _ => unreachable!(),
    };

    // Besides the "typed" AST API, there's an untyped CST one as well.
    // To switch from AST to CST, call `.syntax()` method:
    let expr_syntax: &SyntaxNode = expr.syntax();

    // Note how `expr` and `bin_expr` are in fact the same node underneath:
    assert!(expr_syntax == bin_expr.syntax());

    // To go from CST to AST, `AstNode::cast` function is used:
    let _expr: ast::Expr = match ast::Expr::cast(expr_syntax.clone()) {
        Some(e) => e,
        None => unreachable!(),
    };

    // The two properties each syntax node has is a `SyntaxKind`:
    assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);

    // And text range:
    assert_eq!(expr_syntax.text_range(), TextRange::new(32.into(), 37.into()));

    // You can get node's text as a `SyntaxText` object, which will traverse the
    // tree collecting token's text:
    let text: SyntaxText = expr_syntax.text();
    assert_eq!(text.to_string(), "1 + 1");

    // There's a bunch of traversal methods on `SyntaxNode`:
    assert_eq!(expr_syntax.parent().as_ref(), Some(body.syntax()));
    assert_eq!(body.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
    assert_eq!(
        expr_syntax.next_sibling_or_token().map(|it| it.kind()),
        Some(SyntaxKind::WHITESPACE)
    );

    // As well as some iterator helpers:
    let f = expr_syntax.ancestors().find_map(ast::FnDef::cast);
    assert_eq!(f, Some(func));
    assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
    assert_eq!(
        expr_syntax.descendants_with_tokens().count(),
        8, // 5 tokens `1`, ` `, `+`, ` `, `!`
           // 2 child literal expressions: `1`, `1`
           // 1 the node itself: `1 + 1`
    );

    // There's also a `preorder` method with a more fine-grained iteration control:
    let mut buf = String::new();
    let mut indent = 0;
    for event in expr_syntax.preorder_with_tokens() {
        match event {
            WalkEvent::Enter(node) => {
                let text = match &node {
                    NodeOrToken::Node(it) => it.text().to_string(),
                    NodeOrToken::Token(it) => it.text().to_string(),
                };
                format_to!(buf, "{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
                indent += 2;
            }
            WalkEvent::Leave(_) => indent -= 2,
        }
    }
    assert_eq!(indent, 0);
    assert_eq!(
        buf.trim(),
        r#"
"1 + 1" BIN_EXPR
  "1" LITERAL
    "1" INT_NUMBER
  " " WHITESPACE
  "+" PLUS
  " " WHITESPACE
  "1" LITERAL
    "1" INT_NUMBER
"#
        .trim()
    );

    // To recursively process the tree, there are three approaches:
    // 1. explicitly call getter methods on AST nodes.
    // 2. use descendants and `AstNode::cast`.
    // 3. use descendants and `match_ast!`.
    //
    // Here's how the first one looks like:
    let exprs_cast: Vec<String> = file
        .syntax()
        .descendants()
        .filter_map(ast::Expr::cast)
        .map(|expr| expr.syntax().text().to_string())
        .collect();

    // An alternative is to use a macro.
    let mut exprs_visit = Vec::new();
    for node in file.syntax().descendants() {
        match_ast! {
            match node {
                ast::Expr(it) => {
                    let res = it.syntax().text().to_string();
                    exprs_visit.push(res);
                },
                _ => (),
            }
        }
    }
    assert_eq!(exprs_cast, exprs_visit);
}
<commit_msg>Make ra_syntax::{SyntaxNodeChildren, SyntaxElementChildren} public.<commit_after>//! Syntax Tree library used throughout the rust analyzer.
//!
//! Properties:
//!   - easy and fast incremental re-parsing
//!   - graceful handling of errors
//!   - full-fidelity representation (*any* text can be precisely represented as
//!     a syntax tree)
//!
//! For more information, see the [RFC]. Current implementation is inspired by
//! the [Swift] one.
//!
//! The most interesting modules here are `syntax_node` (which defines concrete
//! syntax tree) and `ast` (which defines abstract syntax tree on top of the
//! CST). The actual parser live in a separate `ra_parser` crate, though the
//! lexer lives in this crate.
//!
//! See `api_walkthrough` test in this file for a quick API tour!
//!
//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>

#[allow(unused)]
macro_rules! eprintln {
    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}

mod syntax_node;
mod syntax_error;
mod parsing;
mod validation;
mod ptr;
#[cfg(test)]
mod tests;

pub mod algo;
pub mod ast;
#[doc(hidden)]
pub mod fuzz;

use std::{marker::PhantomData, sync::Arc};

use ra_text_edit::Indel;
use stdx::format_to;

use crate::syntax_node::GreenNode;

pub use crate::{
    algo::InsertPosition,
    ast::{AstNode, AstToken},
    parsing::{lex_single_syntax_kind, lex_single_valid_syntax_kind, tokenize, Token},
    ptr::{AstPtr, SyntaxNodePtr},
    syntax_error::SyntaxError,
    syntax_node::{
        Direction, NodeOrToken, SyntaxElement, SyntaxElementChildren, SyntaxNode,
        SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder,
    },
};
pub use ra_parser::{SyntaxKind, T};
pub use rowan::{SmolStr, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent};

/// `Parse` is the result of the parsing: a syntax tree and a collection of
/// errors.
///
/// Note that we always produce a syntax tree, even for completely invalid
/// files.
#[derive(Debug, PartialEq, Eq)]
pub struct Parse<T> {
    green: GreenNode,
    errors: Arc<Vec<SyntaxError>>,
    _ty: PhantomData<fn() -> T>,
}

impl<T> Clone for Parse<T> {
    fn clone(&self) -> Parse<T> {
        Parse { green: self.green.clone(), errors: self.errors.clone(), _ty: PhantomData }
    }
}

impl<T> Parse<T> {
    fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
        Parse { green, errors: Arc::new(errors), _ty: PhantomData }
    }

    pub fn syntax_node(&self) -> SyntaxNode {
        SyntaxNode::new_root(self.green.clone())
    }
}

impl<T: AstNode> Parse<T> {
    pub fn to_syntax(self) -> Parse<SyntaxNode> {
        Parse { green: self.green, errors: self.errors, _ty: PhantomData }
    }

    pub fn tree(&self) -> T {
        T::cast(self.syntax_node()).unwrap()
    }

    pub fn errors(&self) -> &[SyntaxError] {
        &*self.errors
    }

    pub fn ok(self) -> Result<T, Arc<Vec<SyntaxError>>> {
        if self.errors.is_empty() {
            Ok(self.tree())
        } else {
            Err(self.errors)
        }
    }
}

impl Parse<SyntaxNode> {
    pub fn cast<N: AstNode>(self) -> Option<Parse<N>> {
        if N::cast(self.syntax_node()).is_some() {
            Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData })
        } else {
            None
        }
    }
}

impl Parse<SourceFile> {
    pub fn debug_dump(&self) -> String {
        let mut buf = format!("{:#?}", self.tree().syntax());
        for err in self.errors.iter() {
            format_to!(buf, "error {:?}: {}\n", err.range(), err);
        }
        buf
    }

    pub fn reparse(&self, indel: &Indel) -> Parse<SourceFile> {
        self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel))
    }

    fn incremental_reparse(&self, indel: &Indel) -> Option<Parse<SourceFile>> {
        // FIXME: validation errors are not handled here
        parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map(
            |(green_node, errors, _reparsed_range)| Parse {
                green: green_node,
                errors: Arc::new(errors),
                _ty: PhantomData,
            },
        )
    }

    fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> {
        let mut text = self.tree().syntax().text().to_string();
        indel.apply(&mut text);
        SourceFile::parse(&text)
    }
}

/// `SourceFile` represents a parse tree for a single Rust file.
pub use crate::ast::SourceFile;

impl SourceFile {
    pub fn parse(text: &str) -> Parse<SourceFile> {
        let (green, mut errors) = parsing::parse_text(text);
        let root = SyntaxNode::new_root(green.clone());

        if cfg!(debug_assertions) {
            validation::validate_block_structure(&root);
        }

        errors.extend(validation::validate(&root));

        assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
        Parse { green, errors: Arc::new(errors), _ty: PhantomData }
    }
}

/// Matches a `SyntaxNode` against an `ast` type.
///
/// # Example:
///
/// ```ignore
/// match_ast! {
///     match node {
///         ast::CallExpr(it) => { ... },
///         ast::MethodCallExpr(it) => { ... },
///         ast::MacroCall(it) => { ... },
///         _ => None,
///     }
/// }
/// ```
#[macro_export]
macro_rules! match_ast {
    (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };

    (match ($node:expr) {
        $( ast::$ast:ident($it:ident) => $res:expr, )*
        _ => $catch_all:expr $(,)?
    }) => {{
        $( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
        { $catch_all }
    }};
}

/// This test does not assert anything and instead just shows off the crate's
/// API.
#[test]
fn api_walkthrough() {
    use ast::{ModuleItemOwner, NameOwner};

    let source_code = "
        fn foo() {
            1 + 1
        }
    ";
    // `SourceFile` is the main entry point.
    //
    // The `parse` method returns a `Parse` -- a pair of syntax tree and a list
    // of errors. That is, syntax tree is constructed even in presence of errors.
    let parse = SourceFile::parse(source_code);
    assert!(parse.errors().is_empty());

    // The `tree` method returns an owned syntax node of type `SourceFile`.
    // Owned nodes are cheap: inside, they are `Rc` handles to the underling data.
    let file: SourceFile = parse.tree();

    // `SourceFile` is the root of the syntax tree. We can iterate file's items.
    // Let's fetch the `foo` function.
    let mut func = None;
    for item in file.items() {
        match item {
            ast::ModuleItem::FnDef(f) => func = Some(f),
            _ => unreachable!(),
        }
    }
    let func: ast::FnDef = func.unwrap();

    // Each AST node has a bunch of getters for children. All getters return
    // `Option`s though, to account for incomplete code. Some getters are common
    // for several kinds of node. In this case, a trait like `ast::NameOwner`
    // usually exists. By convention, all ast types should be used with `ast::`
    // qualifier.
    let name: Option<ast::Name> = func.name();
    let name = name.unwrap();
    assert_eq!(name.text(), "foo");

    // Let's get the `1 + 1` expression!
    let body: ast::BlockExpr = func.body().unwrap();
    let expr: ast::Expr = body.expr().unwrap();

    // Enums are used to group related ast nodes together, and can be used for
    // matching. However, because there are no public fields, it's possible to
    // match only the top level enum: that is the price we pay for increased API
    // flexibility
    let bin_expr: &ast::BinExpr = match &expr {
        ast::Expr::BinExpr(e) => e,
        _ => unreachable!(),
    };

    // Besides the "typed" AST API, there's an untyped CST one as well.
    // To switch from AST to CST, call `.syntax()` method:
    let expr_syntax: &SyntaxNode = expr.syntax();

    // Note how `expr` and `bin_expr` are in fact the same node underneath:
    assert!(expr_syntax == bin_expr.syntax());

    // To go from CST to AST, `AstNode::cast` function is used:
    let _expr: ast::Expr = match ast::Expr::cast(expr_syntax.clone()) {
        Some(e) => e,
        None => unreachable!(),
    };

    // The two properties each syntax node has is a `SyntaxKind`:
    assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);

    // And text range:
    assert_eq!(expr_syntax.text_range(), TextRange::new(32.into(), 37.into()));

    // You can get node's text as a `SyntaxText` object, which will traverse the
    // tree collecting token's text:
    let text: SyntaxText = expr_syntax.text();
    assert_eq!(text.to_string(), "1 + 1");

    // There's a bunch of traversal methods on `SyntaxNode`:
    assert_eq!(expr_syntax.parent().as_ref(), Some(body.syntax()));
    assert_eq!(body.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
    assert_eq!(
        expr_syntax.next_sibling_or_token().map(|it| it.kind()),
        Some(SyntaxKind::WHITESPACE)
    );

    // As well as some iterator helpers:
    let f = expr_syntax.ancestors().find_map(ast::FnDef::cast);
    assert_eq!(f, Some(func));
    assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
    assert_eq!(
        expr_syntax.descendants_with_tokens().count(),
        8, // 5 tokens `1`, ` `, `+`, ` `, `!`
           // 2 child literal expressions: `1`, `1`
           // 1 the node itself: `1 + 1`
    );

    // There's also a `preorder` method with a more fine-grained iteration control:
    let mut buf = String::new();
    let mut indent = 0;
    for event in expr_syntax.preorder_with_tokens() {
        match event {
            WalkEvent::Enter(node) => {
                let text = match &node {
                    NodeOrToken::Node(it) => it.text().to_string(),
                    NodeOrToken::Token(it) => it.text().to_string(),
                };
                format_to!(buf, "{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
                indent += 2;
            }
            WalkEvent::Leave(_) => indent -= 2,
        }
    }
    assert_eq!(indent, 0);
    assert_eq!(
        buf.trim(),
        r#"
"1 + 1" BIN_EXPR
  "1" LITERAL
    "1" INT_NUMBER
  " " WHITESPACE
  "+" PLUS
  " " WHITESPACE
  "1" LITERAL
    "1" INT_NUMBER
"#
        .trim()
    );

    // To recursively process the tree, there are three approaches:
    // 1. explicitly call getter methods on AST nodes.
    // 2. use descendants and `AstNode::cast`.
    // 3. use descendants and `match_ast!`.
    //
    // Here's how the first one looks like:
    let exprs_cast: Vec<String> = file
        .syntax()
        .descendants()
        .filter_map(ast::Expr::cast)
        .map(|expr| expr.syntax().text().to_string())
        .collect();

    // An alternative is to use a macro.
    let mut exprs_visit = Vec::new();
    for node in file.syntax().descendants() {
        match_ast! {
            match node {
                ast::Expr(it) => {
                    let res = it.syntax().text().to_string();
                    exprs_visit.push(res);
                },
                _ => (),
            }
        }
    }
    assert_eq!(exprs_cast, exprs_visit);
}
<|endoftext|>


<commit_before><commit_msg>Update no_atomic.rs<commit_after><|endoftext|>


<commit_before>use clap::AppSettings;
use roff::{bold, italic, roman, Inline, Roff};

pub(crate) fn subcommand_heading(cmd: &clap::Command) -> String {
    match cmd.get_subcommand_help_heading() {
        Some(title) => title.to_string(),
        None => "SUBCOMMANDS".to_string(),
    }
}

pub(crate) fn about(roff: &mut Roff, cmd: &clap::Command) {
    let s = match cmd.get_about().or_else(|| cmd.get_long_about()) {
        Some(about) => format!("{} - {}", cmd.get_name(), about),
        None => cmd.get_name().to_string(),
    };
    roff.text([roman(&s)]);
}

pub(crate) fn description(roff: &mut Roff, cmd: &clap::Command) {
    if let Some(about) = cmd.get_long_about().or_else(|| cmd.get_about()) {
        for line in about.lines() {
            if line.trim().is_empty() {
                roff.control("PP", []);
            } else {
                roff.text([roman(line)]);
            }
        }
    }
}

pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) {
    let mut line = vec![bold(cmd.get_name()), roman(" ")];

    for opt in cmd.get_arguments() {
        let (lhs, rhs) = option_markers(opt);
        match (opt.get_short(), opt.get_long()) {
            (Some(short), Some(long)) => {
                line.push(roman(lhs));
                line.push(bold(&format!("-{}", short)));
                line.push(roman("|"));
                line.push(bold(&format!("--{}", long)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (Some(short), None) => {
                line.push(roman(lhs));
                line.push(bold(&format!("-{} ", short)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (None, Some(long)) => {
                line.push(roman(lhs));
                line.push(bold(&format!("--{}", long)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (None, None) => (),
        };
    }

    for arg in cmd.get_positionals() {
        let (lhs, rhs) = option_markers(arg);
        line.push(roman(lhs));
        line.push(italic(arg.get_id()));
        line.push(roman(rhs));
        line.push(roman(" "));
    }

    if cmd.has_subcommands() {
        let (lhs, rhs) = subcommand_markers(cmd);
        line.push(roman(lhs));
        line.push(italic(
            &cmd.get_subcommand_value_name()
                .unwrap_or(&subcommand_heading(cmd))
                .to_lowercase(),
        ));
        line.push(roman(rhs));
    }

    roff.text(line);
}

pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) {
    let items: Vec<_> = cmd.get_arguments().filter(|i| !i.is_hide_set()).collect();

    for opt in items.iter().filter(|a| !a.is_positional()) {
        let mut body = vec![];

        let mut header = match (opt.get_short(), opt.get_long()) {
            (Some(short), Some(long)) => {
                vec![short_option(short), roman(", "), long_option(long)]
            }
            (Some(short), None) => vec![short_option(short)],
            (None, Some(long)) => vec![long_option(long)],
            (None, None) => vec![],
        };

        if let Some(value) = &opt.get_value_names() {
            header.push(roman("="));
            header.push(italic(&value.join(" ")));
        }

        if let Some(defs) = option_default_values(opt) {
            header.push(roman(" "));
            header.push(roman(&defs));
        }

        if let Some(help) = opt.get_long_help().or_else(|| opt.get_help()) {
            body.push(roman(help));
        }

        if let Some(mut env) = option_environment(opt) {
            body.append(&mut env);
        }

        roff.control("TP", []);
        roff.text(header);
        roff.text(body);
    }

    for pos in items.iter().filter(|a| a.is_positional()) {
        let (lhs, rhs) = option_markers(pos);
        let name = format!("{}{}{}", lhs, pos.get_id(), rhs);

        let mut header = vec![bold(&name)];

        let mut body = vec![];

        if let Some(defs) = option_default_values(pos) {
            header.push(roman(&format!(" {}", defs)));
        }

        if let Some(help) = pos.get_long_help().or_else(|| pos.get_help()) {
            body.push(roman(&help.to_string()));
        }

        if let Some(mut env) = option_environment(pos) {
            body.append(&mut env);
        }

        roff.control("TP", []);
        roff.text(body);
    }
}

pub(crate) fn subcommands(roff: &mut Roff, cmd: &clap::Command, section: &str) {
    for sub in cmd.get_subcommands().filter(|s| !s.is_hide_set()) {
        roff.control("TP", []);

        let name = format!("{}-{}({})", cmd.get_name(), sub.get_name(), section);
        roff.text([roman(&name)]);

        if let Some(about) = sub.get_about().or_else(|| sub.get_long_about()) {
            for line in about.lines() {
                roff.text([roman(line)]);
            }
        }
    }
}

pub(crate) fn version(cmd: &clap::Command) -> String {
    format!(
        "v{}",
        cmd.get_long_version()
            .or_else(|| cmd.get_version())
            .unwrap()
    )
}

pub(crate) fn after_help(roff: &mut Roff, cmd: &clap::Command) {
    if let Some(about) = cmd.get_after_long_help().or_else(|| cmd.get_after_help()) {
        for line in about.lines() {
            roff.text([roman(line)]);
        }
    }
}

fn subcommand_markers(cmd: &clap::Command) -> (&'static str, &'static str) {
    #[allow(deprecated)]
    markers(cmd.is_subcommand_required_set() || cmd.is_set(AppSettings::SubcommandRequiredElseHelp))
}

fn option_markers(opt: &clap::Arg) -> (&'static str, &'static str) {
    markers(opt.is_required_set())
}

fn markers(required: bool) -> (&'static str, &'static str) {
    if required {
        ("<", ">")
    } else {
        ("[", "]")
    }
}

fn short_option(opt: char) -> Inline {
    bold(&format!("-{}", opt))
}

fn long_option(opt: &str) -> Inline {
    bold(&format!("--{}", opt))
}

fn option_environment(opt: &clap::Arg) -> Option<Vec<Inline>> {
    if opt.is_hide_env_set() {
        return None;
    } else if let Some(env) = opt.get_env() {
        return Some(vec![
            roman("May also be specified with the "),
            bold(env.to_string_lossy().to_owned()),
            roman(" environment variable. "),
        ]);
    }

    None
}

fn option_default_values(opt: &clap::Arg) -> Option<String> {
    if !opt.get_default_values().is_empty() {
        let values = opt
            .get_default_values()
            .iter()
            .map(|s| s.to_string_lossy())
            .collect::<Vec<_>>()
            .join(",");

        return Some(format!("[default: {}]", values));
    }

    None
}
<commit_msg>fix(clap_mangen): Use value_names for positionals<commit_after>use clap::AppSettings;
use roff::{bold, italic, roman, Inline, Roff};

pub(crate) fn subcommand_heading(cmd: &clap::Command) -> String {
    match cmd.get_subcommand_help_heading() {
        Some(title) => title.to_string(),
        None => "SUBCOMMANDS".to_string(),
    }
}

pub(crate) fn about(roff: &mut Roff, cmd: &clap::Command) {
    let s = match cmd.get_about().or_else(|| cmd.get_long_about()) {
        Some(about) => format!("{} - {}", cmd.get_name(), about),
        None => cmd.get_name().to_string(),
    };
    roff.text([roman(&s)]);
}

pub(crate) fn description(roff: &mut Roff, cmd: &clap::Command) {
    if let Some(about) = cmd.get_long_about().or_else(|| cmd.get_about()) {
        for line in about.lines() {
            if line.trim().is_empty() {
                roff.control("PP", []);
            } else {
                roff.text([roman(line)]);
            }
        }
    }
}

pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) {
    let mut line = vec![bold(cmd.get_name()), roman(" ")];

    for opt in cmd.get_arguments() {
        let (lhs, rhs) = option_markers(opt);
        match (opt.get_short(), opt.get_long()) {
            (Some(short), Some(long)) => {
                line.push(roman(lhs));
                line.push(bold(&format!("-{}", short)));
                line.push(roman("|"));
                line.push(bold(&format!("--{}", long)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (Some(short), None) => {
                line.push(roman(lhs));
                line.push(bold(&format!("-{} ", short)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (None, Some(long)) => {
                line.push(roman(lhs));
                line.push(bold(&format!("--{}", long)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (None, None) => (),
        };
    }

    for arg in cmd.get_positionals() {
        let (lhs, rhs) = option_markers(arg);
        line.push(roman(lhs));
        if let Some(value) = arg.get_value_names() {
            line.push(bold(value.join(" ")));
        } else {
            line.push(bold(arg.get_id()));
        }
        line.push(roman(rhs));
        line.push(roman(" "));
    }

    if cmd.has_subcommands() {
        let (lhs, rhs) = subcommand_markers(cmd);
        line.push(roman(lhs));
        line.push(italic(
            &cmd.get_subcommand_value_name()
                .unwrap_or(&subcommand_heading(cmd))
                .to_lowercase(),
        ));
        line.push(roman(rhs));
    }

    roff.text(line);
}

pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) {
    let items: Vec<_> = cmd.get_arguments().filter(|i| !i.is_hide_set()).collect();

    for opt in items.iter().filter(|a| !a.is_positional()) {
        let mut header = match (opt.get_short(), opt.get_long()) {
            (Some(short), Some(long)) => {
                vec![short_option(short), roman(", "), long_option(long)]
            }
            (Some(short), None) => vec![short_option(short)],
            (None, Some(long)) => vec![long_option(long)],
            (None, None) => vec![],
        };

        if let Some(value) = &opt.get_value_names() {
            header.push(roman("="));
            header.push(italic(&value.join(" ")));
        }

        if let Some(defs) = option_default_values(opt) {
            header.push(roman(" "));
            header.push(roman(&defs));
        }

        let mut body = vec![];
        if let Some(help) = opt.get_long_help().or_else(|| opt.get_help()) {
            body.push(roman(help));
        }

        if let Some(mut env) = option_environment(opt) {
            body.append(&mut env);
        }

        roff.control("TP", []);
        roff.text(header);
        roff.text(body);
    }

    for pos in items.iter().filter(|a| a.is_positional()) {
        let mut header = vec![];
        let (lhs, rhs) = option_markers(pos);
        header.push(roman(lhs));
        if let Some(value) = pos.get_value_names() {
            header.push(bold(value.join(" ")));
        } else {
            header.push(bold(pos.get_id()));
        };
        header.push(roman(rhs));

        if let Some(defs) = option_default_values(pos) {
            header.push(roman(&format!(" {}", defs)));
        }

        let mut body = vec![];
        if let Some(help) = pos.get_long_help().or_else(|| pos.get_help()) {
            body.push(roman(&help.to_string()));
        }

        if let Some(mut env) = option_environment(pos) {
            body.append(&mut env);
        }

        roff.control("TP", []);
        roff.text(header);
        roff.text(body);
    }
}

pub(crate) fn subcommands(roff: &mut Roff, cmd: &clap::Command, section: &str) {
    for sub in cmd.get_subcommands().filter(|s| !s.is_hide_set()) {
        roff.control("TP", []);

        let name = format!("{}-{}({})", cmd.get_name(), sub.get_name(), section);
        roff.text([roman(&name)]);

        if let Some(about) = sub.get_about().or_else(|| sub.get_long_about()) {
            for line in about.lines() {
                roff.text([roman(line)]);
            }
        }
    }
}

pub(crate) fn version(cmd: &clap::Command) -> String {
    format!(
        "v{}",
        cmd.get_long_version()
            .or_else(|| cmd.get_version())
            .unwrap()
    )
}

pub(crate) fn after_help(roff: &mut Roff, cmd: &clap::Command) {
    if let Some(about) = cmd.get_after_long_help().or_else(|| cmd.get_after_help()) {
        for line in about.lines() {
            roff.text([roman(line)]);
        }
    }
}

fn subcommand_markers(cmd: &clap::Command) -> (&'static str, &'static str) {
    #[allow(deprecated)]
    markers(cmd.is_subcommand_required_set() || cmd.is_set(AppSettings::SubcommandRequiredElseHelp))
}

fn option_markers(opt: &clap::Arg) -> (&'static str, &'static str) {
    markers(opt.is_required_set())
}

fn markers(required: bool) -> (&'static str, &'static str) {
    if required {
        ("<", ">")
    } else {
        ("[", "]")
    }
}

fn short_option(opt: char) -> Inline {
    bold(&format!("-{}", opt))
}

fn long_option(opt: &str) -> Inline {
    bold(&format!("--{}", opt))
}

fn option_environment(opt: &clap::Arg) -> Option<Vec<Inline>> {
    if opt.is_hide_env_set() {
        return None;
    } else if let Some(env) = opt.get_env() {
        return Some(vec![
            roman("May also be specified with the "),
            bold(env.to_string_lossy().to_owned()),
            roman(" environment variable. "),
        ]);
    }

    None
}

fn option_default_values(opt: &clap::Arg) -> Option<String> {
    if !opt.get_default_values().is_empty() {
        let values = opt
            .get_default_values()
            .iter()
            .map(|s| s.to_string_lossy())
            .collect::<Vec<_>>()
            .join(",");

        return Some(format!("[default: {}]", values));
    }

    None
}
<|endoftext|>