<commit_before>extern crate xml;
extern crate petgraph;

use common::{BuildTask};
use wincmd;

use std::fmt::{Display, Formatter};
use std::io::{Read, Error, ErrorKind};
use std::collections::{HashSet, HashMap};
use std::iter::FromIterator;

use self::petgraph::graph::{Graph, NodeIndex};

use self::xml::reader::EventReader;
use self::xml::reader::events::XmlEvent;

#[derive(Debug)]
pub enum XgParseError {
	AttributeNotFound(&'static str),
	ToolNotFound(String),
	DependencyNotFound(String),
}

impl Display for XgParseError {
	fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
		match self {
			&XgParseError::AttributeNotFound(ref attr) => write!(f, "attribute not found: {}", attr),
			&XgParseError::ToolNotFound(ref id) => write!(f, "сan't find tool with id: {}", id),
			&XgParseError::DependencyNotFound(ref id) => write!(f, "сan't find task for dependency with id: {}", id),
		}
	}
}

impl ::std::error::Error for XgParseError {
	fn description(&self) -> &str {
		match self {
			&XgParseError::AttributeNotFound(_) => "attribute not found",
			&XgParseError::ToolNotFound(_) => "сan't find tool with id",
			&XgParseError::DependencyNotFound(_) => "сan't find task for dependency with id",
		}
	}

	fn cause(&self) -> Option<&::std::error::Error> {
		None
	}
}

#[derive(Debug)]
struct XgTask {
	id: Option<String>,
	title: Option<String>,
	tool: String,
	working_dir: String,
	depends_on: Vec<String>,
}

#[derive(Debug)]
struct XgTool {
	id: String,
	exec: String,
	args: String,
	output: Option<String>,
}

pub fn parse<B: Read>(reader: B) -> Result<Graph<BuildTask, ()>, Error> {
	let mut parser = EventReader::new(reader);
	let mut tasks:Vec<XgTask> = Vec::new();
	let mut tools:HashMap<String, XgTool> = HashMap::new();
	for e in parser.events() {
		match e {
			XmlEvent::StartElement {name, attributes, ..} => {
				match &name.local_name[..] {
					"Task" => {
						tasks.push(try! (parse_task (attributes)));
					}
					"Tool" => {
						let tool = try! (parse_tool (attributes));
						tools.insert(tool.id.to_string(), tool);
					}
					_ => {}
				}
			}
			XmlEvent::EndElement{..} => {
			}
			_ => {
			}
		}
	}
	parse_create_graph(&tasks, &tools)
}

fn parse_create_graph(tasks:&Vec<XgTask>, tools:&HashMap<String, XgTool>) -> Result<Graph<BuildTask, ()>, Error> {
	let mut graph: Graph<BuildTask, ()> = Graph::new();
	let mut nodes: Vec<NodeIndex> = Vec::new();
	let mut task_refs: HashMap<&str, NodeIndex> = HashMap::new();
	for task in tasks.iter() {
		match tools.get(&task.tool){
			Some(tool) => {
				let node = graph.add_node(BuildTask {
					title: match task.title {
						Some(ref v) => {v.clone()}
						_ => {
							match tool.output {
								Some(ref v) => {v.clone()}
								_ => String::new()
							}
						}
					},
					exec: tool.exec.clone(),
					args: wincmd::parse(&tool.args),
					working_dir : task.working_dir.clone(),
				});
				match task.id {
					Some(ref v) => {
						task_refs.insert(&v, node);
					}
					_ => {}
				}
				nodes.push(node);
			}
			_ => {
				return Err(Error::new(ErrorKind::InvalidInput, XgParseError::ToolNotFound(task.tool.clone())))
			}
		}
	}
	for idx in 0..nodes.len() {
		let ref task = tasks[idx];
		let ref node = nodes[idx];
		for id in task.depends_on.iter() {
			match task_refs.get(&id[..]) {
				Some(v) => {
					graph.add_edge(*node, *v, ());
				}
				_ => {
					return Err(Error::new(ErrorKind::InvalidInput, XgParseError::DependencyNotFound(task.tool.clone())))
				}
			}
		}
	}
	Ok(graph)
}

fn map_attributes (attributes: Vec<xml::attribute::OwnedAttribute>) -> HashMap<String, String> {
	HashMap::from_iter(attributes.into_iter().map(|v| (v.name.local_name, v.value)))
}

fn parse_task (attributes: Vec<xml::attribute::OwnedAttribute>)->Result<XgTask, Error> {
	let mut attrs = map_attributes(attributes);
	let tool = try! (take_attr(&mut attrs, "Tool"));
	let working_dir = try! (take_attr(&mut attrs, "WorkingDir"));
	// DependsOn
	let depends_on : HashSet<String> = match attrs.remove("DependsOn") {
		Some(v) => HashSet::from_iter(v.split(";").map(|v| v.to_string())),
		_ => HashSet::new()
	};

	Ok(XgTask {
		id: attrs.remove("Name"),
		title: attrs.remove("Caption"),
		tool: tool,
		working_dir: working_dir,
		depends_on: depends_on.into_iter().collect::<Vec<String>>(),
	})
}

fn parse_tool (attributes: Vec<xml::attribute::OwnedAttribute>)->Result<XgTool, Error> {
	let mut attrs = map_attributes(attributes);
	let id = try! (take_attr(&mut attrs, "Name"));
	let exec = try! (take_attr(&mut attrs, "Path"));
	Ok(XgTool {
		id: id,
		exec: exec,
		output: attrs.remove("OutputPrefix"),
		args: match attrs.remove("Params") {
			Some(v) => v,
			_ => String::new()
		},
	})
}

fn take_attr(attrs: &mut HashMap<String, String>, attr: &'static str) -> Result<String, Error> {
	match attrs.remove(attr) {
		Some(v) => Ok(v),
		_ => Err(Error::new(ErrorKind::InvalidInput, XgParseError::AttributeNotFound(attr)))
	}
}
<commit_msg>Rewrite XGE-tasks file parser<commit_after>extern crate xml;
extern crate petgraph;

use common::{BuildTask};
use wincmd;

use std::fmt::{Display, Formatter};
use std::io::{Read, Error, ErrorKind};
use std::collections::{HashSet, HashMap};
use std::iter::FromIterator;

use self::petgraph::graph::{Graph, NodeIndex};

use self::xml::reader::EventReader;
use self::xml::reader::Events;
use self::xml::reader::events::XmlEvent;

#[derive(Debug)]
pub enum XgParseError {
	AttributeNotFound(&'static str),
	EnvironmentNotFound(String),
	ToolNotFound(String),
	DependencyNotFound(String),
	InvalidStreamFormat,
	EndOfStream,
}

impl Display for XgParseError {
	fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
		match self {
			&XgParseError::AttributeNotFound(ref attr) => write!(f, "attribute not found: {}", attr),
			&XgParseError::EnvironmentNotFound(ref id) => write!(f, "сan't find environment with id: {}", id),
			&XgParseError::ToolNotFound(ref id) => write!(f, "сan't find tool with id: {}", id),
			&XgParseError::DependencyNotFound(ref id) => write!(f, "сan't find task for dependency with id: {}", id),
			&XgParseError::InvalidStreamFormat => write!(f, "unexpected XML-stream root element"),
			&XgParseError::EndOfStream => write!(f, "unexpended end of stream"),
		}
	}
}

impl ::std::error::Error for XgParseError {
	fn description(&self) -> &str {
		match self {
			&XgParseError::AttributeNotFound(_) => "attribute not found",
			&XgParseError::EnvironmentNotFound(_) => "сan't find environment by id",
			&XgParseError::ToolNotFound(_) => "сan't find tool by id",
			&XgParseError::DependencyNotFound(_) => "сan't find task for dependency by id",
			&XgParseError::InvalidStreamFormat => "unexpected XML-stream root element",
			&XgParseError::EndOfStream => "unexpended end of stream",
		}
	}

	fn cause(&self) -> Option<&::std::error::Error> {
		None
	}
}

#[derive(Debug)]
struct XgEnvironment {
    variables: HashMap<String, String>,
    tools: HashMap<String, XgTool>,
}

#[derive(Debug)]
struct XgProject {
	env: String,
	tasks: HashMap<String, XgTask>,
}

#[derive(Debug)]
struct XgTask {
	title: Option<String>,
	tool: String,
	working_dir: String,
	depends_on: Vec<String>,
}

#[derive(Debug)]
struct XgTool {
	exec: String,
	args: String,
	output: Option<String>,
}

pub fn parse<R: Read>(reader: R) -> Result<Graph<BuildTask, ()>, Error> {
	let mut parser = EventReader::new(reader);
	let mut events = parser.events();
	loop {
		match events.next() {
			Some(XmlEvent::StartElement {name, ..}) => {
				return match &name.local_name[..] {
					"BuildSet" => parse_build_set(&mut events),
					_ => Err(Error::new(ErrorKind::InvalidInput, XgParseError::InvalidStreamFormat)),
				}
			}
			Some(_) => {}
			None => {
				return Err(Error::new(ErrorKind::InvalidInput, XgParseError::EndOfStream));
			}
		}
	}
}

pub fn parse_build_set<R: Read>(events: &mut Events<R>) -> Result<Graph<BuildTask, ()>, Error> {
	let mut envs:HashMap<String, XgEnvironment> = HashMap::new();
	let mut projects:Vec<XgProject> = Vec::new();
	loop {
		match events.next() {
			Some(XmlEvent::StartElement {name, attributes, ..}) => {
				match &name.local_name[..] {
					"Environments" => {try! (parse_environments(events, &mut envs));}
					"Project" => {
						let mut attrs = map_attributes(attributes);
						projects.push(XgProject {
							env: try! (take_attr(&mut attrs, "Env")),
							tasks: try! (parse_tasks(events)),
						});
					}
					_ => {try! (parse_skip(events, ()));}
				}
			}
			Some(_) => {}
			None => {
				break;
			}
		}
	}
	parse_create_graph(envs, projects)
}

fn parse_environments<R: Read>(events: &mut Events<R>, envs: &mut HashMap<String, XgEnvironment>) -> Result<(), Error> {
	loop {
		match events.next() {
			Some(XmlEvent::StartElement {name, attributes, ..}) => {
				match &name.local_name[..] {
					"Environment" => {
						let mut attrs = map_attributes(attributes);
						let name = try! (take_attr(&mut attrs, "Name"));
						envs.insert(name, try!(parse_environment(events)));
					}
					_ => {try!(parse_skip(events, ()));}
				}
			}
			Some(XmlEvent::EndElement {..}) => {return Ok(());}
			Some(_) => {}
			None => {return Err(Error::new(ErrorKind::InvalidInput, XgParseError::EndOfStream));}
		}
	}
}

fn parse_environment<R: Read>(events: &mut Events<R>) -> Result<XgEnvironment, Error> {
	let mut env = XgEnvironment {
		variables: HashMap::new(),
		tools: HashMap::new(),
	};
	loop {
		match events.next() {
			Some(XmlEvent::StartElement {name, ..}) => {
				match &name.local_name[..] {
					"Variables" => try!(parse_variables(events, &mut env.variables)),
					"Tools" => try!(parse_tools(events, &mut env.tools)),
					_ => try!(parse_skip(events, ())),
				};
			}
			Some(XmlEvent::EndElement {..}) => {return Ok(env);}
			Some(_) => {}
			None => {return Err(Error::new(ErrorKind::InvalidInput, XgParseError::EndOfStream));}
		}
	}
}

fn parse_variables<R: Read>(events: &mut Events<R>, variables: &mut HashMap<String, String>) -> Result<(), Error> {
	loop {
		match events.next() {
			Some(XmlEvent::StartElement {name, attributes, ..}) => {
				match &name.local_name[..] {
					"Variable" => {
						let mut attrs = map_attributes(attributes);
						let name = try! (take_attr(&mut attrs, "Name"));
						let value = try! (take_attr(&mut attrs, "Value"));
						variables.insert(name, value);
					}
					_ => {
					}
				}
				try!(parse_skip(events, ()));
			}
			Some(XmlEvent::EndElement {..}) => {
				return Ok(());
			}
			Some(_) => {}
			None => {
				return Err(Error::new(ErrorKind::InvalidInput, XgParseError::EndOfStream));
			}
		}
	}
}

