<commit_before>//! Management of IRC server connection

use io_error = std::io::io_error::cond;
use std::io::{TcpStream,IpAddr};
use std::io::net::addrinfo;
use std::io::net::ip::SocketAddr;
use std::str;

/// Conn represenets a connection to a single IRC server
pub struct Conn<'a> {
    host: OptionsHost<'a>,
    priv tcp: TcpStream
}

/// OptionsHost allows for using an IP address or a host string
pub enum OptionsHost<'a> {
    Host(&'a str),
    Addr(IpAddr)
}

/// Options used with Conn for connecting to the server.
pub struct Options<'a> {
    host: OptionsHost<'a>,
    port: u16
}

impl<'a> Options<'a> {
    /// Returns a new Options struct with default values
    pub fn new(host: &'a str, port: u16) -> Options<'a> {
        #[inline];
        Options {
            host: Host(host),
            port: port
        }
    }
}

pub static DefaultPort: u16 = 6667;

/// Connects to the remote server. This method will not return until the connection
/// is terminated. Returns Ok(()) after connection termination if the connection was
/// established successfully, or Err(&str) if the connection could not be established in the
/// first place.
///
/// # Failure
///
/// Raises the `io_error` condition if an IO error happens at any point after the connection
/// is established.
pub fn connect(opts: Options) -> Result<(),&'static str> {
    let addr = {
        match opts.host {
            Addr(x) => x,
            Host(host) => {
                let _guard = io_error.trap(|err| {
                    warn!("io_error resolving host address: {}", err.to_str());
                }).guard();
                match addrinfo::get_host_addresses(host) {
                    None | Some([]) => return Err("could not resolve host address"),
                    Some([x, ..]) => x
                }
            }
        }
    };
    let addr = SocketAddr{ ip: addr, port: opts.port };

    let stream = io_error.trap(|err| {
        warn!("io_error connecting to server: {}", err.to_str());
    }).inside(|| {
        // I don't know if ::connect() can throw io_error, but better safe than sorry
        TcpStream::connect(addr)
    });
    let stream = match stream {
        None => return Err("could not connect to server"),
        Some(tcp) => tcp
    };

    let mut conn = Conn{
        host: opts.host,
        tcp: stream
    };

    conn.run();

    Ok(())
}

impl<'a> Conn<'a> {
    fn run(&mut self) {

    }
}

/// An IRC command
#[deriving(Eq,Clone)]
pub enum Command {
    /// An IRC command
    IRCCmd(~str),
    /// A 3-digit command code
    IRCCode(uint),
    /// CTCP actions. The string is the destination
    IRCAction(~str),
    /// CTCP commands. The first string is the command, the second is the destination
    IRCCTCP(~str, ~str),
    /// CTCP replies. The first string is the command, the second is the destination
    IRCCTCPReply(~str, ~str)
}

impl Command {
    /// Returns true if the command is a CTCP command
    pub fn is_ctcp(&self) -> bool {
        match *self {
            IRCAction(_) | IRCCTCP(_,_) | IRCCTCPReply(_,_) => true,
            _ => false
        }
    }
}

/// A parsed line
#[deriving(Eq,Clone)]
pub struct Line {
    /// The optional prefix
    prefix: Option<~str>,
    /// The command
    command: Command,
    /// Any arguments
    args: ~[~str],
}

impl Line {
    /// Parse a line into a Line struct
    pub fn parse(mut s: &str) -> Option<Line> {
        let mut prefix = None;
        if s.starts_with(":") {
            let idx = match s.find(' ') {
                None => return None,
                Some(idx) => idx
            };
            prefix = Some(s.slice(1, idx).to_owned());
            s = s.slice_from(idx+1);
        }
        let (mut command, checkCTCP) = {
            let cmd;
            match s.find(' ') {
                Some(0) => return None,
                None => {
                    cmd = s;
                    s = "";
                }
                Some(idx) => {
                    cmd = s.slice_to(idx);
                    s = s.slice_from(idx+1);
                }
            }
            if cmd.len() == 3 && cmd.chars().all(|c| c >= '0' && c <= '9') {
                (IRCCode(from_str(cmd).unwrap()), false)
            } else if cmd.chars().all(|c| c.is_ascii() && ::std::char::is_alphabetic(c)) {
                let shouldCheck = cmd == "PRIVMSG" || cmd == "NOTICE";
                (IRCCmd(cmd.to_owned()), shouldCheck)
            } else {
                return None;
            }
        };
        let mut args = ~[];
        while !s.is_empty() {
            if s.starts_with(":") {
                args.push(s.slice_from(1).to_owned());
                break;
            }
            let idx = match s.find(' ') {
                None => {
                    args.push(s.to_owned());
                    break;
                }
                Some(idx) => idx
            };
            args.push(s.slice_to(idx).to_owned());
            s = s.slice_from(idx+1);
        }
        if checkCTCP && args.len() > 1 && args.last().starts_with("\x01") {
            let mut text = args.pop();
            if text.len() > 1 && text.ends_with("\x01") {
                text = text.slice(1,text.len()-1).to_owned();
            } else {
                text.shift_char();
            }
            let dst = args[0];
            let mut argi = text.splitn(' ', 1).map(|s| s.to_owned());
            let ctcpcmd = argi.next().unwrap(); // splitn() should return at least 1 value
            args = argi.collect();
            match command {
                IRCCmd(~"PRIVMSG") => {
                    if "ACTION" == ctcpcmd {
                        command = IRCAction(dst);
                    } else {
                        command = IRCCTCP(ctcpcmd, dst);
                    }
                }
                IRCCmd(~"NOTICE") => {
                    command = IRCCTCPReply(ctcpcmd, dst);
                }
                _ => unreachable!()
            }
        }
        Some(Line{
            prefix: prefix,
            command: command,
            args: args
        })
    }
}

impl ToStr for Line {
    fn to_str(&self) -> ~str {
        let mut cap = self.prefix.as_ref().map_default(0, |s| 1+s.len()+1);
        let mut found_space = false;
        cap += match self.command {
            IRCCmd(ref s) => s.len(),
            IRCCode(_) => 3,
            IRCAction(ref dst) => {
                "PRIVMSG".len() + 1 + dst.len() + 1 + ":\x01ACTION".len()
            }
            IRCCTCP(ref cmd, ref dst) => {
                "PRIVMSG".len() + 1 + dst.len() + 1 + 2 + cmd.len()
            }
            IRCCTCPReply(ref cmd, ref dst) => {
                "NOTICE".len() + 1 + dst.len() + 1 + 2 + cmd.len()
            }
        };
        if self.command.is_ctcp() {
            for arg in self.args.iter() {
                cap += 1 + arg.len();
            }
            cap += 1; // for the final \x01
        } else if !self.args.is_empty() {
            if self.args.len() > 1 {
                for arg in self.args.init().iter() {
                    cap += 1 + arg.len();
                }
            }
            let last = self.args.last();
            found_space = last.find(' ').is_some();
            if found_space {
                cap += 1 + 1 /* : */ + last.len();
            } else {
                cap += 1 + last.len();
            }
        }
        let mut res = str::with_capacity(cap);
        if self.prefix.is_some() {
            res.push_char(':');
            res.push_str(*self.prefix.as_ref().unwrap());
            res.push_char(' ');
        }
        match self.command {
            IRCCmd(ref s) => res.push_str(*s),
            IRCCode(c) => {
                res.push_str(format!("{:03u}", c));
            }
            IRCAction(ref dst) => {
                res.push_str("PRIVMSG ");
                res.push_str(*dst);
                res.push_str(" :\x01ACTION");
            }
            IRCCTCP(ref cmd, ref dst) => {
                res.push_str("PRIVMSG ");
                res.push_str(*dst);
                res.push_str(" :\x01");
                res.push_str(*cmd);
            }
            IRCCTCPReply(ref cmd, ref dst) => {
                res.push_str("NOTICE ");
                res.push_str(*dst);
                res.push_str(" :\x01");
                res.push_str(*cmd);
            }
        }
        if self.command.is_ctcp() {
            for arg in self.args.iter() {
                res.push_char(' ');
                res.push_str(*arg);
            }
            res.push_char('\x01');
        } else if !self.args.is_empty() {
            if self.args.len() > 1 {
                for arg in self.args.init().iter() {
                    res.push_char(' ');
                    res.push_str(*arg);
                }
            }
            res.push_char(' ');
            if found_space {
                res.push_char(':');
            }
            res.push_str(*self.args.last());
        }
        res
    }
}

#[cfg(test)]
mod tests {
    use super::{Line,IRCCmd,IRCCode,IRCAction,IRCCTCP,IRCCTCPReply};

    #[test]
    fn parse_line() {
        macro_rules! t(
            ($s:expr, Some($exp:expr)) => ({
                t!($s, Some($exp), $s);
            });
            ($s:expr, Some($exp:expr), $res:expr) => ({
                let s = $s;
                let line = Line::parse(s);
                assert_eq!(line, Some($exp));
                let line = line.unwrap().to_str();
                assert_eq!(line.as_slice(), $res);
            });
            ($s:expr, None) => (
                assert_eq!(Line::parse($s), None);
            )
        )
        t!(":sendak.freenode.net 001 asldfkj :Welcome to the freenode Internet \
            Relay Chat Network asldfkj",
            Some(Line{
                prefix: Some(~"sendak.freenode.net"),
                command: IRCCode(1),
                args: ~[~"asldfkj",
                        ~"Welcome to the freenode Internet Relay Chat Network asldfkj"],
            }));
        t!("004 asdf :This is a test",
            Some(Line{
                prefix: None,
                command: IRCCode(4),
                args: ~[~"asdf", ~"This is a test"],
            }));
        t!(":nick!user@host.com PRIVMSG #channel :Some message",
            Some(Line{
                prefix: Some(~"nick!user@host.com"),
                command: IRCCmd(~"PRIVMSG"),
                args: ~[~"#channel", ~"Some message"],
            }));
        t!(" :sendak.freenode.net 001 asdf :Test", None);
        t!(":sendak  001 asdf :Test", None);
        t!("004",
            Some(Line{
                prefix: None,
                command: IRCCode(4),
                args: ~[],
            }));
        t!(":bob!user@host.com PRIVMSG #channel :\x01ACTION does some stuff",
            Some(Line{
                prefix: Some(~"bob!user@host.com"),
                command: IRCAction(~"#channel"),
                args: ~[~"does some stuff"],
            }),
            ":bob!user@host.com PRIVMSG #channel :\x01ACTION does some stuff\x01");
        t!(":bob!user@host.com PRIVMSG #channel :\x01VERSION\x01",
            Some(Line{
                prefix: Some(~"bob!user@host.com"),
                command: IRCCTCP(~"VERSION", ~"#channel"),
                args: ~[],
            }));
        t!(":bob NOTICE #frobnitz :\x01RESPONSE to whatever\x01",
            Some(Line{
                prefix: Some(~"bob"),
                command: IRCCTCPReply(~"RESPONSE", ~"#frobnitz"),
                args: ~[~"to whatever"],
            }));
        t!(":bob föo", None);
        t!(":bob f23", None);
    }
}
<commit_msg>Convert Line to base based on ~[u8]<commit_after>//! Management of IRC server connection