fn parse_tools<R: Read>(events: &mut Events<R>, tools: &mut HashMap<String, XgTool>) -> Result<(), Error> {
	loop {
		match events.next() {
			Some(XmlEvent::StartElement {name, attributes, ..}) => {
				match &name.local_name[..] {
					"Tool" => {
						let mut attrs = map_attributes(attributes);
						let name = try! (take_attr(&mut attrs, "Name"));
						let exec = try! (take_attr(&mut attrs, "Path"));
						tools.insert(name, XgTool {
							exec: exec,
							output: attrs.remove("OutputPrefix"),
							args: attrs.remove("Params").unwrap_or_else(|| String::new()),
						});
					}
					_ => {
					}
				}
				try!(parse_skip(events, ()));
			}
			Some(XmlEvent::EndElement {..}) => {
				return Ok(());
			}
			Some(_) => {}
			None => {
				return Err(Error::new(ErrorKind::InvalidInput, XgParseError::EndOfStream));
			}
		}
	}
}

fn parse_tasks<R: Read>(events: &mut Events<R>) -> Result<HashMap<String, XgTask>, Error> {
	let mut tasks = HashMap::new();
	loop {
		match events.next() {
			Some(XmlEvent::StartElement {name, attributes, ..}) => {
				match &name.local_name[..] {
					"Task" => {
						let mut attrs = map_attributes(attributes);
						let name = try! (take_attr(&mut attrs, "Name"));
						let tool = try! (take_attr(&mut attrs, "Tool"));
						let working_dir = try! (take_attr(&mut attrs, "WorkingDir"));
						// DependsOn
						let depends_on : HashSet<String> = match attrs.remove("DependsOn") {
							Some(v) => HashSet::from_iter(v.split(";").map(|v| v.to_string())),
							_ => HashSet::new()
						};

						tasks.insert(name.clone(), XgTask {
							title: attrs.remove("Caption"),
							tool: tool,
							working_dir: working_dir,
							depends_on: depends_on.into_iter().collect::<Vec<String>>(),
						});
					}
					_ => {
					}
				}
				try!(parse_skip(events, ()));
			}
			Some(XmlEvent::EndElement {..}) => {
				return Ok(tasks);
			}
			Some(_) => {}
			None => {
				return Err(Error::new(ErrorKind::InvalidInput, XgParseError::EndOfStream));
			}
		}
	}
}

fn parse_skip<R: Read, T>(events: &mut Events<R>, result: T) -> Result<T, Error> {
	let mut depth: isize = 0;
	loop {
		match events.next() {
			Some(XmlEvent::StartElement {..}) => {
				depth += 1;
			}
			Some(XmlEvent::EndElement {..}) => {
				if depth == 0 {break;}
				depth -= 1;
			}
			Some(_) => {}
			None => {
				return Err(Error::new(ErrorKind::InvalidInput, XgParseError::EndOfStream));
			}
		}
	}
	Ok(result)
}

fn parse_create_graph(envs:HashMap<String, XgEnvironment>, projects:Vec<XgProject>) -> Result<Graph<BuildTask, ()>, Error> {
	let mut graph: Graph<BuildTask, ()> = Graph::new();
	for project in projects.into_iter() {
		let env = try!(envs.get(&project.env).ok_or_else(|| Error::new(ErrorKind::InvalidInput, XgParseError::EnvironmentNotFound(project.env.clone()))));
		try!(graph_project(&mut graph, project, env));
	}
	Ok(graph)
}

fn graph_project(graph: &mut Graph<BuildTask, ()>, project: XgProject, env: &XgEnvironment) -> Result<(), Error> {
	let mut nodes: Vec<NodeIndex> = Vec::new();
	let mut task_refs: HashMap<&str, NodeIndex> = HashMap::new();
	for (id, task) in project.tasks.iter() {
		let tool = try!(env.tools.get(&task.tool).ok_or_else(|| Error::new(ErrorKind::InvalidInput, XgParseError::ToolNotFound(task.tool.clone()))));
		let node = graph.add_node(BuildTask {
			title: match task.title {
				Some(ref v) => v.clone(),
				None => match tool.output {
					Some(ref v) => v.clone(),
					None => String::new(),
				},
			},
			exec: tool.exec.clone(),
			args: wincmd::parse(&tool.args),
			working_dir : task.working_dir.clone(),
		});
		task_refs.insert(&id, node);
		nodes.push(node);
	}
	for (src_id, task) in project.tasks.iter() {
		let src = task_refs.get(&src_id[..]).unwrap();
		for dst_id in task.depends_on.iter() {
			let dst = try!(task_refs.get(&dst_id[..]).ok_or_else(|| Error::new(ErrorKind::InvalidInput, XgParseError::DependencyNotFound(dst_id.clone()))));
			graph.add_edge(*src, *dst, ());
		}
	}
	Ok(())
}

fn map_attributes (attributes: Vec<xml::attribute::OwnedAttribute>) -> HashMap<String, String> {
	HashMap::from_iter(attributes.into_iter().map(|v| (v.name.local_name, v.value)))
}

fn take_attr(attrs: &mut HashMap<String, String>, attr: &'static str) -> Result<String, Error> {
	attrs.remove(attr).ok_or_else(|| Error::new(ErrorKind::InvalidInput, XgParseError::AttributeNotFound(attr)))
}
<|endoftext|>


<commit_before>use std::io::{BufReader, ErrorKind, Result};
use std::net::TcpStream;

use mio::{net::TcpListener, Events, Poll, PollOpt, Ready, Token};
use slab::Slab;

use birch::client::{ClientConnection, ClientEvent};
use birch::socket::{IrcReader, IrcSocketConfig, IrcWriter};

// TODO: blah
type NetworkId = usize;
type ClientId = usize;

struct Socket {
    reader: BufReader<mio::net::TcpStream>,
    writer: mio::net::TcpStream,
}

impl Socket {
    fn from_stream(stream: &mio::net::TcpStream) -> Result<Self> {
        let reader = BufReader::new(stream.try_clone()?);
        let writer = stream.try_clone()?;
        Ok(Self { reader, writer })
    }
}

impl std::io::Read for Socket {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.reader.read(buf)
    }
}

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

    fn flush(&mut self) -> Result<()> {
        self.writer.flush()
    }
}

struct Client {
    socket: Socket,
    conn: ClientConnection,
    network: Option<NetworkId>,
}

impl Client {
    fn new(stream: &mio::net::TcpStream) -> Self {
        let socket = Socket::from_stream(stream).expect("create client socket");
        let conn = ClientConnection::new();

        Self {
            socket,
            conn,
            network: None,
        }
    }
}

struct ClientManager {
    clients: Slab<(ClientId, Client)>,
}

impl ClientManager {
    fn new() -> Self {
        Self {
            clients: Slab::with_capacity(32),
        }
    }

    fn add(&mut self, client: Client) -> ClientId {
        let entry = self.clients.vacant_entry();
        let key = entry.key();
        entry.insert((key, client));

        key
    }

    fn find(&mut self, id: ClientId) -> Option<&mut Client> {
        self.clients.get_mut(id).map(|c| &mut c.1)
    }

    fn remove(&mut self, id: ClientId) -> Option<Client> {
        if !self.clients.contains(id) {
            return None;
        }

        Some(self.clients.remove(id).1)
    }

    // TODO: use an iterator
    fn with_network(&mut self, id: NetworkId) -> Vec<&mut Client> {
        self.clients
            .iter_mut()
            .map(|(_, (_, client))| client)
            .filter(|client| client.network.is_some())
            // TODO: .filter(|client| Some(id) == client.network)
            .collect()
    }
}

struct NetworkConfig<'a> {
    network_name: &'a str,
    nick: &'a str,
    socket: IrcSocketConfig,
}

struct Network<'a> {
    id: NetworkId,
    config: NetworkConfig<'a>,
    network: birch::network::NetworkConnection,
    socket: Socket,
}

enum SocketKind {
    Listener,
    Network(std::io::BufReader<mio::net::TcpStream>, mio::net::TcpStream),
    Client(ClientId),
}

struct SocketManager {
    sockets: Slab<SocketKind>,
}

impl SocketManager {
    fn new() -> Self {
        Self {
            sockets: Slab::with_capacity(1024),
        }
    }

    fn add(&mut self, socket: SocketKind) -> Token {
        Token(self.sockets.insert(socket))
    }

    fn add_client_listener(&mut self) -> Token {
        Token(self.sockets.insert(SocketKind::Listener))
    }

    fn add_network(&mut self, stream: &mio::net::TcpStream) -> Token {
        let reader = BufReader::new(stream.try_clone().expect("fork stream"));
        let writer = stream.try_clone().expect("fork stream");

        Token(self.sockets.insert(SocketKind::Network(reader, writer)))
    }

    fn find(&mut self, token: Token) -> Option<&mut SocketKind> {
        self.sockets.get_mut(token.0)
    }

    fn remove(&mut self, token: Token) -> bool {
        if self.sockets.contains(token.0) {
            self.sockets.remove(token.0);
            true
        } else {
            false
        }
    }
}

fn main() -> Result<()> {
    let poll = Poll::new()?;
    let mut socket_manager = SocketManager::new();
    let mut client_manager = ClientManager::new();

    let config = NetworkConfig {
        network_name: "freenode",
        nick: "ep",
        socket: IrcSocketConfig {
            addr: "irc.freenode.net:6667".parse().unwrap(),
            max_retries: Some(3),
        },
    };

    let mut network = birch::network::NetworkConnection::new("ep");
    // TODO: this sucks
    network.initialize()?;

    // TODO: Wrap in retry logic
    let network_stream =
        mio::net::TcpStream::from_stream(TcpStream::connect(config.socket.addr)?)?;

    poll.register(
        &network_stream,
        socket_manager.add_network(&network_stream),
        Ready::readable(),
        PollOpt::edge(),
    )?;

    let listener = TcpListener::bind(&"127.0.0.1:9123".parse().unwrap())?;
    poll.register(
        &listener,
        socket_manager.add_client_listener(),
        Ready::readable(),
        PollOpt::edge(),
    )?;

    loop {
        let mut events = Events::with_capacity(128);
        poll.poll(&mut events, None)?;

        for ev in events {
            let mut remove_socket = false;

            let socket = socket_manager.find(ev.token());
            match socket {
                Some(SocketKind::Listener) => loop {
                    match listener.accept() {
                        Ok((ref socket, _)) => {
                            let client_id = client_manager.add(Client::new(socket));
                            let token = socket_manager.add(SocketKind::Client(client_id));

                            poll.register(
                                socket,
                                token,
                                Ready::readable(),
                                PollOpt::edge(),
                            )?;
                        }
                        Err(ref e) if e.kind() == ErrorKind::WouldBlock => break,
                        e => panic!("err={:?}", e),
                    }
                },

                Some(SocketKind::Network(ref mut reader, writer)) => loop {
                    match reader.read_message() {
                        Ok(msg) => {
                            println!("[birch <- \u{1b}[37;1mnet\u{1b}[0m] {}", msg);
                            if let Err(err) = network.handle(&msg) {
                                println!("Failed to handle message: {}", err);
                                break;
                            }

                            for msg in network.network_messages() {
                                println!("[\u{1b}[37;1mbirch\u{1b}[0m -> net] {}", msg);
                                // TODO: handle errors
                                writer.write_message(&msg)?;
                            }

                            for msg in network.user_messages() {
                                // TODO: implement this correctly
                                for client in client_manager.with_network(0) {
                                    client.socket.write_message(&msg)?;
                                }
                            }
                        }
                        Err(ref e) if e.kind() == ErrorKind::WouldBlock => break,
                        Err(e) => {
                            // TODO: Reconnection logic
                            println!("Network errored, disconnecting: {}", e);
                            remove_socket = true;
                            break;
                        }
                    }
                },
                Some(SocketKind::Client(client_id)) => {
                    let client = client_manager
                        .find(*client_id)
                        .expect("activity on unassigned client id");

                    loop {
                        match client.socket.reader.read_message() {
                            Ok(msg) => {
                                println!(
                                    "[birch <- \u{1b}[37;1mclient({})\u{1b}[0m] {}",
                                    ev.token().0,
                                    msg
                                );

                                if let Err(err) = client.conn.handle_message(&msg) {
                                    println!("Failed to handle message: {}", err);
                                    break;
                                }

                                for event in client.conn.events() {
                                    match event {
                                        ClientEvent::WriteNetwork(ref _msg) => unimplemented!(),
                                        ClientEvent::WriteClient(ref msg) => {
                                            client.socket.write_message(msg)?
                                        }
                                        ClientEvent::RegistrationComplete => {
                                            // TODO: dynamic network
                                            // TODO: Update client's nick
                                            network
                                                .state
                                                .welcome_user(&mut client.socket)?;
                                        }
                                        ClientEvent::AuthAttempt(_auth) => {
                                            // TODO: actual auth here
                                            // TODO: set auth result back on client.conn
                                            client.network = Some(0);
                                        }
                                    }
                                }
                            }
                            Err(ref e) if e.kind() == ErrorKind::WouldBlock => break,
                            Err(e) => {
                                println!("Client errored, disconnecting: {}", e);
                                remove_socket = true;
                                break;
                            }
                        }
                    }
                }

                _ => unreachable!(),
            }

            // TODO: Deregister sockets.
            if remove_socket {
                socket_manager.remove(ev.token());
            }
        }
    }

    Ok(())
}
<commit_msg>Encapsulate socket logic and extract from main<commit_after>use std::io::{BufReader, ErrorKind, Result};
use std::net::{SocketAddr, TcpStream};

use mio::{net::TcpListener, Events, Poll, PollOpt, Ready, Token};
use slab::Slab;

use birch::client::{ClientConnection, ClientEvent};
use birch::network::NetworkConnection;
use birch::socket::{IrcReader, IrcSocketConfig, IrcWriter};

// TODO: blah
type NetworkId = usize;
type ClientId = usize;

struct Socket {
    reader: BufReader<mio::net::TcpStream>,
    writer: mio::net::TcpStream,
}

impl Socket {
    fn from_stream(stream: &mio::net::TcpStream) -> Result<Self> {
        let reader = BufReader::new(stream.try_clone()?);
        let writer = stream.try_clone()?;
        Ok(Self { reader, writer })
    }
}

impl std::io::Read for Socket {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.reader.read(buf)
    }
}

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

    fn flush(&mut self) -> Result<()> {
        self.writer.flush()
    }
}

struct Client {
    socket: Socket,
    conn: ClientConnection,
    network: Option<NetworkId>,
}

impl Client {
    fn new(stream: &mio::net::TcpStream) -> Self {
        let socket = Socket::from_stream(stream).expect("create client socket");
        let conn = ClientConnection::new();

        Self {
            socket,
            conn,
            network: None,
        }
    }
}

struct NetworkConfig {
    network_name: String,
    nick: String,
    socket: IrcSocketConfig,
}

struct Network {
    socket: Socket,
    conn: NetworkConnection,
}

impl Network {
    fn new(config: &NetworkConfig) -> Result<Self> {
        // TODO: Wrap in retry logic
        let stream =
            mio::net::TcpStream::from_stream(TcpStream::connect(&config.socket.addr)?)?;

        let socket = Socket::from_stream(&stream).expect("create network socket");
        let conn = NetworkConnection::new(&config.nick);

        Ok(Self { socket, conn })
    }
}

enum SocketKind {
    Listener,
    Network(NetworkId),
    Client(ClientId),
}

struct SocketManager {
    sockets: Slab<SocketKind>,
}

impl SocketManager {
    fn new() -> Self {
        Self {
            sockets: Slab::with_capacity(1024),
        }
    }

    fn add(&mut self, socket: SocketKind) -> Token {
        Token(self.sockets.insert(socket))
    }

    fn find(&self, token: Token) -> Option<&SocketKind> {
        self.sockets.get(token.0)
    }

    fn remove(&mut self, token: Token) -> bool {
        if self.sockets.contains(token.0) {
            self.sockets.remove(token.0);
            true
        } else {
            false
        }
    }
}

struct BirchServer {
    sockets: SocketManager,
    clients: Slab<Client>,
    // TODO: Since networks aren't dynamic, no benefit really to having this be a slab
    networks: Slab<Network>,
}

impl BirchServer {
    fn new() -> Self {
        Self {
            sockets: SocketManager::new(),
            clients: Slab::with_capacity(32),
            networks: Slab::with_capacity(16),
        }
    }

    fn accept_client(&mut self, poll: &Poll, socket: &mio::net::TcpStream) -> Result<()> {
        let client_id = self.clients.insert(Client::new(socket));
        let token = self.sockets.add(SocketKind::Client(client_id));

        poll.register(socket, token, Ready::readable(), PollOpt::edge())?;
        Ok(())
    }

    fn handle_network_message(&mut self, network_id: NetworkId) -> Result<()> {
        let network = self
            .networks
            .get_mut(network_id)
            .expect("activity on unassigned networks id");

        let msg = network.socket.reader.read_message()?;

        println!("[birch <- \u{1b}[37;1mnet\u{1b}[0m] {}", msg);
        network.conn.handle(&msg)?;

        for msg in network.conn.network_messages() {
            println!("[\u{1b}[37;1mbirch\u{1b}[0m -> net] {}", msg);
            network.socket.write_message(&msg)?;
        }

        for msg in network.conn.user_messages() {
            let clients = self
                .clients
                .iter_mut()
                .filter(|(_, c)| c.network == Some(network_id));

            for (_, client) in clients {
                if let Err(err) = client.socket.write_message(&msg) {
                    println!("Failed to write to client: {}", err);
                }
            }
        }

        Ok(())
    }

    fn handle_client_message(&mut self, client_id: ClientId) -> Result<()> {
        let client = self
            .clients
            .get_mut(client_id)
            .expect("activity on unassigned client id");

        let msg = client.socket.reader.read_message()?;

        println!(
            "[birch <- \u{1b}[37;1mclient({})\u{1b}[0m] {}",
            client_id, msg
        );

        client.conn.handle_message(&msg)?;

        for event in client.conn.events() {
            match event {
                ClientEvent::WriteNetwork(ref _msg) => unimplemented!(),
                ClientEvent::WriteClient(ref msg) => client.socket.write_message(msg)?,
                ClientEvent::RegistrationComplete => {
                    // TODO: Update client's nick
                    match client.network {
                        Some(id) => {
                            let network = self
                                .networks
                                .get_mut(id)
                                .expect("client referencing unknown network");

                            network.conn.state.welcome_user(&mut client.socket)?;
                        }
                        None => panic!("registration completed without network"),
                    }
                }
                ClientEvent::AuthAttempt(_auth) => {
                    let id = self.networks.iter().find(|n| true).map(|(id, _)| id);
                    // TODO: actual auth here
                    // TODO: set auth result back on client.conn
                    client.network = id
                }
            }
        }

        Ok(())
    }

    fn handle_poll_event(
        &mut self,
        poll: &Poll,
        listener: &TcpListener,
        token: Token,
    ) -> Result<()> {
        let mut remove_socket = false;

        match self.sockets.find(token) {
            None => panic!("token not associated with a socket: {:?}", token),

            Some(&SocketKind::Listener) => loop {
                match listener.accept() {
                    Ok((ref socket, _)) => self.accept_client(&poll, socket)?,
                    Err(ref e) if e.kind() == ErrorKind::WouldBlock => break,
                    e => panic!("failed to accept err={:?}", e),
                }
            },

            Some(&SocketKind::Network(network_id)) => loop {
                match self.handle_network_message(network_id) {
                    Ok(_) => (),
                    Err(ref e) if e.kind() == ErrorKind::WouldBlock => break,
                    Err(e) => {
                        // TODO: Reconnection logic
                        println!("Network errored, disconnecting: {}", e);
                        remove_socket = true;
                        break;
                    }
                }
            },
            Some(&SocketKind::Client(client_id)) => loop {
                match self.handle_client_message(client_id) {
                    Ok(_) => (),
                    Err(ref e) if e.kind() == ErrorKind::WouldBlock => break,
                    Err(e) => {
                        println!("Client errored, disconnecting: {}", e);
                        remove_socket = true;
                        break;
                    }
                }
            },
        }

        // TODO: Deregister sockets (remove from managers etc)
        if remove_socket {
            self.sockets.remove(token);
        }

        Ok(())
    }

    fn add_network(&mut self, config: &NetworkConfig) -> Result<NetworkId> {
        let mut network = Network::new(config)?;
        network.conn.initialize()?;

        let token = self.networks.insert(network);
        self.sockets.add(SocketKind::Network(token));

        Ok(token)
    }

    fn serve(&mut self, bind_addr: &SocketAddr) -> Result<()> {
        let poll = Poll::new()?;

        // TODO: is this iffy? Should we just store the Poll object on
        // the struct itself?
        for (id, network) in self.networks.iter_mut() {
            poll.register(
                &network.socket.writer,
                Token(id),
                Ready::readable(),
                PollOpt::edge(),
            )?;
        }

        let listener = TcpListener::bind(bind_addr)?;
        poll.register(
            &listener,
            self.sockets.add(SocketKind::Listener),
            Ready::readable(),
            PollOpt::edge(),
        )?;

        loop {
            let mut events = Events::with_capacity(128);
            poll.poll(&mut events, None)?;

            for ev in events {
                self.handle_poll_event(&poll, &listener, ev.token())?;
            }
        }
    }
}

fn main() -> Result<()> {
    let config = &NetworkConfig {
        network_name: "freenode".to_string(),
        nick: "ep".to_string(),
        socket: IrcSocketConfig {
            addr: "irc.freenode.net:6667".parse().unwrap(),
            max_retries: Some(3),
        },
    };

    let mut server = BirchServer::new();
    server.add_network(config)?;

    server.serve(&"127.0.0.1:9123".parse().unwrap())?;

    Ok(())
}
<|endoftext|>


<commit_before>// force-host
// no-prefer-dynamic

#![crate_type = "proc-macro"]

extern crate proc_macro;

use proc_macro::TokenStream;

#[proc_macro_derive(Unstable)]
pub fn derive(_input: TokenStream) -> TokenStream {

    "unsafe fn foo() -> u32 { ::std::intrinsics::init() }".parse().unwrap()
}
<commit_msg>fix expand-to-unstable test<commit_after>// force-host
// no-prefer-dynamic

#![crate_type = "proc-macro"]

extern crate proc_macro;

use proc_macro::TokenStream;

#[proc_macro_derive(Unstable)]
pub fn derive(_input: TokenStream) -> TokenStream {

    "unsafe fn foo() -> u32 { ::std::intrinsics::abort() }".parse().unwrap()
}
<|endoftext|>


<commit_before>
pub use wtools::error::*;
use std::collections::HashMap;

///
/// Instruction.
///

#[ derive( Debug, PartialEq ) ]
pub struct Instruction
{
  /// Error of parsing an instruction.
  pub err : Option< Error >,
  /// Command name.
  pub command_name : Box< str >,
  /// Subject of command.
  pub subject : Vec< Box< str > >,
  /// Properties of command.
  pub properties_map : HashMap< Box< str >, Box< str > >,
}

impl Instruction
{
  fn new() -> Self
  {
    Self
    {
      err : None,
      command_name : Default::default(),
      subject : Default::default(),
      properties_map : Default::default(),

    }
  }
}

//

///
/// Adapter for instruction.
///