use io_error = std::io::io_error::cond;
use std::io::{TcpStream,IpAddr};
use std::io::net::addrinfo;
use std::io::net::ip::SocketAddr;
use std::{char,str,vec,uint};
use std::cmp::min;

/// Conn represenets a connection to a single IRC server
pub struct Conn<'a> {
    host: OptionsHost<'a>,
    priv tcp: TcpStream
}

/// OptionsHost allows for using an IP address or a host string
pub enum OptionsHost<'a> {
    Host(&'a str),
    Addr(IpAddr)
}

/// Options used with Conn for connecting to the server.
pub struct Options<'a> {
    host: OptionsHost<'a>,
    port: u16
}

impl<'a> Options<'a> {
    /// Returns a new Options struct with default values
    pub fn new(host: &'a str, port: u16) -> Options<'a> {
        #[inline];
        Options {
            host: Host(host),
            port: port
        }
    }
}

pub static DefaultPort: u16 = 6667;

/// Connects to the remote server. This method will not return until the connection
/// is terminated. Returns Ok(()) after connection termination if the connection was
/// established successfully, or Err(&str) if the connection could not be established in the
/// first place.
///
/// # Failure
///
/// Raises the `io_error` condition if an IO error happens at any point after the connection
/// is established.
pub fn connect(opts: Options) -> Result<(),&'static str> {
    let addr = {
        match opts.host {
            Addr(x) => x,
            Host(host) => {
                let _guard = io_error.trap(|err| {
                    warn!("io_error resolving host address: {}", err.to_str());
                }).guard();
                match addrinfo::get_host_addresses(host) {
                    None | Some([]) => return Err("could not resolve host address"),
                    Some([x, ..]) => x
                }
            }
        }
    };
    let addr = SocketAddr{ ip: addr, port: opts.port };

    let stream = io_error.trap(|err| {
        warn!("io_error connecting to server: {}", err.to_str());
    }).inside(|| {
        // I don't know if ::connect() can throw io_error, but better safe than sorry
        TcpStream::connect(addr)
    });
    let stream = match stream {
        None => return Err("could not connect to server"),
        Some(tcp) => tcp
    };

    let mut conn = Conn{
        host: opts.host,
        tcp: stream
    };

    conn.run();

    Ok(())
}

impl<'a> Conn<'a> {
    fn run(&mut self) {

    }
}

/// An IRC command
#[deriving(Eq,Clone)]
pub enum Command {
    /// An IRC command
    IRCCmd(~str),
    /// A 3-digit command code
    IRCCode(uint),
    /// CTCP actions. The first arg is the destination
    IRCAction(~[u8]),
    /// CTCP commands. The first arg is the command, the second is the destination
    IRCCTCP(~[u8], ~[u8]),
    /// CTCP replies. The first arg is the command, the second is the destination
    IRCCTCPReply(~[u8], ~[u8])
}

impl Command {
    /// Returns true if the command is a CTCP command
    pub fn is_ctcp(&self) -> bool {
        match *self {
            IRCAction(_) | IRCCTCP(_,_) | IRCCTCPReply(_,_) => true,
            _ => false
        }
    }
}

/// A parsed line
#[deriving(Eq,Clone)]
pub struct Line {
    /// The optional prefix
    prefix: Option<~[u8]>,
    /// The command
    command: Command,
    /// Any arguments
    args: ~[~[u8]],
}

impl Line {
    /// Parse a line into a Line struct
    pub fn parse(mut v: &[u8]) -> Option<Line> {
        let mut prefix = None;
        if v.starts_with(bytes!(":")) {
            let idx = match v.position_elem(&(' ' as u8)) {
                None => return None,
                Some(idx) => idx
            };
            prefix = Some(v.slice(1, idx).to_owned());
            v = v.slice_from(idx+1);
        }
        let (mut command, checkCTCP) = {
            let cmd;
            match v.position_elem(&(' ' as u8)) {
                Some(0) => return None,
                None => {
                    cmd = v;
                    v = &[];
                }
                Some(idx) => {
                    cmd = v.slice_to(idx);
                    v = v.slice_from(idx+1);
                }
            }
            if cmd.len() == 3 && cmd.iter().all(|&b| b >= '0' as u8 && b <= '9' as u8) {
                (IRCCode(uint::parse_bytes(cmd, 10).unwrap()), false)
            } else if cmd.iter().all(|&b| b < 0x80 && char::is_alphabetic(b as char)) {
                let shouldCheck = cmd == bytes!("PRIVMSG") || cmd == bytes!("NOTICE");
                (IRCCmd(str::from_utf8(cmd).to_owned()), shouldCheck)
            } else {
                return None;
            }
        };
        let mut args = ~[];
        while !v.is_empty() {
            if v[0] == ':' as u8 {
                args.push(v.slice_from(1).to_owned());
                break;
            }
            let idx = match v.position_elem(&(' ' as u8)) {
                None => {
                    args.push(v.to_owned());
                    break;
                }
                Some(idx) => idx
            };
            args.push(v.slice_to(idx).to_owned());
            v = v.slice_from(idx+1);
        }
        if checkCTCP && args.len() > 1 && args.last().starts_with([0x1]) {
            let mut text = args.pop();
            if text.len() > 1 && text.ends_with([0x1]) {
                text = text.slice(1,text.len()-1).to_owned();
            } else {
                text.shift();
            }
            let dst = args[0];
            let ctcpcmd;
            match text.position_elem(&(' ' as u8)) {
                Some(idx) => {
                    ctcpcmd = text.slice_to(idx).to_owned();
                    args = ~[text.slice_from(idx+1).to_owned()];
                }
                None => {
                    ctcpcmd = text.to_owned();
                    args = ~[];
                }
            }
            match command {
                IRCCmd(~"PRIVMSG") => {
                    if bytes!("ACTION") == ctcpcmd {
                        command = IRCAction(dst);
                    } else {
                        command = IRCCTCP(ctcpcmd, dst);
                    }
                }
                IRCCmd(~"NOTICE") => {
                    command = IRCCTCPReply(ctcpcmd, dst);
                }
                _ => unreachable!()
            }
        }
        Some(Line{
            prefix: prefix,
            command: command,
            args: args
        })
    }

    /// Converts into the "raw" representation :prefix cmd args
    pub fn to_raw(&self) -> ~[u8] {
        let mut cap = self.prefix.as_ref().map_default(0, |s| 1+s.len()+1);
        let mut found_space = false;
        cap += match self.command {
            IRCCmd(ref cmd) => cmd.len(),
            IRCCode(_) => 3,
            IRCAction(ref dst) => {
                "PRIVMSG".len() + 1 + dst.len() + 1 + ":\x01ACTION".len()
            }
            IRCCTCP(ref cmd, ref dst) => {
                "PRIVMSG".len() + 1 + dst.len() + 1 + 2 + cmd.len()
            }
            IRCCTCPReply(ref cmd, ref dst) => {
                "NOTICE".len() + 1 + dst.len() + 1 + 2 + cmd.len()
            }
        };
        if self.command.is_ctcp() {
            for arg in self.args.iter() {
                cap += 1 + arg.len();
            }
            cap += 1; // for the final \x01
        } else if !self.args.is_empty() {
            if self.args.len() > 1 {
                for arg in self.args.init().iter() {
                    cap += 1 + arg.len();
                }
            }
            let last = self.args.last();
            found_space = last.contains(&(' ' as u8));
            if found_space {
                cap += 1 + 1 /* : */ + last.len();
            } else {
                cap += 1 + last.len();
            }
        }
        let mut res = vec::with_capacity(cap);
        if self.prefix.is_some() {
            res.push(':' as u8);
            res.push_all(*self.prefix.as_ref().unwrap());
            res.push(' ' as u8);
        }
        match self.command {
            IRCCmd(ref cmd) => res.push_all(cmd.as_bytes()),
            IRCCode(c) => {
                uint::to_str_bytes(c, 10, |v| {
                    for _ in range(0, 3 - min(v.len(), 3)) {
                        res.push('0' as u8);
                    }
                    res.push_all(v);
                })
            }
            IRCAction(ref dst) => {
                res.push_all(bytes!("PRIVMSG "));
                res.push_all(*dst);
                res.push_all(bytes!(" :\x01ACTION"));
            }
            IRCCTCP(ref cmd, ref dst) => {
                res.push_all(bytes!("PRIVMSG "));
                res.push_all(*dst);
                res.push_all(bytes!(" :\x01"));
                res.push_all(cmd.as_slice());
            }
            IRCCTCPReply(ref cmd, ref dst) => {
                res.push_all(bytes!("NOTICE "));
                res.push_all(*dst);
                res.push_all(bytes!(" :\x01"));
                res.push_all(cmd.as_slice());
            }
        }
        if self.command.is_ctcp() {
            for arg in self.args.iter() {
                res.push(' ' as u8);
                res.push_all(*arg);
            }
            res.push(0x1);
        } else if !self.args.is_empty() {
            if self.args.len() > 1 {
                for arg in self.args.init().iter() {
                    res.push(' ' as u8);
                    res.push_all(*arg);
                }
            }
            res.push(' ' as u8);
            if found_space {
                res.push(':' as u8);
            }
            res.push_all(*self.args.last());
        }
        res
    }
}

#[cfg(test)]
mod tests {
    use super::{Line,IRCCmd,IRCCode,IRCAction,IRCCTCP,IRCCTCPReply};

    #[test]
    fn parse_line() {
        macro_rules! b(
            ($val:expr) => (bytes!($val).to_owned())
        )
        macro_rules! t(
            ($v:expr, Some($exp:expr)) => (
                t!($v, Some($exp), $v);
            );
            ($v:expr, Some($exp:expr), $res:expr) => ({
                let v = $v;
                let exp = $exp;
                let line = Line::parse(v);
                assert!(line.is_some());
                let line = line.unwrap();
                assert_eq!(line.prefix, exp.prefix);
                assert_eq!(line.command, exp.command);
                assert_eq!(line.args, exp.args);
                let line = line.to_raw();
                assert_eq!(line, $res);
            });
            ($s:expr, None) => (
                assert_eq!(Line::parse($s), None);
            )
        )
        t!(b!(":sendak.freenode.net 001 asldfkj :Welcome to the freenode Internet \
            Relay Chat Network asldfkj"),
            Some(Line{
                prefix: Some(b!("sendak.freenode.net")),
                command: IRCCode(1),
                args: ~[b!("asldfkj"),
                        b!("Welcome to the freenode Internet Relay Chat Network asldfkj")]
            }));
        t!(b!("004 asdf :This is a test"),
            Some(Line{
                prefix: None,
                command: IRCCode(4),
                args: ~[b!("asdf"), b!("This is a test")]
            }));
        t!(b!(":nick!user@host.com PRIVMSG #channel :Some message"),
            Some(Line{
                prefix: Some(b!("nick!user@host.com")),
                command: IRCCmd(~"PRIVMSG"),
                args: ~[b!("#channel"), b!("Some message")]
            }));
        t!(b!(" :sendak.freenode.net 001 asdf :Test"), None);
        t!(b!(":sendak  001 asdf :Test"), None);
        t!(b!("004"),
            Some(Line{
                prefix: None,
                command: IRCCode(4),
                args: ~[]
            }));
        t!(b!(":bob!user@host.com PRIVMSG #channel :\x01ACTION does some stuff"),
            Some(Line{
                prefix: Some(b!("bob!user@host.com")),
                command: IRCAction(b!("#channel")),
                args: ~[b!("does some stuff")]
            }),
            b!(":bob!user@host.com PRIVMSG #channel :\x01ACTION does some stuff\x01"));
        t!(b!(":bob!user@host.com PRIVMSG #channel :\x01VERSION\x01"),
            Some(Line{
                prefix: Some(b!("bob!user@host.com")),
                command: IRCCTCP(b!("VERSION"), b!("#channel")),
                args: ~[]
            }));
        t!(b!(":bob NOTICE #frobnitz :\x01RESPONSE to whatever\x01"),
            Some(Line{
                prefix: Some(b!("bob")),
                command: IRCCTCPReply(b!("RESPONSE"), b!("#frobnitz")),
                args: ~[b!("to whatever")]
            }));
        t!(b!(":bob föo"), None);
        t!(b!(":bob f23"), None);
    }
}
<|endoftext|>


<commit_before>#[deriving(Eq)]
#[deriving(Clone)]
pub enum Value {
	List(~[Value]),
	Atom(~str),
	Str(~str),
	Num(float)
}

#[inline]
fn isws(c: char) -> bool {
	c == ' ' || c == '\t' || c == '\n'
}

fn read_nows(input: &str, start: uint) -> Option<char> {
	input.iter().skip(start).skip_while(|&c| isws(c)).next()
}

fn read_number(input: &str, start: uint) -> Option<(~Value, uint)> {
	let end = input.iter().skip(start).position(|c| isws(c) || c == ')');
	let pos = match end {
		Some(end) => start+end,
		None => input.len()
	};
	let acc = input.slice(start, pos);
	match std::float::from_str(acc) {
		Some(f) => Some((~Num(f), pos)),
		None => None
	}
}

fn read_list(input: &str, start: uint) -> Option<(~Value, uint)> {
	let mut list = ~[];
	let mut i = start;
	loop {
		match read_value(input, i) {
			Some((v,new_i)) => {
				list.push(*v);
				i = new_i;
			}
			None if read_nows(input, i) == Some(')') => return Some((~List(list), i)),
			None => return None
		}
	}
}

fn read_atom(input: &str, start: uint) -> Option<(~Value, uint)> {
	let atom = std::str::from_chars(input.iter().skip(start).take_while(|&c| !isws(c) && c != ')').to_owned_vec());
	let len = atom.len();
	Some((~Atom(atom), start + len))
}

fn read_value(input: &str, start: uint) -> Option<(~Value, uint)> {
	if(start >= input.len()) {
		return None
	}

	match input[start] as char {
		c if isws(c) => read_value(input, start+1), // ignore whitespace
		'(' => read_list(input, start+1),
		')' => None,
		c if c.is_digit() || c == '.' => read_number(input, start),
		_ => read_atom(input, start)
	}
}

pub fn parse(input: &str) -> Option<Value> {
	match read_value(input, 0) {
		Some((v,_)) => Some(*v),
		None => None
	}
}

#[cfg(test)]
mod test {
	use super::{parse, Value, Num, List, Atom};

	fn parse_some(input: &'static str, expr: Value) {
		match parse(input) {
			Some(ref p) if p == &expr => {}
			Some(ref p) => {
				if(p != &expr) {
					fail!("Expected %?, not %?, for %?", expr, *p, input)
				}
			}
			None => fail!("Got None for %?", input)
		}
	}

	#[test]
	fn test_sexpr() {
		assert_eq!(parse(""), None);
		parse_some("123", Num(123.0));
		parse_some("()", List(~[]));
		parse_some("3.14159265358", Num(3.14159265358));
		parse_some("(hi)", List(~[Atom(~"hi")]));
		parse_some("(1)", List(~[Num(1.0)]));
		parse_some("(hi there)", List(~[Atom(~"hi"), Atom(~"there")]));
		parse_some("(hi (there))", List(~[Atom(~"hi"), List(~[Atom(~"there")])]));
		parse_some("(hi 123456)", List(~[Atom(~"hi"), Num(123456.0)]));
		parse_some("(())", List(~[List(~[])]));
		parse_some("(hi (there (fellow (human-bot!))))",
			List(~[Atom(~"hi"),
				List(~[Atom(~"there"),
					List(~[Atom(~"fellow"),
						List(~[Atom(~"human-bot!")])])])])
		);
	}

	#[test]
	fn test_whitespace() {
		// spaces
		parse_some("  (  1  )  ", List(~[Num(1.0)]));
		parse_some("  (  (  )  )  )", List(~[List(~[])]));

		// newlines
		parse_some("\n\n( \n\n )\n\n", List(~[]));
		parse_some("\n\n(\n\n123\n\n)\n\n", List(~[Num(123.0)]));
	}
}<commit_msg>parse -> from_str<commit_after>extern mod std;

#[deriving(Eq)]
#[deriving(Clone)]
pub enum Value {
	List(~[Value]),
	Atom(~str),
	Str(~str),
	Num(float)
}

#[inline]
fn isws(c: char) -> bool {
	c == ' ' || c == '\t' || c == '\n'
}

fn read_nows(input: &str, start: uint) -> Option<char> {
	input.iter().skip(start).skip_while(|&c| isws(c)).next()
}

fn read_number(input: &str, start: uint) -> Option<(~Value, uint)> {
	let end = input.iter().skip(start).position(|c| isws(c) || c == ')');
	let pos = match end {
		Some(end) => start+end,
		None => input.len()
	};
	let acc = input.slice(start, pos);
	match std::float::from_str(acc) {
		Some(f) => Some((~Num(f), pos)),
		None => None
	}
}

fn read_list(input: &str, start: uint) -> Option<(~Value, uint)> {
	let mut list = ~[];
	let mut i = start;
	loop {
		match read_value(input, i) {
			Some((v,new_i)) => {
				list.push(*v);
				i = new_i;
			}
			None if read_nows(input, i) == Some(')') => return Some((~List(list), i)),
			None => return None
		}
	}
}

fn read_atom(input: &str, start: uint) -> Option<(~Value, uint)> {
	let atom = std::str::from_chars(input.iter().skip(start).take_while(|&c| !isws(c) && c != ')').to_owned_vec());
	let len = atom.len();
	Some((~Atom(atom), start + len))
}

fn read_value(input: &str, start: uint) -> Option<(~Value, uint)> {
	if(start >= input.len()) {
		return None
	}

	match input[start] as char {
		c if isws(c) => read_value(input, start+1), // ignore whitespace
		'(' => read_list(input, start+1),
		')' => None,
		c if c.is_digit() || c == '.' => read_number(input, start),
		_ => read_atom(input, start)
	}
}

pub fn from_str(input: &str) -> Option<Value> {
	match read_value(input, 0) {
		Some((v,_)) => Some(*v),
		None => None
	}
}

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

	fn parse_some(input: &str, expr: Value) {
		match from_str(input) {
			Some(ref p) if p == &expr => {}
			Some(ref p) => {
				if(p != &expr) {
					fail!("Expected %?, not %?, for %?", expr, *p, input)
				}
			}
			None => fail!("Got None for %?", input)
		}
	}

	#[test]
	fn test_sexpr() {
		assert_eq!(from_str(""), None);
		parse_some("123", Num(123.0));
		parse_some("()", List(~[]));
		parse_some("3.14159265358", Num(3.14159265358));
		parse_some("(hi)", List(~[Atom(~"hi")]));
		parse_some("(1)", List(~[Num(1.0)]));
		parse_some("(hi there)", List(~[Atom(~"hi"), Atom(~"there")]));
		parse_some("(hi (there))", List(~[Atom(~"hi"), List(~[Atom(~"there")])]));
		parse_some("(hi 123456)", List(~[Atom(~"hi"), Num(123456.0)]));
		parse_some("(())", List(~[List(~[])]));
		parse_some("(hi (there (fellow (human-bot!))))",
			List(~[Atom(~"hi"),
				List(~[Atom(~"there"),
					List(~[Atom(~"fellow"),
						List(~[Atom(~"human-bot!")])])])])
		);
	}

	#[test]
	fn test_whitespace() {
		// spaces
		parse_some("  (  1  )  ", List(~[Num(1.0)]));
		parse_some("  (  (  )  )  )", List(~[List(~[])]));

		// newlines
		parse_some("\n\n( \n\n )\n\n", List(~[]));
		parse_some("\n\n(\n\n123\n\n)\n\n", List(~[Num(123.0)]));
	}
}<|endoftext|>