pub trait InstructionParseParamsAdapter
{

  /// Print info about command format.
  fn about_command_format( &self ) -> &'static str
  {
r#"Command should start from a dot `.`.
Command can have a subject and properties.
Property is pair delimited by colon `:`.
For example: `.struct1 subject key1:val key2:val2`."#
  }

  /// Check that command begins with dot.
  fn instruction_split_is_command< Src : AsRef< str > >( &self, src : Src ) -> bool
  {
    src.as_ref().starts_with( "." )
  }

  /// Normalize command name.
  fn command_name_normalize< Src : AsRef< str > >( &self, src : Src ) -> Box< str >
  {
    let splits : Vec< &str > = src.as_ref()
    .split_whitespace()
    .flat_map( | e | e.split( "." ) )
    .filter( | e | e != &"" )
    .collect();
    ( ".".to_string() + &splits.join( "." ) ).to_string().into_boxed_str()
  }

  /// Make properties map.
  fn split_belong_to_properties< Src : AsRef< str > >( &self, src : Src ) -> i32
  {
    let src = src.as_ref();
    if !src.contains( ':' )
    {
      return 0;
    }
    let splits : Vec< &str > = src
    .split_ascii_whitespace()
    .flat_map( | e | e.split( ":" ) )
    .filter( | e | e != &"" )
    .collect();
    let index = splits.iter().position( | e | *e == ":" ).unwrap();
    if index == 0
    {
      return 2;
    }
    return 1;
  }

  /// Parse instruction from splits.
  /* zzz : make it accept also vector */
  fn parse_from_splits< I >( &self, mut splits : I ) -> Instruction
  where
    < I as Iterator >::Item : core::fmt::Display,
    < I as Iterator >::Item : AsRef< str >,
    I : core::iter::Iterator,
  {
    let mut result = Instruction::new();

    // splits.for_each( | arg | println!( "{}", arg ) );

    let command_name = splits.next();

    if command_name.is_none()
    {
      result.err = Some( err!( "Lack of arguments" ) );
      return result;
    }

    let command_name = command_name.unwrap();

    if !self.instruction_split_is_command( &command_name )
    {
      result.err = Some( err!( "{}\nDoes not start as command\n{}", command_name, self.about_command_format() ) );
      return result;
    }

    result.command_name = self.command_name_normalize( command_name );

    // let params_splits;

    while let Some( split ) = splits.next()
    {
      let split_unwrap = split.as_ref();
      let belong = self.split_belong_to_properties( split_unwrap );
      if belong > 0
      {
        // if belong == 1
        {
          let props_splits = std::iter::once( split ).chain( splits );
          result.properties_map = super::props::parse_from_splits( props_splits );
        }
        break;
      }
      result.subject.push( split_unwrap.to_string().into_boxed_str() );
      // params_splits.chain();
    }

    // dbg!(  );

    // super::params::parse_from_splits(  );

    result
  }

//   //
//
//   fn str_structure_parse()
//   {
//
//   }

}

///
/// Parameters of instruction.
///

#[ derive( Debug, PartialEq ) ]
pub struct InstructionParseParams
{
}

impl InstructionParseParams
{
  /// Create new instruction parameters.
  pub fn new() -> Self
  {
    Self
    {
    }
  }
}

impl InstructionParseParamsAdapter for InstructionParseParams
{
}

//

///
/// Parse input as instruction from splits.
///

pub fn parse_from_splits< I >( splits : I ) -> Instruction
where
  < I as Iterator >::Item : core::fmt::Display,
  < I as Iterator >::Item : AsRef< str >,
  I : core::iter::Iterator,
{
  let params = InstructionParseParams::new();
  let instruction = params.parse_from_splits( splits );
  instruction
}

//

// var command = commandIdentitySet.command = Object.create( null );
// command.subjectHint = 'A name of identity.';
// command.hint = 'Modify an existed identity.';
// command.longHint = 'Much longer description.';
// command.properties =
// {
//   'login' : 'An identity login ( user name ) that is used for all identity scripts if no specifique login defined.',
//   'email' : 'An email that is used for all identity scripts if no specifique email defined.',
// };
<commit_msg>Fix module `wcensor`, use valid type<commit_after>
pub use wtools::error::*;
use std::collections::HashMap;

///
/// Instruction.
///

#[ derive( Debug, PartialEq ) ]
pub struct Instruction
{
  /// Error of parsing an instruction.
  pub err : Option< BasicError >,
  /// Command name.
  pub command_name : Box< str >,
  /// Subject of command.
  pub subject : Vec< Box< str > >,
  /// Properties of command.
  pub properties_map : HashMap< Box< str >, Box< str > >,
}

impl Instruction
{
  fn new() -> Self
  {
    Self
    {
      err : None,
      command_name : Default::default(),
      subject : Default::default(),
      properties_map : Default::default(),

    }
  }
}

//

///
/// Adapter for instruction.
///

pub trait InstructionParseParamsAdapter
{

  /// Print info about command format.
  fn about_command_format( &self ) -> &'static str
  {
r#"Command should start from a dot `.`.
Command can have a subject and properties.
Property is pair delimited by colon `:`.
For example: `.struct1 subject key1:val key2:val2`."#
  }

  /// Check that command begins with dot.
  fn instruction_split_is_command< Src : AsRef< str > >( &self, src : Src ) -> bool
  {
    src.as_ref().starts_with( "." )
  }

  /// Normalize command name.
  fn command_name_normalize< Src : AsRef< str > >( &self, src : Src ) -> Box< str >
  {
    let splits : Vec< &str > = src.as_ref()
    .split_whitespace()
    .flat_map( | e | e.split( "." ) )
    .filter( | e | e != &"" )
    .collect();
    ( ".".to_string() + &splits.join( "." ) ).to_string().into_boxed_str()
  }

  /// Make properties map.
  fn split_belong_to_properties< Src : AsRef< str > >( &self, src : Src ) -> i32
  {
    let src = src.as_ref();
    if !src.contains( ':' )
    {
      return 0;
    }
    let splits : Vec< &str > = src
    .split_ascii_whitespace()
    .flat_map( | e | e.split( ":" ) )
    .filter( | e | e != &"" )
    .collect();
    let index = splits.iter().position( | e | *e == ":" ).unwrap();
    if index == 0
    {
      return 2;
    }
    return 1;
  }

  /// Parse instruction from splits.
  /* zzz : make it accept also vector */
  fn parse_from_splits< I >( &self, mut splits : I ) -> Instruction
  where
    < I as Iterator >::Item : core::fmt::Display,
    < I as Iterator >::Item : AsRef< str >,
    I : core::iter::Iterator,
  {
    let mut result = Instruction::new();

    // splits.for_each( | arg | println!( "{}", arg ) );

    let command_name = splits.next();

    if command_name.is_none()
    {
      result.err = Some( err!( "Lack of arguments" ) );
      return result;
    }

    let command_name = command_name.unwrap();

    if !self.instruction_split_is_command( &command_name )
    {
      result.err = Some( err!( "{}\nDoes not start as command\n{}", command_name, self.about_command_format() ) );
      return result;
    }

    result.command_name = self.command_name_normalize( command_name );

    // let params_splits;

    while let Some( split ) = splits.next()
    {
      let split_unwrap = split.as_ref();
      let belong = self.split_belong_to_properties( split_unwrap );
      if belong > 0
      {
        // if belong == 1
        {
          let props_splits = std::iter::once( split ).chain( splits );
          result.properties_map = super::props::parse_from_splits( props_splits );
        }
        break;
      }
      result.subject.push( split_unwrap.to_string().into_boxed_str() );
      // params_splits.chain();
    }

    // dbg!(  );

    // super::params::parse_from_splits(  );

    result
  }

//   //
//
//   fn str_structure_parse()
//   {
//
//   }

}

///
/// Parameters of instruction.
///

#[ derive( Debug, PartialEq ) ]
pub struct InstructionParseParams
{
}

impl InstructionParseParams
{
  /// Create new instruction parameters.
  pub fn new() -> Self
  {
    Self
    {
    }
  }
}

impl InstructionParseParamsAdapter for InstructionParseParams
{
}

//

///
/// Parse input as instruction from splits.
///

pub fn parse_from_splits< I >( splits : I ) -> Instruction
where
  < I as Iterator >::Item : core::fmt::Display,
  < I as Iterator >::Item : AsRef< str >,
  I : core::iter::Iterator,
{
  let params = InstructionParseParams::new();
  let instruction = params.parse_from_splits( splits );
  instruction
}

//

// var command = commandIdentitySet.command = Object.create( null );
// command.subjectHint = 'A name of identity.';
// command.hint = 'Modify an existed identity.';
// command.longHint = 'Much longer description.';
// command.properties =
// {
//   'login' : 'An identity login ( user name ) that is used for all identity scripts if no specifique login defined.',
//   'email' : 'An email that is used for all identity scripts if no specifique email defined.',
// };
<|endoftext|>


<commit_before>use internal::prelude::*;
use parking_lot::Mutex;
use std::collections::{HashMap, VecDeque};
use std::sync::mpsc::{self, Sender};
use std::sync::Arc;
use std::thread;
use super::super::super::EventHandler;
use super::{
    ShardClientMessage,
    ShardId,
    ShardManagerMessage,
    ShardManagerMonitor,
    ShardQueuer,
    ShardQueuerMessage,
    ShardRunnerInfo,
};
use threadpool::ThreadPool;
use typemap::ShareMap;

#[cfg(feature = "framework")]
use framework::Framework;

/// A manager for handling the status of shards by starting them, restarting
/// them, and stopping them when required.
///
/// **Note**: The [`Client`] internally uses a shard manager. If you are using a
/// Client, then you do not need to make one of these.
///
/// # Examples
///
/// Initialize a shard manager with a framework responsible for shards 0 through
/// 2, of 5 total shards:
///
/// ```rust,no_run
/// extern crate parking_lot;
/// extern crate serenity;
/// extern crate threadpool;
/// extern crate typemap;
///
/// # use std::error::Error;
/// #
/// # #[cfg(feature = "framework")]
/// # fn try_main() -> Result<(), Box<Error>> {
/// #
/// use parking_lot::Mutex;
/// use serenity::client::bridge::gateway::ShardManager;
/// use serenity::client::EventHandler;
/// use serenity::http;
/// use std::sync::Arc;
/// use std::env;
/// use threadpool::ThreadPool;
/// use typemap::ShareMap;
///
/// struct Handler;
///
/// impl EventHandler for Handler { }
///
/// let token = env::var("DISCORD_TOKEN")?;
/// http::set_token(&token);
/// let token = Arc::new(Mutex::new(token));
///
/// let gateway_url = Arc::new(Mutex::new(http::get_gateway()?.url));
/// let data = Arc::new(Mutex::new(ShareMap::custom()));
/// let event_handler = Arc::new(Handler);
/// let framework = Arc::new(Mutex::new(None));
/// let threadpool = ThreadPool::with_name("my threadpool".to_owned(), 5);
///
/// ShardManager::new(
///     0, // the shard index to start initiating from
///     3, // the number of shards to initiate (this initiates 0, 1, and 2)
///     5, // the total number of shards in use
///     &gateway_url,
///     &token,
///     &data,
///     &event_handler,
///     &framework,
///     threadpool,
/// );
/// #     Ok(())
/// # }
/// #
/// # #[cfg(not(feature = "framework"))]
/// # fn try_main() -> Result<(), Box<Error>> {
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// ```
///
/// [`Client`]: ../../struct.Client.html
#[derive(Debug)]
pub struct ShardManager {
    monitor_tx: Sender<ShardManagerMessage>,
    /// The shard runners currently managed.
    ///
    /// **Note**: It is highly unrecommended to mutate this yourself unless you
    /// need to. Instead prefer to use methods on this struct that are provided
    /// where possible.
    pub runners: Arc<Mutex<HashMap<ShardId, ShardRunnerInfo>>>,
    /// The index of the first shard to initialize, 0-indexed.
    shard_index: u64,
    /// The number of shards to initialize.
    shard_init: u64,
    /// The total shards in use, 1-indexed.
    shard_total: u64,
    shard_queuer: Sender<ShardQueuerMessage>,
}

impl ShardManager {
    /// Creates a new shard manager, returning both the manager and a monitor
    /// for usage in a separate thread.
    pub fn new<'a, H>(
        opt: ShardManagerOptions<'a, H>,
    ) -> (Arc<Mutex<Self>>, ShardManagerMonitor) where H: EventHandler + Send + Sync + 'static {
        let (thread_tx, thread_rx) = mpsc::channel();
        let (shard_queue_tx, shard_queue_rx) = mpsc::channel();

        let runners = Arc::new(Mutex::new(HashMap::new()));

        let mut shard_queuer = ShardQueuer {
            data: Arc::clone(opt.data),
            event_handler: Arc::clone(opt.event_handler),
            #[cfg(feature = "framework")]
            framework: Arc::clone(opt.framework),
            last_start: None,
            manager_tx: thread_tx.clone(),
            queue: VecDeque::new(),
            runners: Arc::clone(&runners),
            rx: shard_queue_rx,
            threadpool: opt.threadpool,
            token: Arc::clone(opt.token),
            ws_url: Arc::clone(opt.ws_url),
        };

        thread::spawn(move || {
            shard_queuer.run();
        });

        let manager = Arc::new(Mutex::new(Self {
            monitor_tx: thread_tx,
            shard_index: opt.shard_index,
            shard_init: opt.shard_init,
            shard_queuer: shard_queue_tx,
            shard_total: opt.shard_total,
            runners,
        }));

        (Arc::clone(&manager), ShardManagerMonitor {
            rx: thread_rx,
            manager,
        })
    }

    /// Returns whether the shard manager contains either an active instance of
    /// a shard runner responsible for the given ID.
    ///
    /// If a shard has been queued but has not yet been initiated, then this
    /// will return `false`. Consider double-checking [`is_responsible_for`] to
    /// determine whether this shard manager is responsible for the given shard.
    ///
    /// [`is_responsible_for`]: #method.is_responsible_for
    pub fn has(&self, shard_id: ShardId) -> bool {
        self.runners.lock().contains_key(&shard_id)
    }

    /// Initializes all shards that the manager is responsible for.
    ///
    /// This will communicate shard boots with the [`ShardQueuer`] so that they
    /// are properly queued.
    ///
    /// [`ShardQueuer`]: struct.ShardQueuer.html
    pub fn initialize(&mut self) -> Result<()> {
        let shard_to = self.shard_index + self.shard_init;

        for shard_id in self.shard_index..shard_to {
            let shard_total = self.shard_total;

            self.boot([ShardId(shard_id), ShardId(shard_total)]);
        }

        Ok(())
    }

    /// Sets the new sharding information for the manager.
    ///
    /// This will shutdown all existing shards.
    ///
    /// This will _not_ instantiate the new shards.
    pub fn set_shards(&mut self, index: u64, init: u64, total: u64) {
        self.shutdown_all();

        self.shard_index = index;
        self.shard_init = init;
        self.shard_total = total;
    }

    /// Restarts a shard runner.
    ///
    /// This sends a shutdown signal to a shard's associated [`ShardRunner`],
    /// and then queues a initialization of a shard runner for the same shard
    /// via the [`ShardQueuer`].
    ///
    /// # Examples
    ///
    /// Creating a client and then restarting a shard by ID:
    ///
    /// _(note: in reality this precise code doesn't have an effect since the
    /// shard would not yet have been initialized via [`initialize`], but the
    /// concept is the same)_
    ///
    /// ```rust,no_run
    /// use serenity::client::bridge::gateway::ShardId;
    /// use serenity::client::{Client, EventHandler};
    /// use std::env;
    ///
    /// struct Handler;
    ///
    /// impl EventHandler for Handler { }
    ///
    /// let token = env::var("DISCORD_TOKEN").unwrap();
    /// let mut client = Client::new(&token, Handler).unwrap();
    ///
    /// // restart shard ID 7
    /// client.shard_manager.lock().restart(ShardId(7));
    /// ```
    ///
    /// [`ShardQueuer`]: struct.ShardQueuer.html
    /// [`ShardRunner`]: struct.ShardRunner.html
    pub fn restart(&mut self, shard_id: ShardId) {
        info!("Restarting shard {}", shard_id);
        self.shutdown(shard_id);

        let shard_total = self.shard_total;

        self.boot([shard_id, ShardId(shard_total)]);
    }

    /// Returns the [`ShardId`]s of the shards that have been instantiated and
    /// currently have a valid [`ShardRunner`].
    ///
    /// [`ShardId`]: struct.ShardId.html
    /// [`ShardRunner`]: struct.ShardRunner.html
    pub fn shards_instantiated(&self) -> Vec<ShardId> {
        self.runners.lock().keys().cloned().collect()
    }

    /// Attempts to shut down the shard runner by Id.
    ///
    /// Returns a boolean indicating whether a shard runner was present. This is
    /// _not_ necessary an indicator of whether the shard runner was
    /// successfully shut down.
    ///
    /// **Note**: If the receiving end of an mpsc channel - theoretically owned
    /// by the shard runner - no longer exists, then the shard runner will not
    /// know it should shut down. This _should never happen_. It may already be
    /// stopped.
    pub fn shutdown(&mut self, shard_id: ShardId) -> bool {
        info!("Shutting down shard {}", shard_id);

        if let Some(runner) = self.runners.lock().get(&shard_id) {
            let shutdown = ShardManagerMessage::Shutdown(shard_id);
            let msg = ShardClientMessage::Manager(shutdown);

            if let Err(why) = runner.runner_tx.send(msg) {
                warn!(
                    "Failed to cleanly shutdown shard {}: {:?}",
                    shard_id,
                    why,
                );
            }
        }

        self.runners.lock().remove(&shard_id).is_some()
    }

    /// Sends a shutdown message for all shards that the manager is responsible
    /// for that are still known to be running.
    ///
    /// If you only need to shutdown a select number of shards, prefer looping
    /// over the [`shutdown`] method.
    ///
    /// [`shutdown`]: #method.shutdown
    pub fn shutdown_all(&mut self) {
        let keys = {
            let runners = self.runners.lock();

            if runners.is_empty() {
                return;
            }

            runners.keys().cloned().collect::<Vec<_>>()
        };

        info!("Shutting down all shards");

        for shard_id in keys {
            self.shutdown(shard_id);
        }

        let _ = self.shard_queuer.send(ShardQueuerMessage::Shutdown);
        let _ = self.monitor_tx.send(ShardManagerMessage::ShutdownInitiated);
    }

    fn boot(&mut self, shard_info: [ShardId; 2]) {
        info!("Telling shard queuer to start shard {}", shard_info[0]);

        let msg = ShardQueuerMessage::Start(shard_info[0], shard_info[1]);
        let _ = self.shard_queuer.send(msg);
    }
}

impl Drop for ShardManager {
    /// A custom drop implementation to clean up after the manager.
    ///
    /// This shuts down all active [`ShardRunner`]s and attempts to tell the
    /// [`ShardQueuer`] to shutdown.
    ///
    /// [`ShardQueuer`]: struct.ShardQueuer.html
    /// [`ShardRunner`]: struct.ShardRunner.html
    fn drop(&mut self) {
        self.shutdown_all();

        if let Err(why) = self.shard_queuer.send(ShardQueuerMessage::Shutdown) {
            warn!("Failed to send shutdown to shard queuer: {:?}", why);
        }
    }
}

pub struct ShardManagerOptions<'a, H: EventHandler + Send + Sync + 'static> {
    pub data: &'a Arc<Mutex<ShareMap>>,
    pub event_handler: &'a Arc<H>,
    #[cfg(feature = "framework")]
    pub framework: &'a Arc<Mutex<Option<Box<Framework + Send>>>>,
    pub shard_index: u64,
    pub shard_init: u64,
    pub shard_total: u64,
    pub threadpool: ThreadPool,
    pub token: &'a Arc<Mutex<String>>,
    pub ws_url: &'a Arc<Mutex<String>>,
}
<commit_msg>Fix ShardManager doctest<commit_after>use internal::prelude::*;
use parking_lot::Mutex;
use std::collections::{HashMap, VecDeque};
use std::sync::mpsc::{self, Sender};
use std::sync::Arc;
use std::thread;
use super::super::super::EventHandler;
use super::{
    ShardClientMessage,
    ShardId,
    ShardManagerMessage,
    ShardManagerMonitor,
    ShardQueuer,
    ShardQueuerMessage,
    ShardRunnerInfo,
};
use threadpool::ThreadPool;
use typemap::ShareMap;

#[cfg(feature = "framework")]
use framework::Framework;

/// A manager for handling the status of shards by starting them, restarting
/// them, and stopping them when required.
///
/// **Note**: The [`Client`] internally uses a shard manager. If you are using a
/// Client, then you do not need to make one of these.
///
/// # Examples
///
/// Initialize a shard manager with a framework responsible for shards 0 through
/// 2, of 5 total shards:
///
/// ```rust,no_run
/// extern crate parking_lot;
/// extern crate serenity;
/// extern crate threadpool;
/// extern crate typemap;
///
/// # use std::error::Error;
/// #
/// # #[cfg(feature = "framework")]
/// # fn try_main() -> Result<(), Box<Error>> {
/// #
/// use parking_lot::Mutex;
/// use serenity::client::bridge::gateway::{ShardManager, ShardManagerOptions};
/// use serenity::client::EventHandler;
/// use serenity::http;
/// use std::sync::Arc;
/// use std::env;
/// use threadpool::ThreadPool;
/// use typemap::ShareMap;
///
/// struct Handler;
///
/// impl EventHandler for Handler { }
///
/// let token = env::var("DISCORD_TOKEN")?;
/// http::set_token(&token);
/// let token = Arc::new(Mutex::new(token));
///
/// let gateway_url = Arc::new(Mutex::new(http::get_gateway()?.url));
/// let data = Arc::new(Mutex::new(ShareMap::custom()));
/// let event_handler = Arc::new(Handler);
/// let framework = Arc::new(Mutex::new(None));
/// let threadpool = ThreadPool::with_name("my threadpool".to_owned(), 5);
///
/// ShardManager::new(ShardManagerOptions {
///     data: &data,
///     event_handler: &event_handler,
///     framework: &framework,
///     // the shard index to start initiating from
///     shard_index: 0,
///     // the number of shards to initiate (this initiates 0, 1, and 2)
///     shard_init: 3,
///     // the total number of shards in use
///     shard_total: 5,
///     token: &token,
///     threadpool,
///     ws_url: &gateway_url,
/// });
/// #     Ok(())
/// # }
/// #
/// # #[cfg(not(feature = "framework"))]
/// # fn try_main() -> Result<(), Box<Error>> {
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// ```
///
/// [`Client`]: ../../struct.Client.html
#[derive(Debug)]
pub struct ShardManager {
    monitor_tx: Sender<ShardManagerMessage>,
    /// The shard runners currently managed.
    ///
    /// **Note**: It is highly unrecommended to mutate this yourself unless you
    /// need to. Instead prefer to use methods on this struct that are provided
    /// where possible.
    pub runners: Arc<Mutex<HashMap<ShardId, ShardRunnerInfo>>>,
    /// The index of the first shard to initialize, 0-indexed.
    shard_index: u64,
    /// The number of shards to initialize.
    shard_init: u64,
    /// The total shards in use, 1-indexed.
    shard_total: u64,
    shard_queuer: Sender<ShardQueuerMessage>,
}