<commit_before>use std::cmp::min;
use std::env;
use std::fs::{File};
use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::str::from_utf8;
use std::path::{Path, PathBuf};
use unshare::{Command, Stdio};

use libc::{geteuid, getegid};
use libc::{uid_t, gid_t};

use config::Range;
use config::Settings;
use self::Uidmap::*;
use process_util::{env_path_find, capture_stdout};

#[derive(Clone)]
pub enum Uidmap {
    Singleton(uid_t, gid_t),
    Ranges(Vec<(uid_t, uid_t, uid_t)>, Vec<(gid_t, gid_t, gid_t)>),
}

fn read_id_map(file_path: &str, username: &str) -> Result<Vec<Range>,String>
{
    let file = try_msg!(File::open(&Path::new(file_path)),
        "Can't open {path}: {err}", path=file_path);
    let mut res = Vec::new();
    let reader = BufReader::new(file);
    for (num, line) in reader.lines().enumerate() {
        let line = try_msg!(line, "Error reading {path}: {err}", path=file_path);
        let parts: Vec<&str> = line[..].split(':').collect();
        if parts.len() == 0 || parts[0].trim().starts_with('#') {
            continue;
        }
        if parts.len() != 3 {
            return Err(format!("{}:{}: Bad syntax: {:?}",
                file_path, num+1, line));
        }
        let start = FromStr::from_str(parts[1]);
        let count: Result<uid_t, _> = FromStr::from_str(parts[2].trim_right());
        if parts.len() != 3 || start.is_err() || count.is_err() {
            return Err(format!("{}:{}: Bad syntax: {:?}",
                file_path, num+1, line));
        }
        if parts[0].eq(username) {
            let start: uid_t = start.unwrap();
            let end = start + count.unwrap() - 1;
            res.push(Range::new(start, end));
        }
    }
    return Ok(res);
}

pub fn match_ranges(req: &Vec<Range>, allowed: &Vec<Range>, own_id: uid_t)
    -> Result<Vec<(uid_t, uid_t, uid_t)>, ()>
{
    let mut res = vec!((0, own_id, 1));
    let mut reqiter = req.iter();
    let mut reqval = *reqiter.next().unwrap();
    let mut allowiter = allowed.iter();
    let mut allowval = *allowiter.next().unwrap();
    loop {
        if reqval.start() == 0 {
            reqval = reqval.shift(1);
        }
        if allowval.start() == 0 {
            allowval = allowval.shift(1);
        }
        let clen = min(reqval.len(), allowval.len());
        if clen > 0 {
            res.push((reqval.start(), allowval.start(), clen));
        }
        reqval = reqval.shift(clen);
        allowval = allowval.shift(clen);
        if reqval.len() == 0 {
            reqval = match reqiter.next() {
                Some(val) => *val,
                None => break,
            };
        }
        if allowval.len() == 0 {
            allowval = match allowiter.next() {
                Some(val) => *val,
                None => return Err(()),
            };
        }
    }
    return Ok(res);
}

fn make_uidmap<F>(fun: F) -> Result<Uidmap, String>
        where F: FnOnce(u32, u32, &Vec<Range>, &Vec<Range>)
                 -> Result<Uidmap, String>
{
    let uid = unsafe { geteuid() };
    let gid = unsafe { getegid() };

    if uid == 0 && !Path::new("/etc/subuid").exists() {
        // For root user we may use all available user ids
        // This is useful mostly for running vagga in vagga
        let uid_rng = try!(read_id_ranges("/proc/self/uid_map", true));
        let gid_rng = try!(read_id_ranges("/proc/self/gid_map", true));
        return fun(uid, gid, &uid_rng, &gid_rng);
    }

    let mut cmd = Command::new(env_path_find("id")
                               .unwrap_or(PathBuf::from("/usr/bin/id")));
    cmd.arg("-u").arg("-n");
    if let Ok(path) = env::var("_VAGGA_PATH") {
        cmd.env("PATH", path);
    }
    cmd.stdin(Stdio::null()).stderr(Stdio::inherit());
    let username = try!(capture_stdout(cmd)
        .map_err(|e| format!("Error running `id -u -n`: {}", e))
        .and_then(|val| from_utf8(&val).map(|x| x.trim().to_string())
                   .map_err(|e| format!("Can't decode username: {}", e))));
    let uid_map = read_id_map("/etc/subuid", &username)
        .map_err(|e| error!("Error reading uidmap: {}", e));
    let gid_map = read_id_map("/etc/subgid", &username)
        .map_err(|e| error!("Error reading gidmap: {}", e));

    if let (Ok(uid_map), Ok(gid_map)) = (uid_map, gid_map) {
        if uid_map.len() == 0 && gid_map.len() == 0 && uid == 0 {
            // For root user we may use all available user ids
            // This is useful mostly for running vagga in vagga
                let uid_rng = try!(read_id_ranges(
                    "/proc/self/uid_map", true));
                let gid_rng = try!(read_id_ranges(
                    "/proc/self/gid_map", true));
                return fun(uid, gid, &uid_rng, &gid_rng);
        }
        return fun(uid, gid, &uid_map, &gid_map);
    } else {
        warn!("Could not read /etc/subuid or /etc/subgid \
            (see http://bit.ly/err_subuid)");
        return Ok(Singleton(uid, gid));
    }
}

pub fn get_max_uidmap() -> Result<Uidmap, String>
{
    make_uidmap(|uid, gid, uid_map, gid_map| {
        let mut uids = vec!((0, uid, 1));
        for &rng in uid_map.iter() {
            let mut rng = rng;
            if uid >= rng.start() && uid <= rng.end() {
                // TODO(tailhook) implement better heuristic
                assert!(uid == rng.start());
                rng = rng.shift(1);
                if rng.len() == 0 { continue; }
            }
            uids.push((rng.start(), rng.start(), rng.len()));
        }

        let mut gids = vec!((0, gid, 1));
        for &rng in gid_map.iter() {
            let mut rng = rng;
            if gid >= rng.start() && gid <= rng.end() {
                // TODO(tailhook) implement better heuristic
                assert!(gid == rng.start());
                rng = rng.shift(1);
                if rng.len() == 0 { continue; }
            }
            gids.push((rng.start(), rng.start(), rng.len()));
        }

        return Ok(Ranges(uids, gids));
    })
}

fn read_id_ranges(path: &str, read_inside: bool)
    -> Result<Vec<Range>, String>
{
    let file = BufReader::new(try!(File::open(&Path::new(path))
        .map_err(|e| format!("Error reading uid/gid map: {}", e))));
    let mut result = vec!();
    for line in file.lines() {
        let line = try!(line
            .map_err(|_| format!("Error reading uid/gid map")));
        let mut words = line[..].split_whitespace();
        let inside: u32 = try!(words.next()
            .and_then(|x| FromStr::from_str(x).ok())
            .ok_or(format!("uid/gid map format error")));
        let outside: u32 = try!(words.next()
            .and_then(|x| FromStr::from_str(x).ok())
            .ok_or(format!("uid/gid map format error")));
        let count: u32 = try!(words.next()
            .and_then(|x| FromStr::from_str(x).ok())
            .ok_or(format!("uid/gid map format error")));
        if read_inside {
            result.push(Range::new(inside, inside+count-1));
        } else {
            result.push(Range::new(outside, outside+count-1));
        }
    }
    return Ok(result);
}

pub fn read_mapped_gids() -> Result<Vec<Range>, String>
{
    read_id_ranges("/etc/subuid", true)
}

pub fn map_users(settings: &Settings, uids: &Vec<Range>, gids: &Vec<Range>)
    -> Result<Uidmap, String>
{
    let default_uids = vec!(Range::new(0, 0));
    let default_gids = vec!(Range::new(0, 0));
    let uids = if uids.len() > 0 { uids } else { &default_uids };
    let gids = if gids.len() > 0 { gids } else { &default_gids };
    match settings.uid_map {
        None => {
            make_uidmap(|uid, gid, uid_ranges, gid_ranges| {
                let uid_map = try!(match_ranges(uids, &uid_ranges, uid)
                    .map_err(|()| {
                        return format!("Number of allowed subuids is too small. \
                            Required {:?}, allowed {:?}. You either need to increase \
                            allowed numbers in /etc/subuid (preferred) or decrease \
                            needed ranges in vagga.yaml by adding `uids` key \
                            to container config", uids, uid_ranges);
                    }));
                let gid_map = try!(match_ranges(gids, &gid_ranges, gid)
                    .map_err(|()| {
                        return format!("Number of allowed subgids is too small. \
                            Required {:?}, allowed {:?}. You either need to increase \
                            allowed numbers in /etc/subgid (preferred) or decrease \
                            needed ranges in vagga.yaml by adding `gids` key \
                            to container config", gids, gid_ranges);
                    }));
                Ok(Ranges(uid_map, gid_map))
            })
        }
        Some((ref uids, ref gids)) => {
            Ok(Ranges(uids.clone(), gids.clone()))
        }
    }
}
<commit_msg>WIP: Fix typo in `external-user-id` handling<commit_after>use std::cmp::min;
use std::env;
use std::fs::{File};
use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::str::from_utf8;
use std::path::{Path, PathBuf};
use unshare::{Command, Stdio};

use libc::{geteuid, getegid};
use libc::{uid_t, gid_t};

use config::Range;
use config::Settings;
use self::Uidmap::*;
use process_util::{env_path_find, capture_stdout};

#[derive(Clone)]
pub enum Uidmap {
    Singleton(uid_t, gid_t),
    Ranges(Vec<(uid_t, uid_t, uid_t)>, Vec<(gid_t, gid_t, gid_t)>),
}