impl ShardManager {
    /// Creates a new shard manager, returning both the manager and a monitor
    /// for usage in a separate thread.
    pub fn new<'a, H>(
        opt: ShardManagerOptions<'a, H>,
    ) -> (Arc<Mutex<Self>>, ShardManagerMonitor) where H: EventHandler + Send + Sync + 'static {
        let (thread_tx, thread_rx) = mpsc::channel();
        let (shard_queue_tx, shard_queue_rx) = mpsc::channel();

        let runners = Arc::new(Mutex::new(HashMap::new()));

        let mut shard_queuer = ShardQueuer {
            data: Arc::clone(opt.data),
            event_handler: Arc::clone(opt.event_handler),
            #[cfg(feature = "framework")]
            framework: Arc::clone(opt.framework),
            last_start: None,
            manager_tx: thread_tx.clone(),
            queue: VecDeque::new(),
            runners: Arc::clone(&runners),
            rx: shard_queue_rx,
            threadpool: opt.threadpool,
            token: Arc::clone(opt.token),
            ws_url: Arc::clone(opt.ws_url),
        };

        thread::spawn(move || {
            shard_queuer.run();
        });

        let manager = Arc::new(Mutex::new(Self {
            monitor_tx: thread_tx,
            shard_index: opt.shard_index,
            shard_init: opt.shard_init,
            shard_queuer: shard_queue_tx,
            shard_total: opt.shard_total,
            runners,
        }));

        (Arc::clone(&manager), ShardManagerMonitor {
            rx: thread_rx,
            manager,
        })
    }

    /// Returns whether the shard manager contains either an active instance of
    /// a shard runner responsible for the given ID.
    ///
    /// If a shard has been queued but has not yet been initiated, then this
    /// will return `false`. Consider double-checking [`is_responsible_for`] to
    /// determine whether this shard manager is responsible for the given shard.
    ///
    /// [`is_responsible_for`]: #method.is_responsible_for
    pub fn has(&self, shard_id: ShardId) -> bool {
        self.runners.lock().contains_key(&shard_id)
    }

    /// Initializes all shards that the manager is responsible for.
    ///
    /// This will communicate shard boots with the [`ShardQueuer`] so that they
    /// are properly queued.
    ///
    /// [`ShardQueuer`]: struct.ShardQueuer.html
    pub fn initialize(&mut self) -> Result<()> {
        let shard_to = self.shard_index + self.shard_init;

        for shard_id in self.shard_index..shard_to {
            let shard_total = self.shard_total;

            self.boot([ShardId(shard_id), ShardId(shard_total)]);
        }

        Ok(())
    }

    /// Sets the new sharding information for the manager.
    ///
    /// This will shutdown all existing shards.
    ///
    /// This will _not_ instantiate the new shards.
    pub fn set_shards(&mut self, index: u64, init: u64, total: u64) {
        self.shutdown_all();

        self.shard_index = index;
        self.shard_init = init;
        self.shard_total = total;
    }

    /// Restarts a shard runner.
    ///
    /// This sends a shutdown signal to a shard's associated [`ShardRunner`],
    /// and then queues a initialization of a shard runner for the same shard
    /// via the [`ShardQueuer`].
    ///
    /// # Examples
    ///
    /// Creating a client and then restarting a shard by ID:
    ///
    /// _(note: in reality this precise code doesn't have an effect since the
    /// shard would not yet have been initialized via [`initialize`], but the
    /// concept is the same)_
    ///
    /// ```rust,no_run
    /// use serenity::client::bridge::gateway::ShardId;
    /// use serenity::client::{Client, EventHandler};
    /// use std::env;
    ///
    /// struct Handler;
    ///
    /// impl EventHandler for Handler { }
    ///
    /// let token = env::var("DISCORD_TOKEN").unwrap();
    /// let mut client = Client::new(&token, Handler).unwrap();
    ///
    /// // restart shard ID 7
    /// client.shard_manager.lock().restart(ShardId(7));
    /// ```
    ///
    /// [`ShardQueuer`]: struct.ShardQueuer.html
    /// [`ShardRunner`]: struct.ShardRunner.html
    pub fn restart(&mut self, shard_id: ShardId) {
        info!("Restarting shard {}", shard_id);
        self.shutdown(shard_id);

        let shard_total = self.shard_total;

        self.boot([shard_id, ShardId(shard_total)]);
    }

    /// Returns the [`ShardId`]s of the shards that have been instantiated and
    /// currently have a valid [`ShardRunner`].
    ///
    /// [`ShardId`]: struct.ShardId.html
    /// [`ShardRunner`]: struct.ShardRunner.html
    pub fn shards_instantiated(&self) -> Vec<ShardId> {
        self.runners.lock().keys().cloned().collect()
    }

    /// Attempts to shut down the shard runner by Id.
    ///
    /// Returns a boolean indicating whether a shard runner was present. This is
    /// _not_ necessary an indicator of whether the shard runner was
    /// successfully shut down.
    ///
    /// **Note**: If the receiving end of an mpsc channel - theoretically owned
    /// by the shard runner - no longer exists, then the shard runner will not
    /// know it should shut down. This _should never happen_. It may already be
    /// stopped.
    pub fn shutdown(&mut self, shard_id: ShardId) -> bool {
        info!("Shutting down shard {}", shard_id);

        if let Some(runner) = self.runners.lock().get(&shard_id) {
            let shutdown = ShardManagerMessage::Shutdown(shard_id);
            let msg = ShardClientMessage::Manager(shutdown);

            if let Err(why) = runner.runner_tx.send(msg) {
                warn!(
                    "Failed to cleanly shutdown shard {}: {:?}",
                    shard_id,
                    why,
                );
            }
        }

        self.runners.lock().remove(&shard_id).is_some()
    }

    /// Sends a shutdown message for all shards that the manager is responsible
    /// for that are still known to be running.
    ///
    /// If you only need to shutdown a select number of shards, prefer looping
    /// over the [`shutdown`] method.
    ///
    /// [`shutdown`]: #method.shutdown
    pub fn shutdown_all(&mut self) {
        let keys = {
            let runners = self.runners.lock();

            if runners.is_empty() {
                return;
            }

            runners.keys().cloned().collect::<Vec<_>>()
        };

        info!("Shutting down all shards");

        for shard_id in keys {
            self.shutdown(shard_id);
        }

        let _ = self.shard_queuer.send(ShardQueuerMessage::Shutdown);
        let _ = self.monitor_tx.send(ShardManagerMessage::ShutdownInitiated);
    }

    fn boot(&mut self, shard_info: [ShardId; 2]) {
        info!("Telling shard queuer to start shard {}", shard_info[0]);

        let msg = ShardQueuerMessage::Start(shard_info[0], shard_info[1]);
        let _ = self.shard_queuer.send(msg);
    }
}

impl Drop for ShardManager {
    /// A custom drop implementation to clean up after the manager.
    ///
    /// This shuts down all active [`ShardRunner`]s and attempts to tell the
    /// [`ShardQueuer`] to shutdown.
    ///
    /// [`ShardQueuer`]: struct.ShardQueuer.html
    /// [`ShardRunner`]: struct.ShardRunner.html
    fn drop(&mut self) {
        self.shutdown_all();

        if let Err(why) = self.shard_queuer.send(ShardQueuerMessage::Shutdown) {
            warn!("Failed to send shutdown to shard queuer: {:?}", why);
        }
    }
}

pub struct ShardManagerOptions<'a, H: EventHandler + Send + Sync + 'static> {
    pub data: &'a Arc<Mutex<ShareMap>>,
    pub event_handler: &'a Arc<H>,
    #[cfg(feature = "framework")]
    pub framework: &'a Arc<Mutex<Option<Box<Framework + Send>>>>,
    pub shard_index: u64,
    pub shard_init: u64,
    pub shard_total: u64,
    pub threadpool: ThreadPool,
    pub token: &'a Arc<Mutex<String>>,
    pub ws_url: &'a Arc<Mutex<String>>,
}
<|endoftext|>


<commit_before>// exec-env:RUST_POISON_ON_FREE=1

fn main() {
    let mut x = @mut @{f: ~3};
    match x {
      @@{f: b_x} => {
        assert *b_x == 3;
        assert ptr::addr_of(&(x.f)) == ptr::addr_of(&(b_x));

        *x = @{f: ~4};

        debug!("ptr::addr_of(*b_x) = %x", ptr::addr_of(&(*b_x)) as uint);
        assert *b_x == 3;
        assert ptr::addr_of(&(*x.f)) != ptr::addr_of(&(*b_x));
      }
    }
}
<commit_msg>test: Fix borrowck-preserve-box-in-pat. rs=bustage<commit_after>// exec-env:RUST_POISON_ON_FREE=1

fn main() {
    let mut x = @mut @{f: ~3};
    match x {
      @@{f: ref b_x} => {
        assert **b_x == 3;
        assert ptr::addr_of(&(x.f)) == ptr::addr_of(b_x);

        *x = @{f: ~4};

        debug!("ptr::addr_of(*b_x) = %x", ptr::addr_of(&(**b_x)) as uint);
        assert **b_x == 3;
        assert ptr::addr_of(&(*x.f)) != ptr::addr_of(&(**b_x));
      }
    }
}
<|endoftext|>


<commit_before>//! Simple basic model of the C-AST
//!
//! This is the skeleton of the final decompiled output. However, further stages maybe added to
//! make the decompiled output easier to read and add more sugaring.

use petgraph::graph::{Graph, NodeIndex};
use std::default;

pub type Ty = String;

#[derive(Clone, Debug)]
enum CASTNode {
    FunctionHeader,
    Conditional,
    Declaration(Ty),
    Loop,
    Goto,
    Break,
    ExpressionNode(Expr),
}

#[derive(Clone, Debug)]
pub enum Expr {
    Eq,
    Add,
    Sub,
    Mul,
    Div,
    Mod,
    Shr,
    Shl,
}

#[derive(Clone, Debug)]
enum ASTEdge {
    StatementOrd(u8),
    EdgeOrd(u8),
}

#[derive(Clone, Debug)]
pub struct CAST {
    ast: Graph<CASTNode, ASTEdge>,
}

impl default::Default for CAST {
    fn default() -> CAST {
        CAST {
            ast: Graph::new(),
        }
    }
}