fn read_id_map(file_path: &str, username: &str) -> Result<Vec<Range>,String>
{
    let file = try_msg!(File::open(&Path::new(file_path)),
        "Can't open {path}: {err}", path=file_path);
    let mut res = Vec::new();
    let reader = BufReader::new(file);
    for (num, line) in reader.lines().enumerate() {
        let line = try_msg!(line, "Error reading {path}: {err}", path=file_path);
        let parts: Vec<&str> = line[..].split(':').collect();
        if parts.len() == 0 || parts[0].trim().starts_with('#') {
            continue;
        }
        if parts.len() != 3 {
            return Err(format!("{}:{}: Bad syntax: {:?}",
                file_path, num+1, line));
        }
        let start = FromStr::from_str(parts[1]);
        let count: Result<uid_t, _> = FromStr::from_str(parts[2].trim_right());
        if parts.len() != 3 || start.is_err() || count.is_err() {
            return Err(format!("{}:{}: Bad syntax: {:?}",
                file_path, num+1, line));
        }
        if parts[0].eq(username) {
            let start: uid_t = start.unwrap();
            let end = start + count.unwrap() - 1;
            res.push(Range::new(start, end));
        }
    }
    return Ok(res);
}

pub fn match_ranges(req: &Vec<Range>, allowed: &Vec<Range>, own_id: uid_t)
    -> Result<Vec<(uid_t, uid_t, uid_t)>, ()>
{
    let mut res = vec!((0, own_id, 1));
    let mut reqiter = req.iter();
    let mut reqval = *reqiter.next().unwrap();
    let mut allowiter = allowed.iter();
    let mut allowval = *allowiter.next().unwrap();
    loop {
        if reqval.start() == 0 {
            reqval = reqval.shift(1);
        }
        if allowval.start() == 0 {
            allowval = allowval.shift(1);
        }
        let clen = min(reqval.len(), allowval.len());
        if clen > 0 {
            res.push((reqval.start(), allowval.start(), clen));
        }
        reqval = reqval.shift(clen);
        allowval = allowval.shift(clen);
        if reqval.len() == 0 {
            reqval = match reqiter.next() {
                Some(val) => *val,
                None => break,
            };
        }
        if allowval.len() == 0 {
            allowval = match allowiter.next() {
                Some(val) => *val,
                None => return Err(()),
            };
        }
    }
    return Ok(res);
}

fn make_uidmap<F>(fun: F) -> Result<Uidmap, String>
        where F: FnOnce(u32, u32, &Vec<Range>, &Vec<Range>)
                 -> Result<Uidmap, String>
{
    let uid = unsafe { geteuid() };
    let gid = unsafe { getegid() };

    if uid == 0 && !Path::new("/etc/subuid").exists() {
        // For root user we may use all available user ids
        // This is useful mostly for running vagga in vagga
        let uid_rng = try!(read_id_ranges("/proc/self/uid_map", true));
        let gid_rng = try!(read_id_ranges("/proc/self/gid_map", true));
        return fun(uid, gid, &uid_rng, &gid_rng);
    }

    let mut cmd = Command::new(env_path_find("id")
                               .unwrap_or(PathBuf::from("/usr/bin/id")));
    cmd.arg("-u").arg("-n");
    if let Ok(path) = env::var("_VAGGA_PATH") {
        cmd.env("PATH", path);
    }
    cmd.stdin(Stdio::null()).stderr(Stdio::inherit());
    let username = try!(capture_stdout(cmd)
        .map_err(|e| format!("Error running `id -u -n`: {}", e))
        .and_then(|val| from_utf8(&val).map(|x| x.trim().to_string())
                   .map_err(|e| format!("Can't decode username: {}", e))));
    let uid_map = read_id_map("/etc/subuid", &username)
        .map_err(|e| error!("Error reading uidmap: {}", e));
    let gid_map = read_id_map("/etc/subgid", &username)
        .map_err(|e| error!("Error reading gidmap: {}", e));

    if let (Ok(uid_map), Ok(gid_map)) = (uid_map, gid_map) {
        if uid_map.len() == 0 && gid_map.len() == 0 && uid == 0 {
            // For root user we may use all available user ids
            // This is useful mostly for running vagga in vagga
                let uid_rng = try!(read_id_ranges(
                    "/proc/self/uid_map", true));
                let gid_rng = try!(read_id_ranges(
                    "/proc/self/gid_map", true));
                return fun(uid, gid, &uid_rng, &gid_rng);
        }
        return fun(uid, gid, &uid_map, &gid_map);
    } else {
        warn!("Could not read /etc/subuid or /etc/subgid \
            (see http://bit.ly/err_subuid)");
        return Ok(Singleton(uid, gid));
    }
}

pub fn get_max_uidmap() -> Result<Uidmap, String>
{
    make_uidmap(|uid, gid, uid_map, gid_map| {
        let mut uids = vec!((0, uid, 1));
        for &rng in uid_map.iter() {
            let mut rng = rng;
            if uid >= rng.start() && uid <= rng.end() {
                // TODO(tailhook) implement better heuristic
                assert!(uid == rng.start());
                rng = rng.shift(1);
                if rng.len() == 0 { continue; }
            }
            uids.push((rng.start(), rng.start(), rng.len()));
        }

        let mut gids = vec!((0, gid, 1));
        for &rng in gid_map.iter() {
            let mut rng = rng;
            if gid >= rng.start() && gid <= rng.end() {
                // TODO(tailhook) implement better heuristic
                assert!(gid == rng.start());
                rng = rng.shift(1);
                if rng.len() == 0 { continue; }
            }
            gids.push((rng.start(), rng.start(), rng.len()));
        }

        return Ok(Ranges(uids, gids));
    })
}

fn read_id_ranges(path: &str, read_inside: bool)
    -> Result<Vec<Range>, String>
{
    let file = BufReader::new(try!(File::open(&Path::new(path))
        .map_err(|e| format!("Error reading uid/gid map: {}", e))));
    let mut result = vec!();
    for line in file.lines() {
        let line = try!(line
            .map_err(|_| format!("Error reading uid/gid map")));
        let mut words = line[..].split_whitespace();
        let inside: u32 = try!(words.next()
            .and_then(|x| FromStr::from_str(x).ok())
            .ok_or(format!("uid/gid map format error")));
        let outside: u32 = try!(words.next()
            .and_then(|x| FromStr::from_str(x).ok())
            .ok_or(format!("uid/gid map format error")));
        let count: u32 = try!(words.next()
            .and_then(|x| FromStr::from_str(x).ok())
            .ok_or(format!("uid/gid map format error")));
        if read_inside {
            result.push(Range::new(inside, inside+count-1));
        } else {
            result.push(Range::new(outside, outside+count-1));
        }
    }
    return Ok(result);
}

pub fn read_mapped_gids() -> Result<Vec<Range>, String>
{
    read_id_ranges("/proc/self/gid_map", true)
}

pub fn map_users(settings: &Settings, uids: &Vec<Range>, gids: &Vec<Range>)
    -> Result<Uidmap, String>
{
    let default_uids = vec!(Range::new(0, 0));
    let default_gids = vec!(Range::new(0, 0));
    let uids = if uids.len() > 0 { uids } else { &default_uids };
    let gids = if gids.len() > 0 { gids } else { &default_gids };
    match settings.uid_map {
        None => {
            make_uidmap(|uid, gid, uid_ranges, gid_ranges| {
                let uid_map = try!(match_ranges(uids, &uid_ranges, uid)
                    .map_err(|()| {
                        return format!("Number of allowed subuids is too small. \
                            Required {:?}, allowed {:?}. You either need to increase \
                            allowed numbers in /etc/subuid (preferred) or decrease \
                            needed ranges in vagga.yaml by adding `uids` key \
                            to container config", uids, uid_ranges);
                    }));
                let gid_map = try!(match_ranges(gids, &gid_ranges, gid)
                    .map_err(|()| {
                        return format!("Number of allowed subgids is too small. \
                            Required {:?}, allowed {:?}. You either need to increase \
                            allowed numbers in /etc/subgid (preferred) or decrease \
                            needed ranges in vagga.yaml by adding `gids` key \
                            to container config", gids, gid_ranges);
                    }));
                Ok(Ranges(uid_map, gid_map))
            })
        }
        Some((ref uids, ref gids)) => {
            Ok(Ranges(uids.clone(), gids.clone()))
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the file stat action.
//!
//! A file stat action responses with stat of a given file

use std::fs::Metadata;
use std::path::PathBuf;

use log::warn;

use crate::session::{self, Error, Session};

impl From<std::io::Error> for Error {

    fn from(e: std::io::Error) -> Error {
        Error::action(e)
    }
}

#[derive(Debug)]
pub struct Request {
    path: PathBuf,
    collect_ext_attrs: bool,
    follow_symlink: bool,
}

#[derive(Debug)]
pub struct Response {
    path: PathBuf,
    metadata: Metadata,
    symlink: Option<PathBuf>,
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
}

pub fn handle<S: Session>(session: &mut S, request: Request) -> session::Result<()> {
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)?
    } else {
        std::fs::symlink_metadata(&request.path)?
    };

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let mut response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        #[cfg(target_family = "unix")]
        ext_attrs: vec!(),
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    if request.collect_ext_attrs {
        // TODO: This is not pretty. Consider creating a blank `ext_attrs`
        // implementation for Windows and make this code compile regardless of
        // the platform.
        #[cfg(target_family = "unix")]
        {
            // TODO: Do not fail on error.
            response.ext_attrs.extend(crate::fs::unix::ext_attrs(&response.path)?);
        }
    }

    session.reply(response)?;
    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
            follow_symlink: proto.follow_symlink.unwrap_or(false),
            collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            #[cfg(target_family = "unix")]
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            #[cfg(target_os = "linux")]
            st_flags_linux: self.flags_linux,
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
}
<commit_msg>Improve attribute collection in the stat action.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the file stat action.
//!
//! A file stat action responses with stat of a given file

use std::fs::Metadata;
use std::path::PathBuf;

use log::warn;

use crate::session::{self, Error, Session};

impl From<std::io::Error> for Error {

    fn from(e: std::io::Error) -> Error {
        Error::action(e)
    }
}

#[derive(Debug)]
pub struct Request {
    path: PathBuf,
    collect_ext_attrs: bool,
    follow_symlink: bool,
}

#[derive(Debug)]
pub struct Response {
    path: PathBuf,
    metadata: Metadata,
    symlink: Option<PathBuf>,
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
}

pub fn handle<S: Session>(session: &mut S, request: Request) -> session::Result<()> {
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)?
    } else {
        std::fs::symlink_metadata(&request.path)?
    };

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_family = "unix")]
    let ext_attrs = if request.collect_ext_attrs {
        // TODO: Make the `ack!` macro more expressive and then simplify it.
        match crate::fs::unix::ext_attrs(&request.path) {
            Ok(ext_attrs) => ext_attrs.collect(),
            Err(error) => {
                warn! {
                    "failed to collect attributes for '{path}': {cause}",
                    path = request.path.display(),
                    cause = error,
                };
                vec!()
            },
        }
    } else {
        vec!()
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        #[cfg(target_family = "unix")]
        ext_attrs: ext_attrs,
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    session.reply(response)?;
    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
            follow_symlink: proto.follow_symlink.unwrap_or(false),
            collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            #[cfg(target_family = "unix")]
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            #[cfg(target_os = "linux")]
            st_flags_linux: self.flags_linux,
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    access_path::AccessPath,
    account_address::AccountAddress,
    identifier::{IdentStr, Identifier},
    proto::language_storage::ModuleId as ProtoModuleId,
};
use canonical_serialization::{
    CanonicalDeserialize, CanonicalDeserializer, CanonicalSerialize, CanonicalSerializer,
    SimpleSerializer,
};
use crypto::hash::{AccessPathHasher, CryptoHash, CryptoHasher, HashValue};
use failure::Result;
#[cfg(any(test, feature = "testing"))]
use proptest_derive::Arbitrary;
use proto_conv::{FromProto, IntoProto};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
pub struct StructTag {
    pub address: AccountAddress,
    pub module: Identifier,
    pub name: Identifier,
    pub type_params: Vec<StructTag>,
}

/// Represents the intitial key into global storage where we first index by the address, and then
/// the struct tag
#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
pub struct ResourceKey {
    address: AccountAddress,
    type_: StructTag,
}

impl ResourceKey {
    pub fn address(&self) -> AccountAddress {
        self.address
    }

    pub fn type_(&self) -> &StructTag {
        &self.type_
    }
}

impl ResourceKey {
    pub fn new(address: AccountAddress, type_: StructTag) -> Self {
        ResourceKey { address, type_ }
    }
}

/// Represents the initial key into global storage where we first index by the address, and then
/// the struct tag
#[derive(
    Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord, FromProto, IntoProto,
)]
#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
#[ProtoType(ProtoModuleId)]
#[cfg_attr(any(test, feature = "testing"), proptest(no_params))]
pub struct ModuleId {
    address: AccountAddress,
    name: Identifier,
}

impl ModuleId {
    pub fn new(address: AccountAddress, name: Identifier) -> Self {
        ModuleId { address, name }
    }

    pub fn name(&self) -> &IdentStr {
        &self.name
    }

    pub fn address(&self) -> &AccountAddress {
        &self.address
    }
}

impl<'a> From<&'a ModuleId> for AccessPath {
    fn from(module_id: &'a ModuleId) -> Self {
        AccessPath::code_access_path(module_id)
    }
}

impl CanonicalSerialize for ModuleId {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> Result<()> {
        serializer
            .encode_struct(&self.address)?
            .encode_struct(&self.name)?;
        Ok(())
    }
}

impl CanonicalDeserialize for ModuleId {
    fn deserialize(deserializer: &mut impl CanonicalDeserializer) -> Result<Self> {
        let address = deserializer.decode_struct::<AccountAddress>()?;
        let name = deserializer.decode_struct::<Identifier>()?;

        Ok(Self { address, name })
    }
}

impl CryptoHash for ModuleId {
    type Hasher = AccessPathHasher;

    fn hash(&self) -> HashValue {
        let mut state = Self::Hasher::default();
        state.write(&SimpleSerializer::<Vec<u8>>::serialize(self).unwrap());
        state.finish()
    }
}

impl CanonicalSerialize for StructTag {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> Result<()> {
        serializer
            .encode_struct(&self.address)?
            .encode_struct(&self.module)?
            .encode_struct(&self.name)?
            .encode_vec(&self.type_params)?;
        Ok(())
    }
}

impl CanonicalDeserialize for StructTag {
    fn deserialize(deserializer: &mut impl CanonicalDeserializer) -> Result<Self> {
        let address = deserializer.decode_struct::<AccountAddress>()?;
        let module = deserializer.decode_struct::<Identifier>()?;
        let name = deserializer.decode_struct::<Identifier>()?;
        let type_params = deserializer.decode_vec::<StructTag>()?;
        Ok(Self {
            address,
            name,
            module,
            type_params,
        })
    }
}

impl CryptoHash for StructTag {
    type Hasher = AccessPathHasher;

    fn hash(&self) -> HashValue {
        let mut state = Self::Hasher::default();
        state.write(&SimpleSerializer::<Vec<u8>>::serialize(self).unwrap());
        state.finish()
    }
}
<commit_msg>[types] impl conversions from/to prost proto and ModuleId<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    access_path::AccessPath,
    account_address::AccountAddress,
    identifier::{IdentStr, Identifier},
    proto::language_storage::ModuleId as ProtoModuleId,
};
use canonical_serialization::{
    CanonicalDeserialize, CanonicalDeserializer, CanonicalSerialize, CanonicalSerializer,
    SimpleSerializer,
};
use crypto::hash::{AccessPathHasher, CryptoHash, CryptoHasher, HashValue};
use failure::Result;
#[cfg(any(test, feature = "testing"))]
use proptest_derive::Arbitrary;
use proto_conv::{FromProto, IntoProto};
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};

#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
pub struct StructTag {
    pub address: AccountAddress,
    pub module: Identifier,
    pub name: Identifier,
    pub type_params: Vec<StructTag>,
}

/// Represents the intitial key into global storage where we first index by the address, and then
/// the struct tag
#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
pub struct ResourceKey {
    address: AccountAddress,
    type_: StructTag,
}

impl ResourceKey {
    pub fn address(&self) -> AccountAddress {
        self.address
    }

    pub fn type_(&self) -> &StructTag {
        &self.type_
    }
}

impl ResourceKey {
    pub fn new(address: AccountAddress, type_: StructTag) -> Self {
        ResourceKey { address, type_ }
    }
}

/// Represents the initial key into global storage where we first index by the address, and then
/// the struct tag
#[derive(
    Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord, FromProto, IntoProto,
)]
#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
#[ProtoType(ProtoModuleId)]
#[cfg_attr(any(test, feature = "testing"), proptest(no_params))]
pub struct ModuleId {
    address: AccountAddress,
    name: Identifier,
}

impl ModuleId {
    pub fn new(address: AccountAddress, name: Identifier) -> Self {
        ModuleId { address, name }
    }

    pub fn name(&self) -> &IdentStr {
        &self.name
    }

    pub fn address(&self) -> &AccountAddress {
        &self.address
    }
}

impl TryFrom<crate::proto::types::ModuleId> for ModuleId {
    type Error = failure::Error;

    fn try_from(proto: crate::proto::types::ModuleId) -> Result<Self> {
        Ok(Self {
            address: proto.address.try_into()?,
            name: Identifier::new(proto.name)?,
        })
    }
}

impl From<ModuleId> for crate::proto::types::ModuleId {
    fn from(txn: ModuleId) -> Self {
        Self {
            address: txn.address.into(),
            name: txn.name.into_string(),
        }
    }
}

impl<'a> From<&'a ModuleId> for AccessPath {
    fn from(module_id: &'a ModuleId) -> Self {
        AccessPath::code_access_path(module_id)
    }
}

impl CanonicalSerialize for ModuleId {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> Result<()> {
        serializer
            .encode_struct(&self.address)?
            .encode_struct(&self.name)?;
        Ok(())
    }
}

impl CanonicalDeserialize for ModuleId {
    fn deserialize(deserializer: &mut impl CanonicalDeserializer) -> Result<Self> {
        let address = deserializer.decode_struct::<AccountAddress>()?;
        let name = deserializer.decode_struct::<Identifier>()?;

        Ok(Self { address, name })
    }
}

impl CryptoHash for ModuleId {
    type Hasher = AccessPathHasher;

    fn hash(&self) -> HashValue {
        let mut state = Self::Hasher::default();
        state.write(&SimpleSerializer::<Vec<u8>>::serialize(self).unwrap());
        state.finish()
    }
}

impl CanonicalSerialize for StructTag {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> Result<()> {
        serializer
            .encode_struct(&self.address)?
            .encode_struct(&self.module)?
            .encode_struct(&self.name)?
            .encode_vec(&self.type_params)?;
        Ok(())
    }
}

impl CanonicalDeserialize for StructTag {
    fn deserialize(deserializer: &mut impl CanonicalDeserializer) -> Result<Self> {
        let address = deserializer.decode_struct::<AccountAddress>()?;
        let module = deserializer.decode_struct::<Identifier>()?;
        let name = deserializer.decode_struct::<Identifier>()?;
        let type_params = deserializer.decode_vec::<StructTag>()?;
        Ok(Self {
            address,
            name,
            module,
            type_params,
        })
    }
}

impl CryptoHash for StructTag {
    type Hasher = AccessPathHasher;

    fn hash(&self) -> HashValue {
        let mut state = Self::Hasher::default();
        state.write(&SimpleSerializer::<Vec<u8>>::serialize(self).unwrap());
        state.finish()
    }
}
<|endoftext|>


<commit_before><commit_msg>single-object version: compiles, but blocks<commit_after><|endoftext|>


<commit_before>//! Awesome compatibility modules

use wlroots;

use rlua::{self, LightUserData, Lua, Table};
use std::{env, mem, path::PathBuf};
use xcb::{xkb, Connection};
use awesome::lua::setup_lua;

mod awesome;
mod button;
mod class;
mod client;
mod drawable;
mod drawin;
mod key;
pub mod keygrabber;
pub mod lua;
mod mouse;
pub mod mousegrabber;
mod object;
mod property;
mod root;
mod screen;
pub mod signal;
mod tag;
mod xproperty;