impl CAST {
    pub fn expr(operator: Expr, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn new_loop(loop_header: NodeIndex, loop_body: NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn new_conditional(condition: NodeIndex, body: NodeIndex, else_condition: NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn goto(label: String) -> NodeIndex {
        unimplemented!()
    }

    pub fn insert_break(after: NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn print() -> String {
        unimplemented!()
    }
}

#[cfg(test)]
mod test {

    #[test]
    fn c_ast_basic_test() {
        unimplemented!()
    }
}
<commit_msg>Implements more Simple C AST<commit_after>//! Simple basic model of the C-AST
//!
//! This is the skeleton of the final decompiled output. However, further stages maybe added to
//! make the decompiled output easier to read and add more sugaring.

use petgraph::graph::{Graph, NodeIndex};
use std::default;

pub type Ty = String;

#[derive(Clone, Debug)]
enum CASTNode {
    FunctionHeader,
    Conditional,
    Declaration(Ty),
    Loop,
    Goto(String),
    Label(String),
    Break,
    ExpressionNode(Expr),
    Var(String),
}

#[derive(Clone, Debug)]
pub enum Expr {
    Eq,
    Add,
    Sub,
    Mul,
    Div,
    Mod,
    Shr,
    Shl,
}

#[derive(Clone, Debug)]
enum ASTEdge {
    StatementOrd(u64),
    OpOrd(u8),
}

#[derive(Clone, Debug)]
pub struct CAST {
    ast: Graph<CASTNode, ASTEdge>,
    eidx: u64,
}

impl default::Default for CAST {
    fn default() -> CAST {
        let mut ast = CAST {
            ast: Graph::new(),
            eidx: 0,
        };
        ast.ast.add_node(CASTNode::FunctionHeader);
        ast
    }
}

lazy_static! {
    static ref FN_HEAD: NodeIndex = NodeIndex::new(0);
}

impl CAST {
    fn next_edge_idx(&mut self) -> u64 {
        self.eidx += 1;
        self.eidx - 1
    }

    fn replace_incoming_edges(node: NodeIndex, by: NodeIndex) {
        unimplemented!()
    }

    pub fn expr(&mut self, operator: Expr, operands: &[NodeIndex]) -> NodeIndex {
        let operator = self.ast.add_node(CASTNode::ExpressionNode(operator));
        let idx = self.next_edge_idx();
        self.ast.add_edge(*FN_HEAD, operator, ASTEdge::StatementOrd(idx));
        for (i, n) in operands.iter().enumerate() {
            self.ast.add_edge(operator, *n, ASTEdge::OpOrd(i as u8));
        }
        operator
    }

    pub fn new_loop(&mut self, loop_header: NodeIndex, loop_body: NodeIndex) -> NodeIndex {
        let e1 = self.ast.find_edge(*FN_HEAD, loop_header).expect("This cannot be `None`");
        let e2 = self.ast.find_edge(*FN_HEAD, loop_body).expect("This cannot be `None`");
        let loop_h = self.ast.add_node(CASTNode::Loop);
        let idx = if let ASTEdge::StatementOrd(idx) = self.ast[e1] {
            idx
        } else {
            panic!()
        };
        self.ast.remove_edge(e2);
        self.ast.remove_edge(e1);
        self.ast.add_edge(*FN_HEAD, loop_h, ASTEdge::StatementOrd(idx));
        self.ast.add_edge(loop_h, loop_header, ASTEdge::OpOrd(0));
        self.ast.add_edge(loop_h, loop_body, ASTEdge::OpOrd(1));
        loop_h
    }

    pub fn new_conditional(&mut self, condition: NodeIndex, body: NodeIndex, else_condition: Option<NodeIndex>) -> NodeIndex {
        let e1 = self.ast.find_edge(*FN_HEAD, condition).expect("This cannot be `None`");
        let e2 = self.ast.find_edge(*FN_HEAD, body).expect("This cannot be `None`");
        let e3 = if let Some(else_c) = else_condition {
            self.ast.find_edge(*FN_HEAD, else_c)
        } else {
            None
        };
        let idx = if let ASTEdge::StatementOrd(idx) = self.ast[e1] {
            idx
        } else {
            panic!()
        };

        let conditional = self.ast.add_node(CASTNode::Conditional);
        self.ast.remove_edge(e1);
        self.ast.remove_edge(e2);
        if let Some(e3) = e3 {
            self.ast.remove_edge(e3);
        }

        self.ast.add_edge(*FN_HEAD, conditional, ASTEdge::StatementOrd(idx));
        self.ast.add_edge(conditional, condition, ASTEdge::OpOrd(0));
        self.ast.add_edge(conditional, body, ASTEdge::OpOrd(1));
        if let Some(else_condition) = else_condition {
            self.ast.add_edge(conditional, else_condition, ASTEdge::OpOrd(2));
        }
        conditional
    }

    pub fn goto(&mut self, label: String) {
        let goto_n = self.ast.add_node(CASTNode::Goto(label));
        let idx = self.next_edge_idx();
        self.ast.add_edge(*FN_HEAD, goto_n, ASTEdge::StatementOrd(idx));
    }

    pub fn label(label: String) {
        let label = self.ast.add_node(CASTNode::Label(label));
        let idx = self.next_edge_idx();
        self.ast.add_edge(*FN_HEAD, label, ASTEdge::StatementOrd(idx));
    }

    pub fn insert_break(after: NodeIndex) {
        let break_n = self.ast.add_node(CASTNode::Label(label));
        let idx = self.next_edge_idx();
        self.ast.add_edge(*FN_HEAD, break_n, ASTEdge::StatementOrd(idx));
    }

    pub fn print() -> String {
        unimplemented!()
    }
}

#[cfg(test)]
mod test {

    #[test]
    fn c_ast_basic_test() {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>use crate::cli;
use crate::command_prelude::*;
use anyhow::{bail, format_err};
use cargo::core::dependency::DepKind;
use cargo::ops::tree::{self, EdgeKind};
use cargo::ops::Packages;
use cargo::util::print_available_packages;
use cargo::util::CargoResult;
use std::collections::HashSet;
use std::str::FromStr;

pub fn cli() -> App {
    subcommand("tree")
        .about("Display a tree visualization of a dependency graph")
        .arg(opt("quiet", "Suppress status messages").short("q"))
        .arg_manifest_path()
        .arg_package_spec_no_all(
            "Package to be used as the root of the tree",
            "Display the tree for all packages in the workspace",
            "Exclude specific workspace members",
        )
        // Deprecated, use --no-dedupe instead.
        .arg(Arg::with_name("all").long("all").short("a").hidden(true))
        // Deprecated, use --target=all instead.
        .arg(
            Arg::with_name("all-targets")
                .long("all-targets")
                .hidden(true),
        )
        .arg_features()
        .arg_target_triple(
            "Filter dependencies matching the given target-triple (default host platform). \
            Pass `all` to include all targets.",
        )
        // Deprecated, use -e=no-dev instead.
        .arg(
            Arg::with_name("no-dev-dependencies")
                .long("no-dev-dependencies")
                .hidden(true),
        )
        .arg(
            multi_opt(
                "edges",
                "KINDS",
                "The kinds of dependencies to display \
                 (features, normal, build, dev, all, \
                 no-normal, no-build, no-dev, no-proc-macro)",
            )
            .short("e"),
        )
        .arg(
            optional_multi_opt(
                "invert",
                "SPEC",
                "Invert the tree direction and focus on the given package",
            )
            .short("i"),
        )
        .arg(multi_opt(
            "prune",
            "SPEC",
            "Prune the given package from the display of the dependency tree",
        ))
        .arg(opt("depth", "Maximum display depth of the dependency tree").value_name("DEPTH"))
        // Deprecated, use --prefix=none instead.
        .arg(Arg::with_name("no-indent").long("no-indent").hidden(true))
        // Deprecated, use --prefix=depth instead.
        .arg(
            Arg::with_name("prefix-depth")
                .long("prefix-depth")
                .hidden(true),
        )
        .arg(
            opt(
                "prefix",
                "Change the prefix (indentation) of how each entry is displayed",
            )
            .value_name("PREFIX")
            .possible_values(&["depth", "indent", "none"])
            .default_value("indent"),
        )
        .arg(opt(
            "no-dedupe",
            "Do not de-duplicate (repeats all shared dependencies)",
        ))
        .arg(
            opt(
                "duplicates",
                "Show only dependencies which come in multiple versions (implies -i)",
            )
            .short("d")
            .alias("duplicate"),
        )
        .arg(
            opt("charset", "Character set to use in output: utf8, ascii")
                .value_name("CHARSET")
                .possible_values(&["utf8", "ascii"])
                .default_value("utf8"),
        )
        .arg(
            opt("format", "Format string used for printing dependencies")
                .value_name("FORMAT")
                .short("f")
                .default_value("{p}"),
        )
        .arg(
            // Backwards compatibility with old cargo-tree.
            Arg::with_name("version")
                .long("version")
                .short("V")
                .hidden(true),
        )
        .after_help("Run `cargo help tree` for more detailed information.\n")
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    if args.is_present("version") {
        let verbose = args.occurrences_of("verbose") > 0;
        let version = cli::get_version_string(verbose);
        cargo::drop_print!(config, "{}", version);
        return Ok(());
    }
    let prefix = if args.is_present("no-indent") {
        config
            .shell()
            .warn("the --no-indent flag has been changed to --prefix=none")?;
        "none"
    } else if args.is_present("prefix-depth") {
        config
            .shell()
            .warn("the --prefix-depth flag has been changed to --prefix=depth")?;
        "depth"
    } else {
        args.value_of("prefix").unwrap()
    };
    let prefix = tree::Prefix::from_str(prefix).map_err(|e| anyhow::anyhow!("{}", e))?;

    let no_dedupe = args.is_present("no-dedupe") || args.is_present("all");
    if args.is_present("all") {
        config.shell().warn(
            "The `cargo tree` --all flag has been changed to --no-dedupe, \
             and may be removed in a future version.\n\
             If you are looking to display all workspace members, use the --workspace flag.",
        )?;
    }

    let targets = if args.is_present("all-targets") {
        config
            .shell()
            .warn("the --all-targets flag has been changed to --target=all")?;
        vec!["all".to_string()]
    } else {
        args._values_of("target")
    };
    let target = tree::Target::from_cli(targets);

    let (edge_kinds, no_proc_macro) = parse_edge_kinds(config, args)?;
    let graph_features = edge_kinds.contains(&EdgeKind::Feature);

    let pkgs_to_prune = args.values_of("prune").map_or_else(
        || Vec::new(),
        |ps| {
            let mut specs = ps.map(|p| p.to_string()).collect::<Vec<_>>();
            specs.sort_unstable();
            specs.dedup();
            specs
        },
    );

    let packages = args.packages_from_flags()?;
    let mut invert = args
        .values_of("invert")
        .map_or_else(|| Vec::new(), |is| is.map(|s| s.to_string()).collect());
    if args.is_present_with_zero_values("invert") {
        match &packages {
            Packages::Packages(ps) => {
                // Backwards compatibility with old syntax of `cargo tree -i -p foo`.
                invert.extend(ps.clone());
            }
            _ => {
                return Err(format_err!(
                    "The `-i` flag requires a package name.\n\
\n\
The `-i` flag is used to inspect the reverse dependencies of a specific\n\
package. It will invert the tree and display the packages that depend on the\n\
given package.\n\
\n\
Note that in a workspace, by default it will only display the package's\n\
reverse dependencies inside the tree of the workspace member in the current\n\
directory. The --workspace flag can be used to extend it so that it will show\n\
the package's reverse dependencies across the entire workspace. The -p flag\n\
can be used to display the package's reverse dependencies only with the\n\
subtree of the package given to -p.\n\
"
                )
                .into());
            }
        }
    }

    let ws = args.workspace(config)?;

    if args.is_present_with_zero_values("package") {
        print_available_packages(&ws)?;
    }

    let charset = tree::Charset::from_str(args.value_of("charset").unwrap())
        .map_err(|e| anyhow::anyhow!("{}", e))?;
    let opts = tree::TreeOptions {
        cli_features: args.cli_features()?,
        packages,
        target,
        edge_kinds,
        invert,
        pkgs_to_prune,
        prefix,
        no_dedupe,
        duplicates: args.is_present("duplicates"),
        charset,
        format: args.value_of("format").unwrap().to_string(),
        graph_features,
        max_display_depth: args.value_of_u32("depth")?.unwrap_or(u32::MAX),
        no_proc_macro,
    };

    if opts.graph_features && opts.duplicates {
        return Err(format_err!("the `-e features` flag does not support `--duplicates`").into());
    }

    tree::build_and_print(&ws, &opts)?;
    Ok(())
}

/// Parses `--edges` option.
///
/// Returns a tuple of `EdgeKind` map and `no_proc_marco` flag.
fn parse_edge_kinds(
    config: &Config,
    args: &ArgMatches<'_>,
) -> CargoResult<(HashSet<EdgeKind>, bool)> {
    let (kinds, no_proc_macro) = {
        let mut no_proc_macro = false;
        let mut kinds = args.values_of("edges").map_or_else(
            || Vec::new(),
            |es| {
                es.flat_map(|e| e.split(','))
                    .filter(|e| {
                        no_proc_macro = *e == "no-proc-macro";
                        !no_proc_macro
                    })
                    .collect()
            },
        );

        if args.is_present("no-dev-dependencies") {
            config
                .shell()
                .warn("the --no-dev-dependencies flag has changed to -e=no-dev")?;
            kinds.push("no-dev");
        }

        if kinds.is_empty() {
            kinds.extend(&["normal", "build", "dev"]);
        }

        (kinds, no_proc_macro)
    };

    let mut result = HashSet::new();
    let insert_defaults = |result: &mut HashSet<EdgeKind>| {
        result.insert(EdgeKind::Dep(DepKind::Normal));
        result.insert(EdgeKind::Dep(DepKind::Build));
        result.insert(EdgeKind::Dep(DepKind::Development));
    };
    let unknown = |k| {
        bail!(
            "unknown edge kind `{}`, valid values are \
                \"normal\", \"build\", \"dev\", \
                \"no-normal\", \"no-build\", \"no-dev\", \"no-proc-macro\", \
                \"features\", or \"all\"",
            k
        )
    };
    if kinds.iter().any(|k| k.starts_with("no-")) {
        insert_defaults(&mut result);
        for kind in &kinds {
            match *kind {
                "no-normal" => result.remove(&EdgeKind::Dep(DepKind::Normal)),
                "no-build" => result.remove(&EdgeKind::Dep(DepKind::Build)),
                "no-dev" => result.remove(&EdgeKind::Dep(DepKind::Development)),
                "features" => result.insert(EdgeKind::Feature),
                "normal" | "build" | "dev" | "all" => {
                    bail!(
                        "`{}` dependency kind cannot be mixed with \
                            \"no-normal\", \"no-build\", or \"no-dev\" \
                            dependency kinds",
                        kind
                    )
                }
                k => return unknown(k),
            };
        }
        return Ok((result, no_proc_macro));
    }
    for kind in &kinds {
        match *kind {
            "all" => {
                insert_defaults(&mut result);
                result.insert(EdgeKind::Feature);
            }
            "features" => {
                result.insert(EdgeKind::Feature);
            }
            "normal" => {
                result.insert(EdgeKind::Dep(DepKind::Normal));
            }
            "build" => {
                result.insert(EdgeKind::Dep(DepKind::Build));
            }
            "dev" => {
                result.insert(EdgeKind::Dep(DepKind::Development));
            }
            k => return unknown(k),
        }
    }
    if kinds.len() == 1 && kinds[0] == "features" {
        insert_defaults(&mut result);
    }
    Ok((result, no_proc_macro))
}
<commit_msg>refactor(cargo-tree): simplify arg-parsing logic<commit_after>use crate::cli;
use crate::command_prelude::*;
use anyhow::{bail, format_err};
use cargo::core::dependency::DepKind;
use cargo::ops::tree::{self, EdgeKind};
use cargo::ops::Packages;
use cargo::util::print_available_packages;
use cargo::util::CargoResult;
use std::collections::HashSet;
use std::str::FromStr;

pub fn cli() -> App {
    subcommand("tree")
        .about("Display a tree visualization of a dependency graph")
        .arg(opt("quiet", "Suppress status messages").short("q"))
        .arg_manifest_path()
        .arg_package_spec_no_all(
            "Package to be used as the root of the tree",
            "Display the tree for all packages in the workspace",
            "Exclude specific workspace members",
        )
        // Deprecated, use --no-dedupe instead.
        .arg(Arg::with_name("all").long("all").short("a").hidden(true))
        // Deprecated, use --target=all instead.
        .arg(
            Arg::with_name("all-targets")
                .long("all-targets")
                .hidden(true),
        )
        .arg_features()
        .arg_target_triple(
            "Filter dependencies matching the given target-triple (default host platform). \
            Pass `all` to include all targets.",
        )
        // Deprecated, use -e=no-dev instead.
        .arg(
            Arg::with_name("no-dev-dependencies")
                .long("no-dev-dependencies")
                .hidden(true),
        )
        .arg(
            multi_opt(
                "edges",
                "KINDS",
                "The kinds of dependencies to display \
                 (features, normal, build, dev, all, \
                 no-normal, no-build, no-dev, no-proc-macro)",
            )
            .short("e"),
        )
        .arg(
            optional_multi_opt(
                "invert",
                "SPEC",
                "Invert the tree direction and focus on the given package",
            )
            .short("i"),
        )
        .arg(multi_opt(
            "prune",
            "SPEC",
            "Prune the given package from the display of the dependency tree",
        ))
        .arg(opt("depth", "Maximum display depth of the dependency tree").value_name("DEPTH"))
        // Deprecated, use --prefix=none instead.
        .arg(Arg::with_name("no-indent").long("no-indent").hidden(true))
        // Deprecated, use --prefix=depth instead.
        .arg(
            Arg::with_name("prefix-depth")
                .long("prefix-depth")
                .hidden(true),
        )
        .arg(
            opt(
                "prefix",
                "Change the prefix (indentation) of how each entry is displayed",
            )
            .value_name("PREFIX")
            .possible_values(&["depth", "indent", "none"])
            .default_value("indent"),
        )
        .arg(opt(
            "no-dedupe",
            "Do not de-duplicate (repeats all shared dependencies)",
        ))
        .arg(
            opt(
                "duplicates",
                "Show only dependencies which come in multiple versions (implies -i)",
            )
            .short("d")
            .alias("duplicate"),
        )
        .arg(
            opt("charset", "Character set to use in output: utf8, ascii")
                .value_name("CHARSET")
                .possible_values(&["utf8", "ascii"])
                .default_value("utf8"),
        )
        .arg(
            opt("format", "Format string used for printing dependencies")
                .value_name("FORMAT")
                .short("f")
                .default_value("{p}"),
        )
        .arg(
            // Backwards compatibility with old cargo-tree.
            Arg::with_name("version")
                .long("version")
                .short("V")
                .hidden(true),
        )
        .after_help("Run `cargo help tree` for more detailed information.\n")
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    if args.is_present("version") {
        let verbose = args.occurrences_of("verbose") > 0;
        let version = cli::get_version_string(verbose);
        cargo::drop_print!(config, "{}", version);
        return Ok(());
    }
    let prefix = if args.is_present("no-indent") {
        config
            .shell()
            .warn("the --no-indent flag has been changed to --prefix=none")?;
        "none"
    } else if args.is_present("prefix-depth") {
        config
            .shell()
            .warn("the --prefix-depth flag has been changed to --prefix=depth")?;
        "depth"
    } else {
        args.value_of("prefix").unwrap()
    };
    let prefix = tree::Prefix::from_str(prefix).map_err(|e| anyhow::anyhow!("{}", e))?;

    let no_dedupe = args.is_present("no-dedupe") || args.is_present("all");
    if args.is_present("all") {
        config.shell().warn(
            "The `cargo tree` --all flag has been changed to --no-dedupe, \
             and may be removed in a future version.\n\
             If you are looking to display all workspace members, use the --workspace flag.",
        )?;
    }

    let targets = if args.is_present("all-targets") {
        config
            .shell()
            .warn("the --all-targets flag has been changed to --target=all")?;
        vec!["all".to_string()]
    } else {
        args._values_of("target")
    };
    let target = tree::Target::from_cli(targets);

    let (edge_kinds, no_proc_macro) = parse_edge_kinds(config, args)?;
    let graph_features = edge_kinds.contains(&EdgeKind::Feature);

    let pkgs_to_prune = args._values_of("prune");

    let packages = args.packages_from_flags()?;
    let mut invert = args
        .values_of("invert")
        .map_or_else(|| Vec::new(), |is| is.map(|s| s.to_string()).collect());
    if args.is_present_with_zero_values("invert") {
        match &packages {
            Packages::Packages(ps) => {
                // Backwards compatibility with old syntax of `cargo tree -i -p foo`.
                invert.extend(ps.clone());
            }
            _ => {
                return Err(format_err!(
                    "The `-i` flag requires a package name.\n\
\n\
The `-i` flag is used to inspect the reverse dependencies of a specific\n\
package. It will invert the tree and display the packages that depend on the\n\
given package.\n\
\n\
Note that in a workspace, by default it will only display the package's\n\
reverse dependencies inside the tree of the workspace member in the current\n\
directory. The --workspace flag can be used to extend it so that it will show\n\
the package's reverse dependencies across the entire workspace. The -p flag\n\
can be used to display the package's reverse dependencies only with the\n\
subtree of the package given to -p.\n\
"
                )
                .into());
            }
        }
    }

    let ws = args.workspace(config)?;

    if args.is_present_with_zero_values("package") {
        print_available_packages(&ws)?;
    }

    let charset = tree::Charset::from_str(args.value_of("charset").unwrap())
        .map_err(|e| anyhow::anyhow!("{}", e))?;
    let opts = tree::TreeOptions {
        cli_features: args.cli_features()?,
        packages,
        target,
        edge_kinds,
        invert,
        pkgs_to_prune,
        prefix,
        no_dedupe,
        duplicates: args.is_present("duplicates"),
        charset,
        format: args.value_of("format").unwrap().to_string(),
        graph_features,
        max_display_depth: args.value_of_u32("depth")?.unwrap_or(u32::MAX),
        no_proc_macro,
    };

    if opts.graph_features && opts.duplicates {
        return Err(format_err!("the `-e features` flag does not support `--duplicates`").into());
    }

    tree::build_and_print(&ws, &opts)?;
    Ok(())
}

/// Parses `--edges` option.
///
/// Returns a tuple of `EdgeKind` map and `no_proc_marco` flag.
fn parse_edge_kinds(
    config: &Config,
    args: &ArgMatches<'_>,
) -> CargoResult<(HashSet<EdgeKind>, bool)> {
    let (kinds, no_proc_macro) = {
        let mut no_proc_macro = false;
        let mut kinds = args.values_of("edges").map_or_else(
            || Vec::new(),
            |es| {
                es.flat_map(|e| e.split(','))
                    .filter(|e| {
                        no_proc_macro = *e == "no-proc-macro";
                        !no_proc_macro
                    })
                    .collect()
            },
        );

        if args.is_present("no-dev-dependencies") {
            config
                .shell()
                .warn("the --no-dev-dependencies flag has changed to -e=no-dev")?;
            kinds.push("no-dev");
        }

        if kinds.is_empty() {
            kinds.extend(&["normal", "build", "dev"]);
        }

        (kinds, no_proc_macro)
    };

    let mut result = HashSet::new();
    let insert_defaults = |result: &mut HashSet<EdgeKind>| {
        result.insert(EdgeKind::Dep(DepKind::Normal));
        result.insert(EdgeKind::Dep(DepKind::Build));
        result.insert(EdgeKind::Dep(DepKind::Development));
    };
    let unknown = |k| {
        bail!(
            "unknown edge kind `{}`, valid values are \
                \"normal\", \"build\", \"dev\", \
                \"no-normal\", \"no-build\", \"no-dev\", \"no-proc-macro\", \
                \"features\", or \"all\"",
            k
        )
    };
    if kinds.iter().any(|k| k.starts_with("no-")) {
        insert_defaults(&mut result);
        for kind in &kinds {
            match *kind {
                "no-normal" => result.remove(&EdgeKind::Dep(DepKind::Normal)),
                "no-build" => result.remove(&EdgeKind::Dep(DepKind::Build)),
                "no-dev" => result.remove(&EdgeKind::Dep(DepKind::Development)),
                "features" => result.insert(EdgeKind::Feature),
                "normal" | "build" | "dev" | "all" => {
                    bail!(
                        "`{}` dependency kind cannot be mixed with \
                            \"no-normal\", \"no-build\", or \"no-dev\" \
                            dependency kinds",
                        kind
                    )
                }
                k => return unknown(k),
            };
        }
        return Ok((result, no_proc_macro));
    }
    for kind in &kinds {
        match *kind {
            "all" => {
                insert_defaults(&mut result);
                result.insert(EdgeKind::Feature);
            }
            "features" => {
                result.insert(EdgeKind::Feature);
            }
            "normal" => {
                result.insert(EdgeKind::Dep(DepKind::Normal));
            }
            "build" => {
                result.insert(EdgeKind::Dep(DepKind::Build));
            }
            "dev" => {
                result.insert(EdgeKind::Dep(DepKind::Development));
            }
            k => return unknown(k),
        }
    }
    if kinds.len() == 1 && kinds[0] == "features" {
        insert_defaults(&mut result);
    }
    Ok((result, no_proc_macro))
}
<|endoftext|>