pub use self::lua::{NEXT_LUA, LUA};

pub use self::drawin::{Drawin, DRAWINS_HANDLE};
pub use self::key::Key;
pub use self::keygrabber::keygrabber_handle;
pub use self::mousegrabber::mousegrabber_handle;
pub use self::object::{Object, Objectable};
pub use self::root::ROOT_KEYS_HANDLE;
pub use self::signal::*;

use compositor::Server;

pub const GLOBAL_SIGNALS: &'static str = "__awesome_global_signals";
pub const XCB_CONNECTION_HANDLE: &'static str = "__xcb_connection";

/// Called from `wayland_glib_interface.c` after every call back into the wayland event loop.
///
/// This restarts the Lua thread if there is a new one pending
#[no_mangle]
pub extern "C" fn refresh_awesome() {
    NEXT_LUA.with(|new_lua_check| {
        if new_lua_check.get() {
            new_lua_check.set(false);
            LUA.with(|lua| {
                let mut lua = lua.borrow_mut();
                unsafe {
                    *lua = rlua::Lua::new_with_debug();
                }
            });
            let compositor = wlroots::compositor_handle().unwrap();
            setup_lua(compositor);
        }
    });
}

pub fn init(lua: &Lua, server: &mut Server) -> rlua::Result<()> {
    setup_awesome_path(lua)?;
    setup_global_signals(lua)?;
    setup_xcb_connection(lua)?;
    button::init(lua)?;
    awesome::init(lua)?;
    key::init(lua)?;
    client::init(lua)?;
    screen::init(lua, server)?;
    keygrabber::init(lua)?;
    root::init(lua)?;
    mouse::init(lua)?;
    tag::init(lua)?;
    drawin::init(lua)?;
    drawable::init(lua)?;
    mousegrabber::init(lua)?;
    Ok(())
}

fn setup_awesome_path(lua: &Lua) -> rlua::Result<()> {
    let globals = lua.globals();
    let package: Table = globals.get("package")?;
    let mut path = package.get::<_, String>("path")?;
    let mut cpath = package.get::<_, String>("cpath")?;
    let mut xdg_data_path: PathBuf = env::var("XDG_DATA_DIRS").unwrap_or("/usr/share".into())
                                                              .into();
    xdg_data_path.push("awesome/lib");
    path.push_str(&format!(";{0}/?.lua;{0}/?/init.lua",
                           xdg_data_path.as_os_str().to_string_lossy()));
    package.set("path", path)?;
    let mut xdg_config_path: PathBuf = env::var("XDG_CONFIG_DIRS").unwrap_or("/etc/xdg".into())
                                                                  .into();
    xdg_config_path.push("awesome");
    cpath.push_str(&format!(";{}/?.so;{}/?.so",
                            xdg_config_path.into_os_string().to_string_lossy(),
                            xdg_data_path.into_os_string().to_string_lossy()));
    package.set("cpath", cpath)?;

    Ok(())
}

/// Set up global signals value
///
/// We need to store this in Lua, because this make it safer to use.
fn setup_global_signals(lua: &Lua) -> rlua::Result<()> {
    lua.set_named_registry_value(GLOBAL_SIGNALS, lua.create_table()?)
}

/// Sets up the xcb connection and stores it in Lua (for us to access it later)
fn setup_xcb_connection(lua: &Lua) -> rlua::Result<()> {
    let con = match Connection::connect(None) {
        Err(err) => {
            error!("Way Cooler requires XWayland in order to function");
            error!("However, xcb could not connect to it. Is it running?");
            error!("{:?}", err);
            panic!("Could not connect to XWayland instance");
        }
        Ok(con) => con.0
    };
    // Tell xcb we are using the xkb extension
    match xkb::use_extension(&con, 1, 0).get_reply() {
        Ok(r) => {
            if !r.supported() {
                panic!("xkb-1.0 is not supported");
            }
        }
        Err(err) => {
            panic!("Could not get xkb extension supported version {:?}", err);
        }
    }
    lua.set_named_registry_value(XCB_CONNECTION_HANDLE,
                                  LightUserData(con.get_raw_conn() as _))?;
    mem::forget(con);
    Ok(())
}

pub fn dummy<'lua>(_: &'lua Lua, _: rlua::Value) -> rlua::Result<()> {
    Ok(())
}
<commit_msg>Fix XDG_DATA_DIRS and XDG_CONFIG_DIRS handling<commit_after>//! Awesome compatibility modules

use wlroots;

use rlua::{self, LightUserData, Lua, Table};
use std::{env, mem, path::PathBuf};
use xcb::{xkb, Connection};
use awesome::lua::setup_lua;

mod awesome;
mod button;
mod class;
mod client;
mod drawable;
mod drawin;
mod key;
pub mod keygrabber;
pub mod lua;
mod mouse;
pub mod mousegrabber;
mod object;
mod property;
mod root;
mod screen;
pub mod signal;
mod tag;
mod xproperty;

pub use self::lua::{NEXT_LUA, LUA};

pub use self::drawin::{Drawin, DRAWINS_HANDLE};
pub use self::key::Key;
pub use self::keygrabber::keygrabber_handle;
pub use self::mousegrabber::mousegrabber_handle;
pub use self::object::{Object, Objectable};
pub use self::root::ROOT_KEYS_HANDLE;
pub use self::signal::*;

use compositor::Server;

pub const GLOBAL_SIGNALS: &'static str = "__awesome_global_signals";
pub const XCB_CONNECTION_HANDLE: &'static str = "__xcb_connection";

/// Called from `wayland_glib_interface.c` after every call back into the wayland event loop.
///
/// This restarts the Lua thread if there is a new one pending
#[no_mangle]
pub extern "C" fn refresh_awesome() {
    NEXT_LUA.with(|new_lua_check| {
        if new_lua_check.get() {
            new_lua_check.set(false);
            LUA.with(|lua| {
                let mut lua = lua.borrow_mut();
                unsafe {
                    *lua = rlua::Lua::new_with_debug();
                }
            });
            let compositor = wlroots::compositor_handle().unwrap();
            setup_lua(compositor);
        }
    });
}

pub fn init(lua: &Lua, server: &mut Server) -> rlua::Result<()> {
    setup_awesome_path(lua)?;
    setup_global_signals(lua)?;
    setup_xcb_connection(lua)?;
    button::init(lua)?;
    awesome::init(lua)?;
    key::init(lua)?;
    client::init(lua)?;
    screen::init(lua, server)?;
    keygrabber::init(lua)?;
    root::init(lua)?;
    mouse::init(lua)?;
    tag::init(lua)?;
    drawin::init(lua)?;
    drawable::init(lua)?;
    mousegrabber::init(lua)?;
    Ok(())
}

fn setup_awesome_path(lua: &Lua) -> rlua::Result<()> {
    let globals = lua.globals();
    let package: Table = globals.get("package")?;
    let mut path = package.get::<_, String>("path")?;
    let mut cpath = package.get::<_, String>("cpath")?;

    for mut xdg_data_path in env::var("XDG_DATA_DIRS").unwrap_or("/usr/share".into())
                                                      .split(':')
                                                      .map(PathBuf::from)
    {
        xdg_data_path.push("awesome/lib");
        path.push_str(&format!(";{0}/?.lua;{0}/?/init.lua",
                               xdg_data_path.as_os_str().to_string_lossy()));
        cpath.push_str(&format!(";{}/?.so", xdg_data_path.into_os_string().to_string_lossy()));
    }

    for mut xdg_config_path in env::var("XDG_CONFIG_DIRS").unwrap_or("/etc/xdg".into())
                                                          .split(':')
                                                          .map(PathBuf::from)
    {
        xdg_config_path.push("awesome");
        cpath.push_str(&format!(";{}/?.so",
                                xdg_config_path.into_os_string().to_string_lossy()));
    }

    package.set("path", path)?;
    package.set("cpath", cpath)?;

    Ok(())
}

/// Set up global signals value
///
/// We need to store this in Lua, because this make it safer to use.
fn setup_global_signals(lua: &Lua) -> rlua::Result<()> {
    lua.set_named_registry_value(GLOBAL_SIGNALS, lua.create_table()?)
}

/// Sets up the xcb connection and stores it in Lua (for us to access it later)
fn setup_xcb_connection(lua: &Lua) -> rlua::Result<()> {
    let con = match Connection::connect(None) {
        Err(err) => {
            error!("Way Cooler requires XWayland in order to function");
            error!("However, xcb could not connect to it. Is it running?");
            error!("{:?}", err);
            panic!("Could not connect to XWayland instance");
        }
        Ok(con) => con.0
    };
    // Tell xcb we are using the xkb extension
    match xkb::use_extension(&con, 1, 0).get_reply() {
        Ok(r) => {
            if !r.supported() {
                panic!("xkb-1.0 is not supported");
            }
        }
        Err(err) => {
            panic!("Could not get xkb extension supported version {:?}", err);
        }
    }
    lua.set_named_registry_value(XCB_CONNECTION_HANDLE,
                                  LightUserData(con.get_raw_conn() as _))?;
    mem::forget(con);
    Ok(())
}

pub fn dummy<'lua>(_: &'lua Lua, _: rlua::Value) -> rlua::Result<()> {
    Ok(())
}
<|endoftext|>


<commit_before>use std::io::Write;
use std::io::Result as IOResult;

use util::*;
use protocol::*;
use Side;

pub fn write_protocol<O: Write>(protocol: Protocol, out: &mut O, side: Side) -> IOResult<()> {
    for iface in &protocol.interfaces {
        if (iface.name == "wl_display" || iface.name == "wl_registry") && side == Side::Server {
            continue
        }
        try!(write_interface(iface, out, side));
    }
    Ok(())
}

fn write_interface<O: Write>(interface: &Interface, out: &mut O, side: Side) -> IOResult<()> {
    try!(writeln!(out, "pub mod {} {{", interface.name));

    if let Some((ref short, ref long)) = interface.description {
        try!(write_doc(Some(short), long, true, out));
    }

    try!(writeln!(out, "use super::EventQueueHandle;"));
    try!(writeln!(out, "use super::{};", side.object_trait()));
    match side {
        Side::Client => try!(writeln!(out, "use wayland_sys::client::wl_proxy;")),
        Side::Server => try!(writeln!(out, "use wayland_sys::server::wl_resource;"))
    };

    try!(writeln!(out, "pub struct {} {{ ptr: *mut {} }}",
        snake_to_camel(&interface.name),
        side.object_ptr_type()
    ));

    // Generate object trait impl
    try!(writeln!(out, "impl {} for {} {{",
        side.object_trait(),
        snake_to_camel(&interface.name)
    ));
    try!(writeln!(out, "fn ptr(&self) -> *mut {} {{ self.ptr }}", side.object_ptr_type()));
    try!(writeln!(out, "unsafe fn from_ptr(ptr: *mut {0}) -> {1} {{ {1} {{ ptr: ptr }} }}",
        side.object_ptr_type(),
        snake_to_camel(&interface.name)
    ));
    try!(writeln!(out, "}}"));

    try!(write_handler_trait(
        match side {
            Side::Client => &interface.events,
            Side::Server => &interface.requests
        },
        out,
        side
    ));

    try!(write_impl(
        match side {
            Side::Client => &interface.requests,
            Side::Server => &interface.events
        },
        out,
        &interface.name,
        side
    ));

    try!(writeln!(out, "}}"));
    Ok(())
}

fn write_handler_trait<O: Write>(messages: &[Message], out: &mut O, side: Side) -> IOResult<()> {
    try!(writeln!(out, "pub trait Handler {{"));
    for msg in messages {
        if let Some((ref short, ref long)) = msg.description {
            try!(write_doc(Some(short), long, false, out));
        }
        try!(write!(out, "fn {}{}(&mut self, evqh: &mut EventQueueHandle",
            if is_keyword(&msg.name) { "_" } else { "" }, msg.name
        ));
        for arg in &msg.args {
            try!(write!(out, ", {}{}: {}",
                if is_keyword(&arg.name) { "_" } else { "" }, arg.name,
                match arg.typ {
                    Type::Object => arg.interface.as_ref()
                                       .map(|s| format!("&super::{}::{}", s, snake_to_camel(s)))
                                       .unwrap_or(format!("*mut {}",side.object_ptr_type())),
                    Type::NewId => "u32".into(),
                    _ => arg.typ.rust_type().into()
                }
            ));
        }
        try!(writeln!(out, ");"));
    }
    try!(writeln!(out, "}}"));
    Ok(())
}

fn write_impl<O: Write>(messages: &[Message], out: &mut O, iname: &str, side: Side) -> IOResult<()> {
    try!(writeln!(out, "impl {} {{", snake_to_camel(iname)));
    for msg in messages {
        if let Some((ref short, ref long)) = msg.description {
            try!(write_doc(Some(short), long, false, out));
        }
        try!(write!(out, "pub fn {}{}(&self",
            if is_keyword(&msg.name) { "_" } else { "" }, msg.name
        ));
        for arg in &msg.args {
            try!(write!(out, ", {}{}: {}",
                if is_keyword(&arg.name) { "_" } else { "" }, arg.name,
                match arg.typ {
                    Type::Object => arg.interface.as_ref()
                                       .map(|s| format!("&super::{}::{}", s, snake_to_camel(s)))
                                       .unwrap_or(format!("*mut {}",side.object_ptr_type())),
                    Type::NewId => continue,
                    _ => arg.typ.rust_type().into()
                }
            ));
        }
        try!(write!(out, ")"));
        let mut has_newid = false;
        for arg in &msg.args {
            match arg.typ {
                Type::NewId => if has_newid {
                    panic!("Request {}.{} returns more than one new_id", iname, msg.name);
                } else {
                    has_newid = true;
                    try!(write!(out, "-> {}",
                        arg.interface.as_ref()
                            .map(|s| format!("super::{}::{}", s, snake_to_camel(s)))
                            .unwrap_or(format!("*mut {}", side.object_ptr_type())),
                    ));
                },
                _ => ()
            }
        }
        try!(writeln!(out, " {{"));
        try!(writeln!(out, "unimplemented!();"));
        try!(writeln!(out, "}}"));
    }
    try!(writeln!(out, "}}"));
    Ok(())
}

fn write_doc<O: Write>(short: Option<&str>, long: &str, internal: bool, out: &mut O) -> IOResult<()> {
    let p = if internal { '!' } else { '/' };
    if let Some(txt) = short {
        try!(writeln!(out, "//{} {}", p, txt));
        try!(writeln!(out, "//{}", p));
    }
    for l in long.lines() {
        try!(writeln!(out, "//{} {}", p, l.trim()));
    }
    Ok(())
}
<commit_msg>scanner: don't generate handler for wl_display<commit_after>use std::io::Write;
use std::io::Result as IOResult;

use util::*;
use protocol::*;
use Side;

pub fn write_protocol<O: Write>(protocol: Protocol, out: &mut O, side: Side) -> IOResult<()> {
    for iface in &protocol.interfaces {
        if (iface.name == "wl_display" || iface.name == "wl_registry") && side == Side::Server {
            continue
        }
        try!(write_interface(iface, out, side));
    }
    Ok(())
}

fn write_interface<O: Write>(interface: &Interface, out: &mut O, side: Side) -> IOResult<()> {
    try!(writeln!(out, "pub mod {} {{", interface.name));

    if let Some((ref short, ref long)) = interface.description {
        try!(write_doc(Some(short), long, true, out));
    }

    try!(writeln!(out, "use super::EventQueueHandle;"));
    try!(writeln!(out, "use super::{};", side.object_trait()));
    match side {
        Side::Client => try!(writeln!(out, "use wayland_sys::client::wl_proxy;")),
        Side::Server => try!(writeln!(out, "use wayland_sys::server::wl_resource;"))
    };

    try!(writeln!(out, "pub struct {} {{ ptr: *mut {} }}",
        snake_to_camel(&interface.name),
        side.object_ptr_type()
    ));

    // Generate object trait impl
    try!(writeln!(out, "impl {} for {} {{",
        side.object_trait(),
        snake_to_camel(&interface.name)
    ));
    try!(writeln!(out, "fn ptr(&self) -> *mut {} {{ self.ptr }}", side.object_ptr_type()));
    try!(writeln!(out, "unsafe fn from_ptr(ptr: *mut {0}) -> {1} {{ {1} {{ ptr: ptr }} }}",
        side.object_ptr_type(),
        snake_to_camel(&interface.name)
    ));
    try!(writeln!(out, "}}"));

    // client-side events of wl_display are handled by the lib
    if side != Side::Client || interface.name != "wl_display" {
        try!(write_handler_trait(
            match side {
                Side::Client => &interface.events,
                Side::Server => &interface.requests
            },
            out,
            side
        ));
    }

    try!(write_impl(
        match side {
            Side::Client => &interface.requests,
            Side::Server => &interface.events
        },
        out,
        &interface.name,
        side
    ));

    try!(writeln!(out, "}}"));
    Ok(())
}

fn write_handler_trait<O: Write>(messages: &[Message], out: &mut O, side: Side) -> IOResult<()> {
    try!(writeln!(out, "pub trait Handler {{"));
    for msg in messages {
        if let Some((ref short, ref long)) = msg.description {
            try!(write_doc(Some(short), long, false, out));
        }
        try!(write!(out, "fn {}{}(&mut self, evqh: &mut EventQueueHandle",
            if is_keyword(&msg.name) { "_" } else { "" }, msg.name
        ));
        for arg in &msg.args {
            try!(write!(out, ", {}{}: {}",
                if is_keyword(&arg.name) { "_" } else { "" }, arg.name,
                match arg.typ {
                    Type::Object => arg.interface.as_ref()
                                       .map(|s| format!("&super::{}::{}", s, snake_to_camel(s)))
                                       .unwrap_or(format!("*mut {}",side.object_ptr_type())),
                    Type::NewId => "u32".into(),
                    _ => arg.typ.rust_type().into()
                }
            ));
        }
        try!(writeln!(out, ");"));
    }
    try!(writeln!(out, "}}"));
    Ok(())
}

fn write_impl<O: Write>(messages: &[Message], out: &mut O, iname: &str, side: Side) -> IOResult<()> {
    try!(writeln!(out, "impl {} {{", snake_to_camel(iname)));
    for msg in messages {
        if let Some((ref short, ref long)) = msg.description {
            try!(write_doc(Some(short), long, false, out));
        }
        try!(write!(out, "pub fn {}{}(&self",
            if is_keyword(&msg.name) { "_" } else { "" }, msg.name
        ));
        for arg in &msg.args {
            try!(write!(out, ", {}{}: {}",
                if is_keyword(&arg.name) { "_" } else { "" }, arg.name,
                match arg.typ {
                    Type::Object => arg.interface.as_ref()
                                       .map(|s| format!("&super::{}::{}", s, snake_to_camel(s)))
                                       .unwrap_or(format!("*mut {}",side.object_ptr_type())),
                    Type::NewId => continue,
                    _ => arg.typ.rust_type().into()
                }
            ));
        }
        try!(write!(out, ")"));
        let mut has_newid = false;
        for arg in &msg.args {
            match arg.typ {
                Type::NewId => if has_newid {
                    panic!("Request {}.{} returns more than one new_id", iname, msg.name);
                } else {
                    has_newid = true;
                    try!(write!(out, "-> {}",
                        arg.interface.as_ref()
                            .map(|s| format!("super::{}::{}", s, snake_to_camel(s)))
                            .unwrap_or(format!("*mut {}", side.object_ptr_type())),
                    ));
                },
                _ => ()
            }
        }
        try!(writeln!(out, " {{"));
        try!(writeln!(out, "unimplemented!();"));
        try!(writeln!(out, "}}"));
    }
    try!(writeln!(out, "}}"));
    Ok(())
}

fn write_doc<O: Write>(short: Option<&str>, long: &str, internal: bool, out: &mut O) -> IOResult<()> {
    let p = if internal { '!' } else { '/' };
    if let Some(txt) = short {
        try!(writeln!(out, "//{} {}", p, txt));
        try!(writeln!(out, "//{}", p));
    }
    for l in long.lines() {
        try!(writeln!(out, "//{} {}", p, l.trim()));
    }
    Ok(())
}
<|endoftext|>