<commit_before>#![feature(slice_bytes,clone_from_slice)]

extern crate libc;
extern crate errno;
extern crate rustc_serialize; //debug hex dump

#[macro_use]
extern crate nom;

use std::io::Write;
use rustc_serialize::hex::ToHex;

const ETH_P_ALL: u16 = 0x0003;
const ETH_P_IP: u16 = 0x0800;
const ETH_P_ARP: u16 = 0x0806;
const IFNAMSIZ: usize = 16; // net/if.h
const SIOCGIFINDEX: libc::c_ulong = 0x8933;
const RECV_BUF_LEN: usize = 1522;
const SO_ATTACH_FILTER: libc::c_int = 26;

#[repr(C)]
#[allow(non_camel_case_types)]
struct sock_filter { /* Filter block */
    code: u16,       /* Actual filter code */
    jt: u8,          /* Jump true */
    jf: u8,          /* Jump false */
    k: u32           /* Generic multiuse field */
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct sock_fprog {     /* Required for SO_ATTACH_FILTER. */
    len: u16,           /* Number of filter blocks */
    filter: *const sock_filter
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct ifreq {
    ifr_name: [u8; IFNAMSIZ],
    ifr_ifindex: libc::c_int
}

#[derive(Debug)]
struct EthernetFrame {
    dst: MacAddr,
    src: MacAddr,
    tag: Option<VlanTag>,
    tysize: EthernetTypeSize,
    payload: EthernetPayload,
}

#[derive(Debug)]
struct MacAddr([u8; 6]);
impl MacAddr {
    fn from_slice(s: &[u8]) -> MacAddr {
        // bit ugly, is there a better way?
        let mut m = MacAddr([0u8; 6]);
        m.0.clone_from_slice(s);
        return m;
    }
}

impl std::fmt::Display for MacAddr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
               self.0[0], self.0[1], self.0[2],
               self.0[3], self.0[4], self.0[5])
    }
}

#[derive(Debug)]
struct VlanTag(u16);

#[derive(Debug)]
struct IpAddr(u32);

impl std::fmt::Display for IpAddr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}.{}.{}.{}",
               (self.0 >> 24) & 0xff, (self.0 >> 16) & 0xff,
               (self.0 >> 8) & 0xff, self.0 & 0xff)
    }
}

#[derive(Debug)]
#[repr(u16)]
enum EtherType {
    Arp = ETH_P_ARP,
    _Ip = ETH_P_IP
}

#[derive(Debug)]
enum EthernetTypeSize {
    Size(u16),
    Type(EtherType),
}

#[derive(Debug)]
enum EthernetPayload {
    Arp(ArpPacket),
}

#[derive(Debug)]
struct ArpPacket {
    hwtype: u16,
    proto: u16,
    hwsize: u8,
    protosize: u8,
    opcode: u16,
    sender_mac: MacAddr,
    sender_ip: IpAddr,
    target_mac: MacAddr,
    target_ip: IpAddr
}

struct SocketError {
    action: &'static str,
    err: errno::Errno
}

macro_rules! sockerr {
    ($action:expr, $res:expr) => {
        if $res == -1 {
            return Err(SocketError{ action: $action, err: errno::errno() });
        }
    };
}

fn ifindex_from_ifname(ifname: &str, sock: libc::c_int) -> Result<libc::c_int, SocketError> {
    let mut ifr = ifreq {
        ifr_name: [0; IFNAMSIZ],
        ifr_ifindex: 0
    };
    std::slice::bytes::copy_memory(ifname.as_bytes(), &mut ifr.ifr_name);
    let res = unsafe { libc::ioctl(sock, SIOCGIFINDEX, &ifr) };
    sockerr!("getting ifindex", res);
    return Ok(ifr.ifr_ifindex);
}

fn create_listen_socket(iface: &str) -> Result<libc::c_int, SocketError> {
    // tcpdump -dd arp and incoming
    let bpf_filter_arp_incoming: [sock_filter; 6] = [
        sock_filter { code: 0x28, jt: 0, jf: 0, k: 0x0000000c },
        sock_filter { code: 0x15, jt: 0, jf: 3, k: 0x00000806 },
        sock_filter { code: 0x28, jt: 0, jf: 0, k: 0xfffff004 },
        sock_filter { code: 0x15, jt: 1, jf: 0, k: 0x00000004 },
        sock_filter { code: 0x06, jt: 0, jf: 0, k: 0x00040000 },
        sock_filter { code: 0x06, jt: 0, jf: 0, k: 0x00000000 },
    ];
    let bpf_filter_arp_incoming_prog: sock_fprog = sock_fprog {
        len: 6,
        filter: bpf_filter_arp_incoming.as_ptr()
    };

    let listen_socket = unsafe {
        libc::socket(libc::AF_PACKET, libc::SOCK_RAW, ETH_P_ALL.to_be() as i32)
    };
    sockerr!("opening socket", listen_socket);
    let attach_filter_res = unsafe {
        libc::setsockopt(
            listen_socket,
            libc::SOL_SOCKET,
            SO_ATTACH_FILTER,
            std::mem::transmute(&bpf_filter_arp_incoming_prog),
            std::mem::size_of_val(&bpf_filter_arp_incoming_prog) as u32
        )
    };
    sockerr!("attaching filter", attach_filter_res);

    let ifindex = try!(ifindex_from_ifname(&iface, listen_socket));

    let listen_sockaddr = libc::sockaddr_ll {
        sll_family: libc::AF_PACKET as u16,
        sll_protocol: ETH_P_ALL.to_be(),
        sll_ifindex: ifindex,
        sll_hatype: 0,
        sll_pkttype: 0,
        sll_halen: 0,
        sll_addr: [0; 8]
    };
    let bind_result = unsafe {
        libc::bind(
            listen_socket,
            std::mem::transmute(&listen_sockaddr),
            std::mem::size_of_val(&listen_sockaddr) as u32
        )
    };
    sockerr!("binding socket", bind_result);
    return Ok(listen_socket);
}

fn u16_to_be_bytes(i: u16) -> [u8;2] {
    [((i>>8) & 0xff) as u8, (i & 0xff) as u8]
}

named!(arp_packet_parser(&[u8]) -> ArpPacket, chain!(
    hwtype: call!(nom::be_u16)
    ~ proto: call!(nom::be_u16)
    ~ hwsize: call!(nom::be_u8)
    ~ protosize: call!(nom::be_u8)
    ~ opcode: call!(nom::be_u16)
    ~ sender_mac: take!(6)
    ~ sender_ip: call!(nom::be_u32)
    ~ target_mac: take!(6)
    ~ target_ip: call!(nom::be_u32),
    || {
        ArpPacket {
            hwtype: hwtype,
            proto: proto,
            hwsize: hwsize,
            protosize: protosize,
            opcode: opcode,
            sender_mac: MacAddr::from_slice(sender_mac),
            sender_ip: IpAddr(sender_ip),
            target_mac: MacAddr::from_slice(target_mac),
            target_ip: IpAddr(target_ip)
        }
    })
);

fn tysize_parser(input: &[u8]) -> nom::IResult<&[u8], EthernetTypeSize> {
    match nom::be_u16(input) {
        nom::IResult::Done(i,tysize) => {
            if tysize == ETH_P_ARP {
                nom::IResult::Done(i,EthernetTypeSize::Type(EtherType::Arp))
            } else if tysize < 0x0600 {
                nom::IResult::Done(i,EthernetTypeSize::Size(tysize))
            } else {
                //TODO better error code
                nom::IResult::Error(nom::Err::Code(nom::ErrorKind::Custom(1)))
            }
        },
        nom::IResult::Error(err) => nom::IResult::Error(err),
        nom::IResult::Incomplete(need) => nom::IResult::Incomplete(need)
    }
}

named!(eth_frame_parser(&[u8]) -> EthernetFrame, chain!(
    dst: take!(6)
    ~ src: take!(6)
    ~ tag: opt!(
        chain!(
            _tpid: tag!(u16_to_be_bytes(0x8100))
            ~ tci: call!(nom::be_u16),
            || {
                VlanTag(tci)
            }
        )
    )
    ~ tysize: call!(tysize_parser)
    ~ payload: call!(arp_packet_parser),
    || {
        EthernetFrame {
            dst: MacAddr::from_slice(dst),
            src: MacAddr::from_slice(src),
            tag: tag,
            tysize: tysize,
            payload: EthernetPayload::Arp(payload)
        }
    })
);

fn handle_arp_request(arp_packet: &ArpPacket) {
    println!("request from {} ({}) who-has {}?", arp_packet.sender_mac, arp_packet.sender_ip, arp_packet.target_ip);
}

fn handle_arp_response(arp_packet: &ArpPacket) {
    println!("response from {} is-at {}", arp_packet.sender_ip, arp_packet.sender_mac);
}

fn handle_arp_packet(eth_frame: &EthernetFrame) {
    let EthernetPayload::Arp(ref arp_packet) = eth_frame.payload;
    if arp_packet.hwtype == 1
        && arp_packet.proto == ETH_P_IP
        && arp_packet.hwsize == 6
        && arp_packet.protosize == 4
    {
        if arp_packet.opcode == 1 { //request
            handle_arp_request(arp_packet)
        } else if arp_packet.opcode == 2 { //response
            handle_arp_response(arp_packet)
        }
    }
}

fn do_recv(listen_socket: libc::c_int) -> SocketError {
    let buf = [0u8; RECV_BUF_LEN];
    let mut recv_sockaddr = unsafe { std::mem::zeroed::<libc::sockaddr_ll>() };
    let mut recv_sockaddr_len: u32 = std::mem::size_of_val(&recv_sockaddr) as u32;
    loop {
        let recv_result = unsafe {
            libc::recvfrom(
                listen_socket,
                std::mem::transmute(&buf),
                RECV_BUF_LEN,
                0,
                std::mem::transmute(&recv_sockaddr),
                &mut recv_sockaddr_len
            )
        };
        if recv_result == -1 {
            return SocketError { action: "recvfrom", err: errno::errno() };
        }
        // rust has no offsetof yet so we hardcode 12 here instead
        // println!("From: {}", recv_sockaddr.sll_addr[0..(recv_sockaddr_len as usize -12)].to_hex());
        //TODO minimum arp packet length?
        if recv_result >= 42 {
            match eth_frame_parser(&buf[0..(recv_result as usize)]) {
                nom::IResult::Done(_i, frame) => handle_arp_packet(&frame),
                nom::IResult::Error(err) => println!("Parser error: {:?}", err),
                nom::IResult::Incomplete(needed) => println!("Parser incomplete: {:?}", needed)
            }
        } else {
            println!("Too short frame ({}): {}", recv_result, buf[0..(recv_result as usize)].to_hex());
        }
    }
}

fn main_wrapper() -> i32 {
    let mut args = std::env::args();
    if args.len() != 2 {
        std::io::stderr().write("Usage: arpmasqd LISTEN_IFACE\n".as_ref()).unwrap();
        std::io::stderr().write("Error: Missing LISTEN_IFACE\n".as_ref()).unwrap();
        return 1;
    }
    args.next();
    //unwrap is ok because arg count has been checked
    let listen_iface = args.next().unwrap();
    let err = match create_listen_socket(&listen_iface) {
        Ok(sock) => do_recv(sock),
        Err(err) => err
    };
    std::io::stderr().write(format!("Error {}: {} ({})\n", err.action, err.err, err.err.0).as_ref()).unwrap();
    return 1;
}

fn main() {
    std::process::exit(main_wrapper());
}
<commit_msg>left/right side handling<commit_after>#![feature(slice_bytes,clone_from_slice)]

extern crate libc;
extern crate errno;
extern crate rustc_serialize; //debug hex dump

#[macro_use]
extern crate nom;

use std::io::Write;
use rustc_serialize::hex::ToHex;

const ETH_P_ALL: u16 = 0x0003;
const ETH_P_IP: u16 = 0x0800;
const ETH_P_ARP: u16 = 0x0806;
const IFNAMSIZ: usize = 16; // net/if.h
const SIOCGIFINDEX: libc::c_ulong = 0x8933;
const RECV_BUF_LEN: usize = 1522;
const SO_ATTACH_FILTER: libc::c_int = 26;

#[repr(C)]
#[allow(non_camel_case_types)]
struct sock_filter { /* Filter block */
    code: u16,       /* Actual filter code */
    jt: u8,          /* Jump true */
    jf: u8,          /* Jump false */
    k: u32           /* Generic multiuse field */
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct sock_fprog {     /* Required for SO_ATTACH_FILTER. */
    len: u16,           /* Number of filter blocks */
    filter: *const sock_filter
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct ifreq {
    ifr_name: [u8; IFNAMSIZ],
    ifr_ifindex: libc::c_int
}

#[derive(Debug)]
struct EthernetFrame {
    dst: MacAddr,
    src: MacAddr,
    tag: Option<VlanTag>,
    tysize: EthernetTypeSize,
    payload: EthernetPayload,
}

#[derive(Debug)]
struct MacAddr([u8; 6]);
impl MacAddr {
    fn from_slice(s: &[u8]) -> MacAddr {
        // bit ugly, is there a better way?
        let mut m = MacAddr([0u8; 6]);
        m.0.clone_from_slice(s);
        return m;
    }
}

impl std::fmt::Display for MacAddr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
               self.0[0], self.0[1], self.0[2],
               self.0[3], self.0[4], self.0[5])
    }
}

#[derive(Debug)]
struct VlanTag(u16);

#[derive(Debug)]
struct IpAddr(u32);

impl std::fmt::Display for IpAddr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}.{}.{}.{}",
               (self.0 >> 24) & 0xff, (self.0 >> 16) & 0xff,
               (self.0 >> 8) & 0xff, self.0 & 0xff)
    }
}

#[derive(Debug)]
#[repr(u16)]
enum EtherType {
    Arp = ETH_P_ARP,
    _Ip = ETH_P_IP
}

#[derive(Debug)]
enum EthernetTypeSize {
    Size(u16),
    Type(EtherType),
}

#[derive(Debug)]
enum EthernetPayload {
    Arp(ArpPacket),
}

#[derive(Debug)]
struct ArpPacket {
    hwtype: u16,
    proto: u16,
    hwsize: u8,
    protosize: u8,
    opcode: u16,
    sender_mac: MacAddr,
    sender_ip: IpAddr,
    target_mac: MacAddr,
    target_ip: IpAddr
}

struct SocketError {
    action: &'static str,
    err: errno::Errno
}

macro_rules! sockerr {
    ($action:expr, $res:expr) => {
        if $res == -1 {
            return Err(SocketError{ action: $action, err: errno::errno() });
        }
    };
}

fn ifindex_from_ifname(ifname: &str, sock: libc::c_int) -> Result<libc::c_int, SocketError> {
    let mut ifr = ifreq {
        ifr_name: [0; IFNAMSIZ],
        ifr_ifindex: 0
    };
    std::slice::bytes::copy_memory(ifname.as_bytes(), &mut ifr.ifr_name);
    let res = unsafe { libc::ioctl(sock, SIOCGIFINDEX, &ifr) };
    sockerr!("getting ifindex", res);
    return Ok(ifr.ifr_ifindex);
}

fn create_listen_socket(iface: &str) -> Result<libc::c_int, SocketError> {
    // tcpdump -dd arp and incoming
    let bpf_filter_arp_incoming: [sock_filter; 6] = [
        sock_filter { code: 0x28, jt: 0, jf: 0, k: 0x0000000c },
        sock_filter { code: 0x15, jt: 0, jf: 3, k: 0x00000806 },
        sock_filter { code: 0x28, jt: 0, jf: 0, k: 0xfffff004 },
        sock_filter { code: 0x15, jt: 1, jf: 0, k: 0x00000004 },
        sock_filter { code: 0x06, jt: 0, jf: 0, k: 0x00040000 },
        sock_filter { code: 0x06, jt: 0, jf: 0, k: 0x00000000 },
    ];
    let bpf_filter_arp_incoming_prog: sock_fprog = sock_fprog {
        len: 6,
        filter: bpf_filter_arp_incoming.as_ptr()
    };

    let listen_socket = unsafe {
        libc::socket(libc::AF_PACKET, libc::SOCK_RAW, ETH_P_ALL.to_be() as i32)
    };
    sockerr!("opening socket", listen_socket);
    let attach_filter_res = unsafe {
        libc::setsockopt(
            listen_socket,
            libc::SOL_SOCKET,
            SO_ATTACH_FILTER,
            std::mem::transmute(&bpf_filter_arp_incoming_prog),
            std::mem::size_of_val(&bpf_filter_arp_incoming_prog) as u32
        )
    };
    sockerr!("attaching filter", attach_filter_res);

    let ifindex = try!(ifindex_from_ifname(&iface, listen_socket));

    let listen_sockaddr = libc::sockaddr_ll {
        sll_family: libc::AF_PACKET as u16,
        sll_protocol: ETH_P_ALL.to_be(),
        sll_ifindex: ifindex,
        sll_hatype: 0,
        sll_pkttype: 0,
        sll_halen: 0,
        sll_addr: [0; 8]
    };
    let bind_result = unsafe {
        libc::bind(
            listen_socket,
            std::mem::transmute(&listen_sockaddr),
            std::mem::size_of_val(&listen_sockaddr) as u32
        )
    };
    sockerr!("binding socket", bind_result);
    return Ok(listen_socket);
}

fn u16_to_be_bytes(i: u16) -> [u8;2] {
    [((i>>8) & 0xff) as u8, (i & 0xff) as u8]
}

named!(arp_packet_parser(&[u8]) -> ArpPacket, chain!(
    hwtype: call!(nom::be_u16)
    ~ proto: call!(nom::be_u16)
    ~ hwsize: call!(nom::be_u8)
    ~ protosize: call!(nom::be_u8)
    ~ opcode: call!(nom::be_u16)
    ~ sender_mac: take!(6)
    ~ sender_ip: call!(nom::be_u32)
    ~ target_mac: take!(6)
    ~ target_ip: call!(nom::be_u32),
    || {
        ArpPacket {
            hwtype: hwtype,
            proto: proto,
            hwsize: hwsize,
            protosize: protosize,
            opcode: opcode,
            sender_mac: MacAddr::from_slice(sender_mac),
            sender_ip: IpAddr(sender_ip),
            target_mac: MacAddr::from_slice(target_mac),
            target_ip: IpAddr(target_ip)
        }
    })
);

fn tysize_parser(input: &[u8]) -> nom::IResult<&[u8], EthernetTypeSize> {
    match nom::be_u16(input) {
        nom::IResult::Done(i,tysize) => {
            if tysize == ETH_P_ARP {
                nom::IResult::Done(i,EthernetTypeSize::Type(EtherType::Arp))
            } else if tysize < 0x0600 {
                nom::IResult::Done(i,EthernetTypeSize::Size(tysize))
            } else {
                //TODO better error code
                nom::IResult::Error(nom::Err::Code(nom::ErrorKind::Custom(1)))
            }
        },
        nom::IResult::Error(err) => nom::IResult::Error(err),
        nom::IResult::Incomplete(need) => nom::IResult::Incomplete(need)
    }
}

named!(eth_frame_parser(&[u8]) -> EthernetFrame, chain!(
    dst: take!(6)
    ~ src: take!(6)
    ~ tag: opt!(
        chain!(
            _tpid: tag!(u16_to_be_bytes(0x8100))
            ~ tci: call!(nom::be_u16),
            || {
                VlanTag(tci)
            }
        )
    )
    ~ tysize: call!(tysize_parser)
    ~ payload: call!(arp_packet_parser),
    || {
        EthernetFrame {
            dst: MacAddr::from_slice(dst),
            src: MacAddr::from_slice(src),
            tag: tag,
            tysize: tysize,
            payload: EthernetPayload::Arp(payload)
        }
    })
);

fn handle_arp_request(arp_packet: &ArpPacket) {
    println!("request from {} ({}) who-has {}?", arp_packet.sender_mac, arp_packet.sender_ip, arp_packet.target_ip);
}

fn handle_arp_response(arp_packet: &ArpPacket) {
    println!("response from {} is-at {}", arp_packet.sender_ip, arp_packet.sender_mac);
}

fn handle_arp_from_right(eth_frame: &EthernetFrame) {
    let EthernetPayload::Arp(ref arp_packet) = eth_frame.payload;
    if arp_packet.hwtype == 1
        && arp_packet.proto == ETH_P_IP
        && arp_packet.hwsize == 6
        && arp_packet.protosize == 4
    {
        if arp_packet.opcode == 1 { //request
            handle_arp_request(arp_packet)
        } else if arp_packet.opcode == 2 { //response
            handle_arp_response(arp_packet)
        }
    }
}

fn handle_arp_from_left(eth_frame: &EthernetFrame) {
    let EthernetPayload::Arp(ref arp_packet) = eth_frame.payload;
    if arp_packet.hwtype == 1
        && arp_packet.proto == ETH_P_IP
        && arp_packet.hwsize == 6
        && arp_packet.protosize == 4
    {
        if arp_packet.opcode == 1 { //request
            handle_arp_request(arp_packet)
        } else if arp_packet.opcode == 2 { //response
            handle_arp_response(arp_packet)
        }
    }
}

fn do_recv(listen_socket: libc::c_int, ctrl_pipe: libc::c_int, handle_arp_packet: fn(&EthernetFrame)) -> SocketError {
    let buf = [0u8; RECV_BUF_LEN];
    let mut recv_sockaddr = unsafe { std::mem::zeroed::<libc::sockaddr_ll>() };
    let mut recv_sockaddr_len: u32 = std::mem::size_of_val(&recv_sockaddr) as u32;
    loop {
        let recv_result = unsafe {
            // use poll/select
            libc::recvfrom(
                listen_socket,
                std::mem::transmute(&buf),
                RECV_BUF_LEN,
                0,
                std::mem::transmute(&recv_sockaddr),
                &mut recv_sockaddr_len
            )
        };
        if recv_result == -1 {
            return SocketError { action: "recvfrom", err: errno::errno() };
        }
        // rust has no offsetof yet so we hardcode 12 here instead
        // println!("From: {}", recv_sockaddr.sll_addr[0..(recv_sockaddr_len as usize -12)].to_hex());
        //TODO minimum arp packet length?
        if recv_result >= 42 {
            match eth_frame_parser(&buf[0..(recv_result as usize)]) {
                nom::IResult::Done(_i, frame) => handle_arp_packet(&frame),
                nom::IResult::Error(err) => println!("Parser error: {:?}", err),
                nom::IResult::Incomplete(needed) => println!("Parser incomplete: {:?}", needed)
            }
        } else {
            println!("Too short frame ({}): {}", recv_result, buf[0..(recv_result as usize)].to_hex());
        }
    }
}

struct Worker<T> {
    ctrl_pipe: libc::c_int,
    thread: std::thread::JoinHandle<T>,
}

fn create_worker(iface: &str, barrier: &std::sync::Arc<std::sync::Barrier>, callback: fn(&EthernetFrame)) -> Result<Worker<SocketError>, SocketError> {
    let barrier = barrier.clone();
    let pipes: [libc::c_int; 2] = [0; 2];
    unsafe { libc::pipe(std::mem::transmute(&pipes)) };
    let read_pipe = pipes[0];
    let write_pipe = pipes[1];
    let thread = match create_listen_socket(&iface) {
        Ok(sock) => std::thread::spawn(move || { let ret = do_recv(sock, read_pipe, callback); barrier.wait(); ret }),
        Err(err) => {
            std::io::stderr().write(format!("Error {}: {} ({})\n", err.action, err.err, err.err.0).as_ref()).unwrap();
            return Err(err);
        }
    };
    return Ok(Worker { ctrl_pipe: write_pipe, thread: thread })
}

fn main_wrapper() -> i32 {
    let mut args = std::env::args();
    if args.len() != 3 {
        std::io::stderr().write("Usage: arpmasqd LEFT_IFACE RIGHT_IFACE\n".as_ref()).unwrap();
        return 1;
    }
    args.next();
    let barrier = std::sync::Arc::new(std::sync::Barrier::new(2));
    //unwrap is ok because arg count has been checked
    let left_iface = args.next().unwrap();
    let left_worker = match create_worker(&left_iface, &barrier, handle_arp_from_left) {
        Ok(worker) => worker,
        Err(err) => panic!("TODO")
    };
    let right_iface = args.next().unwrap();
    let right_worker = match create_worker(&right_iface, &barrier, handle_arp_from_right) {
        Ok(worker) => worker,
        Err(err) => panic!("TODO")
    };

    barrier.wait();
    unsafe {
        libc::write(left_worker.ctrl_pipe, std::mem::transmute(&'\0'), 1);
        libc::write(right_worker.ctrl_pipe, std::mem::transmute(&'\0'), 1);
    }

    match left_worker.thread.join() {
        Ok(err) => {
            std::io::stderr().write(format!("Error {}: {} ({})\n", err.action, err.err, err.err.0).as_ref()).unwrap();
        },
        Err(err) => {
            std::io::stderr().write(format!("Panic {:?}\n", err).as_ref()).unwrap();
        }
    }
    match right_worker.thread.join() {
        Ok(err) => {
            std::io::stderr().write(format!("Error {}: {} ({})\n", err.action, err.err, err.err.0).as_ref()).unwrap();
        },
        Err(err) => {
            std::io::stderr().write(format!("Panic {:?}\n", err).as_ref()).unwrap();
        }
    }
    return 1;
}

fn main() {
    std::process::exit(main_wrapper());
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::fs::File;
use std::error::Error;
use std::env;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Lines;

use CommandType::{ACommand, CCommand, LCommand};

enum CommandType {
	ACommand,
	CCommand,
	LCommand,
}

struct Parser {
	input_lines: Lines<BufReader<File>>,
	current_command: String,
}

impl Parser {
	/// Opens the input file/stream and gets ready to parse it.
	fn new(filename: &str) -> Parser {
		let path = Path::new(filename);
		let file = match File::open(&path) {
			Err(why) => panic!("couldn't open {}: {}", path.display(), Error::description(&why)),
			Ok(file) => file,
		};

		let lines = BufReader::new(file).lines(); // iterator
		Parser {
			input_lines: lines,
			current_command: String::new(),
		}
	}

	/// Encapsulates has_more_command and avance method from the proposed API.
	///
	///		Are there any more commands in the input?
	///			Reads the next command from the input and makes it the current command
	fn parse(&mut self) {
		loop {
			match self.input_lines.next() {
				Some(line) => {
					self.current_command = line.unwrap().trim().to_string();
					match self.command_type() {
						ACommand => {println!("ACommand: {0}", self.current_command);},
						CCommand => {println!("CCommand: {0}", self.current_command);},
						LCommand => {println!("LCommand: {0}", self.current_command);},
					}
				},
				None => break,
			}
		}
	}

	/// Returns the type of the current command.
	///
	/// * ACommand: For @xxx where xxx is either a symbol or a decimal number
	/// * CCommand: For dest=comp;jump
	/// * LCommand: Pseudo-Command. for (xxx) where xxx is a symbol
	fn command_type(&self) -> CommandType {
		ACommand
	}
}

fn main() {
	let args: Vec<String> = env::args().collect();
	if args.len() != 2 {
		println!("HACK Assembler. Translates assembly (mnemonic) into binary machine code.\n\nUsage:\n\tassembler [PATH_TO_ASM_FILE]");
		return;
	}
	let path_to_asm = &args[1];
	let mut parser = Parser::new(path_to_asm);
	parser.parse();
}
<commit_msg>Command_type is implemented. Returns the correct type of the current command<commit_after>use std::path::Path;
use std::fs::File;
use std::error::Error;
use std::env;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Lines;

use CommandType::{ACommand, CCommand, LCommand};

enum CommandType {
	ACommand,
	CCommand,
	LCommand,
}

struct Parser {
	input_lines: Lines<BufReader<File>>,
	current_command: String,
}

impl Parser {
	/// Opens the input file/stream and gets ready to parse it.
	fn new(filename: &str) -> Parser {
		let path = Path::new(filename);
		let file = match File::open(&path) {
			Err(why) => panic!("couldn't open {}: {}", path.display(), Error::description(&why)),
			Ok(file) => file,
		};

		let lines = BufReader::new(file).lines(); // iterator
		Parser {
			input_lines: lines,
			current_command: String::new(),
		}
	}

	/// Encapsulates has_more_command and avance method from the proposed API.
	///
	///		Are there any more commands in the input?
	///			Reads the next command from the input and makes it the current command
	fn parse(&mut self) {
		loop {
			match self.input_lines.next() {
				Some(line) => {
					let content = line.unwrap().trim().to_string();

					// Ignore comments and empty lines
					if content.starts_with("//") || content == "\n" || content == "" {
						continue
					}

					self.current_command = content;
					match self.command_type() {
						ACommand => {println!("ACommand: {0}", self.current_command);},
						CCommand => {println!("CCommand: {0}", self.current_command);},
						LCommand => {println!("LCommand: {0}", self.current_command);},
					}
				},
				None => break,
			}
		}
	}

	/// Returns the type of the current command.
	///
	/// * ACommand: For @xxx where xxx is either a symbol or a decimal number
	/// * CCommand: For dest=comp;jump
	/// * LCommand: Pseudo-Command. for (xxx) where xxx is a symbol
	fn command_type(&self) -> CommandType {
		if self.current_command.starts_with("@") {
			return ACommand
		} else if self.current_command.starts_with("(") {
			return LCommand
		}
		CCommand
	}
}

fn main() {
	let args: Vec<String> = env::args().collect();
	if args.len() != 2 {
		println!("HACK Assembler. Translates assembly (mnemonic) into binary machine code.\n\nUsage:\n\tassembler [PATH_TO_ASM_FILE]");
		return;
	}
	let path_to_asm = &args[1];
	let mut parser = Parser::new(path_to_asm);
	parser.parse();
}
<|endoftext|>


<commit_before>extern crate filetime;
extern crate rustc_serialize;
extern crate crypto;

use std::path::Path;
use std::collections::HashMap;

mod metadata;
mod workingdirectory;
mod store;

fn main() {
	let mut args = std::env::args();
	match args.nth(1) {
		Some(option) => dispatch_option(&option),
		None => println!("No option")
	}
}

fn dispatch_option(option: &str) {
	match option {
		"new" => new_repo(),
		"update" => update(),
		"commit" => commit(),
		_ => println!("Unknown option {}", option)
	}
}

fn new_repo() {
	println!("Creation of a new repo");
	let json_path = Path::new("metadata.json");
	metadata::create_emty_metadata_file(json_path);
}

fn update() {
	let data_path = Path::new("data");
	let json_path = Path::new("metadata.json");
	let store_path = Path::new("store");

	let wd_hierarchy : HashMap<String, model::MetaData> = workingdirectory::read_working_directory(data_path);
	println!("{} files in the working directory", wd_hierarchy.len());

	let mt_hierarchy = metadata::read_metadata_file(json_path);
	println!("{} files in the metadata", mt_hierarchy.get_number_of_files());

	let file_top_update = files_to_update(wd_hierarchy, &mt_hierarchy);

	for (filename, metadata) in file_top_update.iter() {
		store::extract_file(store_path, &metadata.get_hash(), data_path, filename, metadata.get_timestamp());
	}

}

fn files_to_update(wd_hierarchy: HashMap<String, model::MetaData>, mt_hierarchy: &model::Hierarchy) -> HashMap<String, model::MetaData>  {
	let mut file_to_update : HashMap<String, model::MetaData> = HashMap::new();

	for (filename, metadataset) in mt_hierarchy.get_files().iter() {
		let wd_metadata = wd_hierarchy.get(filename);

		match wd_metadata {
			Some(x) => {
				let wd_timestamp = x.get_timestamp();
				let last_md_timestamp = metadataset.get_last().unwrap().get_timestamp();

				if wd_timestamp == last_md_timestamp {
					println!("- No need to update because timestamps are equal {}", filename);
				} else if metadataset.has_metadata_with_timestamp(wd_timestamp) {
					println!("- Existing file to update {}", filename);
					file_to_update.insert(filename.clone(), metadataset.get_last().unwrap().clone());
				} else { //timestamp unknown
					if wd_timestamp > last_md_timestamp {
						println!("- Working directory file is more recent {}", filename);
					} else {
						print!(" - No !! Working directory file is older. It makes no sense {}", filename);
					}
				}
			    ()},
			None => {
				println!("- New file to update {}", filename);
			    file_to_update.insert(filename.clone(), metadataset.get_last().unwrap().clone());
			    () }
		}
	}

	file_to_update
}

fn commit() {
	let data_path = Path::new("data");
	let json_path = Path::new("metadata.json");
	let store_path = Path::new("store");

	let wd_hierarchy : HashMap<String, model::MetaData> = workingdirectory::read_working_directory(data_path);
	println!("{} files in the working directory", wd_hierarchy.len());

	let mt_hierarchy = metadata::read_metadata_file(json_path);
	println!("{} files in the metadata", mt_hierarchy.get_number_of_files());

	let files_to_commit = files_to_commit(wd_hierarchy, &mt_hierarchy);
	println!("{} files to commit", files_to_commit.len());

	let mut updated_metadata : HashMap<String, model::MetaData> = HashMap::new();
	for (filename, mut metadata) in files_to_commit {
		let hash = store::store_file(store_path, Path::new(&filename));
		metadata.add_hash(hash);

		updated_metadata.insert(filename, metadata);
	}
	let updated_metadata = updated_metadata;

	let mut mt_hierarchy = mt_hierarchy;
	mt_hierarchy.update(updated_metadata);

	metadata::write_metadata_file(json_path, mt_hierarchy);
}

fn files_to_commit(wd_hierarchy: HashMap<String, model::MetaData>, mt_hierarchy: &model::Hierarchy) -> HashMap<String, model::MetaData>  {
	let mut files_to_commit: HashMap<String, model::MetaData> = HashMap::new();

	for (filename, metadata) in wd_hierarchy.iter() {
		let actual_metadata = mt_hierarchy.get_latest_meta_data(&filename);

		match actual_metadata {
			Some(x) => {
				if x.is_more_recent(&metadata) {
					println!("- File to update {}", filename);
					files_to_commit.insert(filename.clone(), metadata.clone());
				} else {
					println!("- No need to update {}", filename);
				}
			    ()},
			None => {
				println!("- New file {}", filename);
			    files_to_commit.insert(filename.clone(), metadata.clone());
			    () }
		}
	}

	files_to_commit
}

mod model {

	use std::collections::HashMap;

	#[derive(Debug, RustcEncodable, RustcDecodable)]
	pub struct Hierarchy {
		nb_revision: i32,
	    files: HashMap<String, MetaDataSet>
	}

	#[derive(Debug, RustcEncodable, RustcDecodable)]
	pub struct MetaDataSet {
	    metadata: Vec<MetaData>
	}

	#[derive(Debug, RustcEncodable, RustcDecodable, Clone)]
	pub struct MetaData {
	   timestamp: u64,
	   size: u64,
	   hash: String,
	   stored_hash: String
	}

	impl Hierarchy {
		pub fn new_empty() -> Hierarchy {
			let empty_hierarchy_map : HashMap<String, MetaDataSet> = HashMap::new();
			Hierarchy {nb_revision: 1, files: empty_hierarchy_map}
		}
		pub fn get_number_of_files(&self) -> usize {
			self.files.len()
		}
		pub fn get_latest_meta_data(&self, filename: &String) -> Option<&MetaData> {
			match self.files.get(filename) {
				Some(x) => x.get_last(),
				None => None
			}
		}
		pub fn update(&mut self, new_metadata_map: HashMap<String, MetaData>) {
			self.nb_revision = self.nb_revision + 1;

			for (filename, metadata) in new_metadata_map {

				let new_metadata = self.new_metadata(&filename);

				if new_metadata {
					self.files.insert(filename, MetaDataSet::new_simple_meta_data_set(metadata));
				} else {
					let mut m = self.files.get_mut(&filename).unwrap();
					m.add_revision(metadata);
				}
			}
		}

		fn new_metadata(&self, filename: &String) -> bool {
			let actual_metadata = self.files.get(filename);

			match actual_metadata {
				Some(_) => false,
				None => true
			}
		}

		pub fn get_files(&self) -> &HashMap<String, MetaDataSet> {
			&self.files
		}
	}

	impl MetaDataSet {
		pub fn get_last(&self) -> Option<&MetaData> {
			self.metadata.last()
		}
		pub fn new_simple_meta_data_set(m: MetaData) -> MetaDataSet {
			let mut v: Vec<MetaData> = Vec::new();
			v.push(m);
			MetaDataSet {metadata: v }
		}
		pub fn add_revision(&mut self, m: MetaData) {
			self.metadata.push(m);
		}
		pub fn has_metadata_with_timestamp(&self, timestamp: u64) -> bool {
			let mut it = self.metadata.iter();
			match it.find(|&m| m.get_timestamp() == timestamp) {
				Some(_) => true,
				None => false
			}
		}
	}

	impl MetaData {
		pub fn new_without_hash(timestamp: u64, size: u64) -> MetaData {
			MetaData {timestamp: timestamp, size: size, hash: "".to_string(), stored_hash: "".to_string()}
		}
		pub fn add_hash(&mut self, hash: String) {
			self.hash = hash;
		}
		pub fn is_more_recent(&self, other: &MetaData) -> bool {
			self.timestamp < other.timestamp
		}
		pub fn get_timestamp(&self) -> u64 {
			self.timestamp
		}
		pub fn get_hash(&self) -> String {
			self.hash.clone()
		}
	}

}<commit_msg>first draft of remote update<commit_after>extern crate filetime;
extern crate rustc_serialize;
extern crate crypto;

use std::path::Path;
use std::collections::HashMap;

mod metadata;
mod workingdirectory;
mod store;

fn main() {
	let mut args = std::env::args();
	match args.nth(1) {
		Some(option) => dispatch_option(&option),
		None => println!("No option")
	}
}

fn dispatch_option(option: &str) {
	match option {
		"new" => new_repo(),
		"update" => update(),
		"update-remote" => update_remote(),
		"commit" => commit(),
		_ => println!("Unknown option {}", option)
	}
}

fn new_repo() {
	println!("Creation of a new repo");
	let json_path = Path::new("metadata.json");
	metadata::create_emty_metadata_file(json_path);
}

fn update() {
	let data_path = Path::new("data");
	let json_path = Path::new("metadata.json");
	let store_path = Path::new("store");

	let wd_hierarchy : HashMap<String, model::MetaData> = workingdirectory::read_working_directory(data_path);
	println!("{} files in the working directory", wd_hierarchy.len());

	let mt_hierarchy = metadata::read_metadata_file(json_path);
	println!("{} files in the metadata", mt_hierarchy.get_number_of_files());

	let file_top_update = files_to_update(wd_hierarchy, &mt_hierarchy);

	for (filename, metadata) in file_top_update.iter() {
		store::extract_file(store_path, &metadata.get_hash(), data_path, filename, metadata.get_timestamp());
	}
}

fn update_remote() {
	let data_path = Path::new("data");
	let json_path = Path::new("metadata.json");
	let json_remote_path = Path::new("metadata-remote.json");
	let store_path = Path::new("store");

	let wd_hierarchy : HashMap<String, model::MetaData> = workingdirectory::read_working_directory(data_path);
	println!("{} files in the working directory", wd_hierarchy.len());

	let mt_hierarchy = metadata::read_metadata_file(json_path);
	println!("{} files in the metadata", mt_hierarchy.get_number_of_files());

	let mt_remote_hierarchy = metadata::read_metadata_file(json_remote_path);
	println!("{} files in the remote metadata", mt_remote_hierarchy.get_number_of_files());

	let file_top_update = files_to_update_remote(wd_hierarchy, &mt_hierarchy, &mt_remote_hierarchy);
	match file_top_update {
		Some(_) => println!("OK to update"),
		None => println!("IMPOSSIBLE to update")
	}

}

fn files_to_update(wd_hierarchy: HashMap<String, model::MetaData>, mt_hierarchy: &model::Hierarchy) -> HashMap<String, model::MetaData>  {
	let mut file_to_update : HashMap<String, model::MetaData> = HashMap::new();

	for (filename, metadataset) in mt_hierarchy.get_files().iter() {
		let wd_metadata = wd_hierarchy.get(filename);

		match wd_metadata {
			Some(x) => {
				let wd_timestamp = x.get_timestamp();
				let last_md_timestamp = metadataset.get_last().unwrap().get_timestamp();

				if wd_timestamp == last_md_timestamp {
					println!("- No need to update because timestamps are equal {}", filename);
				} else if metadataset.has_metadata_with_timestamp(wd_timestamp) {
					println!("- Existing file to update {}", filename);
					file_to_update.insert(filename.clone(), metadataset.get_last().unwrap().clone());
				} else { //timestamp unknown
					if wd_timestamp > last_md_timestamp {
						println!("- Working directory file is more recent {}", filename);
					} else {
						print!(" - No !! Working directory file is older. It makes no sense {}", filename);
					}
				}
			    ()},
			None => {
				println!("- New file to update {}", filename);
			    file_to_update.insert(filename.clone(), metadataset.get_last().unwrap().clone());
			    () }
		}
	}

	file_to_update
}

fn files_to_update_remote(wd_hierarchy: HashMap<String, model::MetaData>, mt_local_hierarchy: &model::Hierarchy, mt_remote_hierarchy: &model::Hierarchy) -> Option<HashMap<String, model::MetaData>>  {
	let mut file_to_update : HashMap<String, model::MetaData> = HashMap::new();

	for (filename, metadataset) in mt_remote_hierarchy.get_files().iter() {
		let wd_metadata = wd_hierarchy.get(filename);

		match wd_metadata {
			Some(x) => {
				let wd_timestamp = x.get_timestamp();
				let last_md_timestamp = metadataset.get_last().unwrap().get_timestamp();

				if wd_timestamp == last_md_timestamp {
					println!("- No need to update because timestamps are equal {}", filename);
				} else if metadataset.has_metadata_with_timestamp(wd_timestamp) {
					println!("- Existing file to update {}", filename);
					file_to_update.insert(filename.clone(), metadataset.get_last().unwrap().clone());
				} else { //timestamp unknown

					match mt_local_hierarchy.get_latest_meta_data(&filename) {
						Some(m) => {
							if m.get_timestamp() == last_md_timestamp {
								println!("- No need to update because working directory file is a correct new version {}", filename);
							} else {
								println!("- CONFLICT ! The remove and the working directory file have changed {}", filename);
								return Option::None;
							}
						}
						None => {
							println!("- CONFLICT ! New file in the working directory but a remote version exists {}", filename);
							return Option::None;
						}
					};
				}
			    ()},
			None => {
				println!("- New file to update {}", filename);
			    file_to_update.insert(filename.clone(), metadataset.get_last().unwrap().clone());
			    () }
		}
	}

	Option::Some(file_to_update)
}

fn commit() {
	let data_path = Path::new("data");
	let json_path = Path::new("metadata.json");
	let store_path = Path::new("store");

	let wd_hierarchy : HashMap<String, model::MetaData> = workingdirectory::read_working_directory(data_path);
	println!("{} files in the working directory", wd_hierarchy.len());

	let mt_hierarchy = metadata::read_metadata_file(json_path);
	println!("{} files in the metadata", mt_hierarchy.get_number_of_files());

	let files_to_commit = files_to_commit(wd_hierarchy, &mt_hierarchy);
	println!("{} files to commit", files_to_commit.len());

	let mut updated_metadata : HashMap<String, model::MetaData> = HashMap::new();
	for (filename, mut metadata) in files_to_commit {
		let hash = store::store_file(store_path, Path::new(&filename));
		metadata.add_hash(hash);

		updated_metadata.insert(filename, metadata);
	}
	let updated_metadata = updated_metadata;

	let mut mt_hierarchy = mt_hierarchy;
	mt_hierarchy.update(updated_metadata);

	metadata::write_metadata_file(json_path, mt_hierarchy);
}

fn files_to_commit(wd_hierarchy: HashMap<String, model::MetaData>, mt_hierarchy: &model::Hierarchy) -> HashMap<String, model::MetaData>  {
	let mut files_to_commit: HashMap<String, model::MetaData> = HashMap::new();

	for (filename, metadata) in wd_hierarchy.iter() {
		let actual_metadata = mt_hierarchy.get_latest_meta_data(&filename);

		match actual_metadata {
			Some(x) => {
				if x.is_more_recent(&metadata) {
					println!("- File to update {}", filename);
					files_to_commit.insert(filename.clone(), metadata.clone());
				} else {
					println!("- No need to update {}", filename);
				}
			    ()},
			None => {
				println!("- New file {}", filename);
			    files_to_commit.insert(filename.clone(), metadata.clone());
			    () }
		}
	}

	files_to_commit
}

mod model {

	use std::collections::HashMap;

	#[derive(Debug, RustcEncodable, RustcDecodable)]
	pub struct Hierarchy {
		nb_revision: i32,
	    files: HashMap<String, MetaDataSet>
	}

	#[derive(Debug, RustcEncodable, RustcDecodable)]
	pub struct MetaDataSet {
	    metadata: Vec<MetaData>
	}

	#[derive(Debug, RustcEncodable, RustcDecodable, Clone)]
	pub struct MetaData {
	   timestamp: u64,
	   size: u64,
	   hash: String,
	   stored_hash: String
	}

	impl Hierarchy {
		pub fn new_empty() -> Hierarchy {
			let empty_hierarchy_map : HashMap<String, MetaDataSet> = HashMap::new();
			Hierarchy {nb_revision: 1, files: empty_hierarchy_map}
		}
		pub fn get_number_of_files(&self) -> usize {
			self.files.len()
		}
		pub fn get_latest_meta_data(&self, filename: &String) -> Option<&MetaData> {
			match self.files.get(filename) {
				Some(x) => x.get_last(),
				None => None
			}
		}
		pub fn update(&mut self, new_metadata_map: HashMap<String, MetaData>) {
			self.nb_revision = self.nb_revision + 1;

			for (filename, metadata) in new_metadata_map {

				let new_metadata = self.new_metadata(&filename);

				if new_metadata {
					self.files.insert(filename, MetaDataSet::new_simple_meta_data_set(metadata));
				} else {
					let mut m = self.files.get_mut(&filename).unwrap();
					m.add_revision(metadata);
				}
			}
		}

		fn new_metadata(&self, filename: &String) -> bool {
			let actual_metadata = self.files.get(filename);

			match actual_metadata {
				Some(_) => false,
				None => true
			}
		}

		pub fn get_files(&self) -> &HashMap<String, MetaDataSet> {
			&self.files
		}
	}

	impl MetaDataSet {
		pub fn get_last(&self) -> Option<&MetaData> {
			self.metadata.last()
		}
		pub fn new_simple_meta_data_set(m: MetaData) -> MetaDataSet {
			let mut v: Vec<MetaData> = Vec::new();
			v.push(m);
			MetaDataSet {metadata: v }
		}
		pub fn add_revision(&mut self, m: MetaData) {
			self.metadata.push(m);
		}
		pub fn has_metadata_with_timestamp(&self, timestamp: u64) -> bool {
			let mut it = self.metadata.iter();
			match it.find(|&m| m.get_timestamp() == timestamp) {
				Some(_) => true,
				None => false
			}
		}
	}

	impl MetaData {
		pub fn new_without_hash(timestamp: u64, size: u64) -> MetaData {
			MetaData {timestamp: timestamp, size: size, hash: "".to_string(), stored_hash: "".to_string()}
		}
		pub fn add_hash(&mut self, hash: String) {
			self.hash = hash;
		}
		pub fn is_more_recent(&self, other: &MetaData) -> bool {
			self.timestamp < other.timestamp
		}
		pub fn get_timestamp(&self) -> u64 {
			self.timestamp
		}
		pub fn get_hash(&self) -> String {
			self.hash.clone()
		}
	}

}<|endoftext|>


<commit_before>extern crate combine;

use std::io::prelude::*;

use combine::{spaces, string, try, choice, parser, Parser, ParserExt, ParseError};
use combine::char as combine_char;
use combine::primitives::{Stream, State, ParseResult};

#[derive(Debug, PartialEq)]
enum LispVal {
    Atom(String),
    Bool(bool)
}

fn symbol<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{

    let lex_char = |c| combine_char(c).skip(spaces());

    let symbols = choice([lex_char('!'), lex_char('$'), lex_char('%'), lex_char('$'), lex_char('&'),
                    lex_char('|'), lex_char('*'), lex_char('+'), lex_char('-'), lex_char(':'),
                    lex_char('<'), lex_char('='), lex_char('>'), lex_char('?'), lex_char('@'),
                    lex_char('^'), lex_char('_'), lex_char('~')]);

    symbols.map(|c: char| {
            let mut s = String::new();
            s.push(c);
            return s;
        })
        .parse_state(input)

}

fn atom<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let atom = (parser(symbol::<I>))
        .parse_state(input);

    atom
}

fn bool<I>(input: State<I>) -> ParseResult<bool, I>
where I: Stream<Item=char>
{
    let bools = try(string("#t").map(|_| return true)).or(
            string("#f").map(|_| return false)
        )
       .parse_state(input);

    bools
}

fn rusty_lisp<I>(input: State<I>) -> ParseResult<LispVal, I>
where I: Stream<Item=char>
{
    let lispval = parser(atom::<I>)
                   .map(LispVal::Atom)
        .or(parser(bool::<I>)
            .map(LispVal::Bool))
        .parse_state(input);

    lispval
}

fn parse(program: String) {
    let program = program.as_ref();

    let result: Result<(LispVal, &str), ParseError<&str>> = parser(rusty_lisp).parse(program);
    match result {
        Ok((value, _remaining_input)) => println!("{:?}", value),
        Err(err) => println!("{}", err)
    }
}

fn main() {
    println!("rusty-lisp v0.1.0");
    println!("Press 'Ctrl+C' to exit");
    println!("");

    let mut stdin = std::io::stdin();
    let mut input = String::new();

    stdin.read_to_string(&mut input).unwrap();
    parse(input);

    println!("Thanks for using!");
}
<commit_msg>we can parse numbers!<commit_after>extern crate combine;

use std::io::prelude::*;

use combine::{many1, digit, spaces, string, try, choice, parser, Parser, ParserExt, ParseError};
use combine::char as combine_char;
use combine::primitives::{Stream, State, ParseResult};

#[derive(Debug, PartialEq)]
enum LispVal {
    Atom(String),
    Bool(bool),
    Number(i32),
}

fn symbol<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let lex_char = |c| combine_char(c).skip(spaces());

    let symbols = choice([lex_char('!'), lex_char('$'), lex_char('%'), lex_char('$'), lex_char('&'),
                    lex_char('|'), lex_char('*'), lex_char('+'), lex_char('-'), lex_char(':'),
                    lex_char('<'), lex_char('='), lex_char('>'), lex_char('?'), lex_char('@'),
                    lex_char('^'), lex_char('_'), lex_char('~')]);

    symbols.map(|c: char| {
            let mut s = String::new();
            s.push(c);
            return s;
        })
        .parse_state(input)
}

fn bool<I>(input: State<I>) -> ParseResult<bool, I>
where I: Stream<Item=char>
{
    let bewl = try(string("#t").map(|_| return true)).or(
            string("#f").map(|_| return false)
        )
       .parse_state(input);

    bewl
}

fn atom<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let atom = (parser(symbol::<I>))
        .parse_state(input);

    atom
}

fn number<I>(input: State<I>) -> ParseResult<i32, I>
where I: Stream<Item=char>
{
    let number = many1(digit())
        .map(|string: String| string.parse::<i32>().unwrap())
        .parse_state(input);
    number
}

fn rusty_lisp<I>(input: State<I>) -> ParseResult<LispVal, I>
where I: Stream<Item=char>
{
    let lispval = parser(atom::<I>)
                   .map(LispVal::Atom)
        .or(parser(bool::<I>)
            .map(LispVal::Bool))
        .or(parser(number::<I>)
            .map(LispVal::Number))
        .parse_state(input);

    lispval
}

fn parse(program: String) {
    let program = program.as_ref();

    let result: Result<(LispVal, &str), ParseError<&str>> = parser(rusty_lisp).parse(program);
    match result {
        Ok((value, _remaining_input)) => println!("{:?}", value),
        Err(err) => println!("{}", err)
    }
}

fn main() {
    println!("rusty-lisp v0.1.0");
    println!("Press 'Ctrl+C' to exit");
    println!("");

    let mut stdin = std::io::stdin();
    let mut input = String::new();

    stdin.read_to_string(&mut input).unwrap();
    parse(input);

    println!("Thanks for using!");
}
<|endoftext|>


<commit_before>// Copyright 2016 rust-fuzz developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

extern crate toml;
extern crate clap;
extern crate term;
#[macro_use]
extern crate error_chain;

use clap::{App, Arg, SubCommand, ArgMatches, AppSettings};
use std::{env, fs, path, process};
use std::io::Write;
use std::io::Read;

#[macro_use]
mod templates;
mod utils;

error_chain! {
    foreign_links {
        Toml(toml::de::Error);
        Io(::std::io::Error);
    }
}

fn main() {
    let app = App::new("cargo-fuzz")
        .version(option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0"))
        .about(option_env!("CARGO_PKG_DESCRIPTION").unwrap_or(""))
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::GlobalVersion)
        // cargo passes in the subcommand name to the invoked executable. Use a hidden, optional
        // positional argument to deal with it?
        .arg(Arg::with_name("dummy").possible_value("fuzz").required(false).hidden(true))
        .subcommand(SubCommand::with_name("init").about("Initialize the fuzz folder"))
        .subcommand(SubCommand::with_name("run").about("Run the fuzz target in fuzz/fuzzers")
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("TARGET").required(true))
            .arg(Arg::with_name("ARGS").multiple(true))
        )
        .subcommand(SubCommand::with_name("add").about("Add a new fuzz target")
                    .arg(Arg::with_name("TARGET").required(true)))
        .subcommand(SubCommand::with_name("list").about("List all fuzz targets"));
    let args = app.get_matches();

    ::std::process::exit(match args.subcommand() {
        ("init", _) => FuzzProject::init().map(|_| ()),
        ("add", matches) =>
            FuzzProject::new().and_then(|p| p.add_target(matches.expect("arguments present"))),
        ("list", _) => FuzzProject::new().and_then(|p| p.list_targets()),
        ("run", matches) =>
            FuzzProject::new().and_then(|p| p.exec_target(matches.expect("arguments present"))),
        (s, _) => panic!("unimplemented subcommand {}!", s),
    }.map(|_| 0).unwrap_or_else(|err| {
        utils::report_error(err);
        1
    }));
}

struct FuzzProject {
    /// Path to the root cargo project
    ///
    /// Not the project with fuzz targets, but the project being fuzzed
    root_project: path::PathBuf,
    targets: Vec<String>,
}

impl FuzzProject {
    fn new() -> Result<Self> {
        let mut project = FuzzProject {
            root_project: find_package()?,
            targets: Vec::new()
        };
        let manifest = project.manifest()?;
        if !is_fuzz_manifest(&manifest) {
            return Err(format!("manifest `{:?}` does not look a cargo-fuzz manifest. \
                                Add following lines to override:\n\
                                [package.metadata]\ncargo-fuzz = true",
                                project.manifest_path()).into());
        }
        project.targets = collect_targets(&manifest);
        Ok(project)
    }

    /// Create the fuzz project structure
    ///
    /// This will not clone libfuzzer-sys
    fn init() -> Result<Self> {
        let project = FuzzProject {
            root_project: find_package()?,
            targets: Vec::new(),
        };
        let fuzz_project = project.path();
        // TODO: check if the project is already initialized
        fs::create_dir(&fuzz_project)?;
        fs::create_dir(fuzz_project.join("fuzzers"))?;

        let mut cargo = fs::File::create(fuzz_project.join("Cargo.toml"))?;
        cargo.write_fmt(toml_template!(project.root_project_name()?))?;

        let mut ignore = fs::File::create(fuzz_project.join(".gitignore"))?;
        ignore.write_fmt(gitignore_template!())?;

        const TARGET: &'static str = "fuzzer_script_1";
        project.create_target_template(TARGET)
               .chain_err(|| format!("could not create template file for target {:?}", TARGET))?;
        Ok(project)
    }

    fn list_targets(&self) -> Result<()> {
        for bin in &self.targets {
            utils::print_message(bin, term::color::GREEN);
        }
        Ok(())
    }

    fn add_target<'a>(&self, args: &ArgMatches<'a>) -> Result<()> {
        let target: String = args.value_of_os("TARGET").expect("TARGET is required").to_os_string()
            .into_string().map_err(|_| "TARGET must be valid unicode")?;
        self.create_target_template(&target)
            .chain_err(|| format!("could not create template file for target {:?}", target))
    }

    /// Add a new fuzz target script with a given name
    fn create_target_template<'a>(&self, target: &str) -> Result<()> {
        let target_path = self.target_path(target);
        let mut script = fs::File::create(path::Path::new(&target_path))?;
        script.write_fmt(target_template!(self.root_project_name()?.replace("-", "_")))?;

        let mut cargo = fs::OpenOptions::new().append(true)
            .open(self.manifest_path())?;
        Ok(cargo.write_fmt(toml_bin_template!(target))?)
    }

    /// Fuzz a given fuzz target
    fn exec_target<'a>(&self, args: &ArgMatches<'a>) -> Result<()> {
        let target: String = args.value_of_os("TARGET").expect("TARGET is required").to_os_string()
            .into_string().map_err(|_| "TARGET must be valid unicode")?;
        let exec_args = args.values_of_os("ARGS");
        let target_triple = "x86_64-unknown-linux-gnu";

        env::set_current_dir(self.path())?;
        let mut flags = env::var("RUSTFLAGS").unwrap_or("".into());
        if !flags.is_empty() {
            flags.push(' ');
        }
        flags.push_str("-Cpasses=sancov -Cllvm-args=-sanitizer-coverage-level=3 -Zsanitizer=address -Cpanic=abort");
        let mut cmd = process::Command::new("cargo");

        fs::create_dir_all("corpus")?;
        fs::create_dir_all("artifacts")?;

        cmd.env("RUSTFLAGS", flags)
           .arg("run")
           .arg("--verbose")
           .arg("--bin")
           .arg(&target)
           .arg("--target")
           // won't pass rustflags to build scripts
           .arg(target_triple)
           .arg("--")
           .arg("-artifact_prefix=artifacts/")
           .env("ASAN_OPTIONS", "detect_odr_violation=0");
        exec_args.map(|args| for arg in args { cmd.arg(arg); });
        cmd.arg("corpus"); // must be last arg
        exec_cmd(&mut cmd).chain_err(|| format!("could not execute command: {:?}", cmd))?;
        Ok(())
    }

    fn path(&self) -> path::PathBuf {
        self.root_project.join("fuzz")
    }

    fn manifest_path(&self) -> path::PathBuf {
        self.path().join("Cargo.toml")
    }

    fn target_path(&self, target: &str) -> path::PathBuf {
        let mut root = self.path();
        root.push("fuzzers");
        root.push(target);
        root.set_extension("rs");
        root
    }

    fn manifest(&self) -> Result<toml::Value> {
        let filename = self.manifest_path();
        let mut file = fs::File::open(&filename).chain_err(||
            format!("could not read the manifest file: {:?}", filename)
        )?;
        let mut data = Vec::new();
        file.read_to_end(&mut data)?;
        toml::from_slice(&data).chain_err(||
            format!("could not decode the manifest file at {:?}", filename)
        )
    }

    fn root_project_name(&self) -> Result<String> {
        let filename = self.root_project.join("Cargo.toml");
        let mut file = fs::File::open(&filename)?;
        let mut data = Vec::new();
        file.read_to_end(&mut data)?;
        let value: toml::Value = toml::from_slice(&data)?;
        let name = value.as_table().and_then(|v| v.get("package"))
                                   .and_then(toml::Value::as_table)
                                   .and_then(|v| v.get("name"))
                                   .and_then(toml::Value::as_str);
        if let Some(name) = name {
            Ok(String::from(name))
        } else {
            Err(format!("{:?} (package.name) is malformed", filename).into())
        }
    }
}

fn collect_targets(value: &toml::Value) -> Vec<String> {
    let bins = value.as_table().and_then(|v| v.get("bin"))
                               .and_then(toml::Value::as_array);
    if let Some(bins) = bins {
        bins.iter().map(|bin|
            bin.as_table().and_then(|v| v.get("name")).and_then(toml::Value::as_str)
        ).filter_map(|name| name.map(|v| String::from(v))).collect()
    } else {
        Vec::new()
    }
}

fn is_fuzz_manifest(value: &toml::Value) -> bool {
    let is_fuzz = value.as_table().and_then(|v| v.get("package"))
                                  .and_then(toml::Value::as_table)
                                  .and_then(|v| v.get("metadata"))
                                  .and_then(toml::Value::as_table)
                                  .and_then(|v| v.get("cargo-fuzz"))
                                  .and_then(toml::Value::as_bool);
    is_fuzz == Some(true)
}

/// Returns the path for the first found non-fuzz Cargo package
fn find_package() -> Result<path::PathBuf> {
    let mut dir = env::current_dir()?;
    let mut data = Vec::new();
    loop {
        let manifest_path = dir.join("Cargo.toml");
        match fs::File::open(&manifest_path) {
            Err(_) => {},
            Ok(mut f) => {
                f.read_to_end(&mut data)?;
                let value: toml::Value = toml::from_slice(&data)
                    .chain_err(||
                        format!("could not decode the manifest file at {:?}", manifest_path)
                    )?;
                if !is_fuzz_manifest(&value) {
                    // Not a cargo-fuzz project => must be a proper cargo project :)
                    return Ok(dir);
                }
            }
        }
        if !dir.pop() { break; }
    }
    Err("could not find a cargo project".into())
}

#[cfg(unix)]
fn exec_cmd(cmd: &mut process::Command) -> Result<process::ExitStatus> {
    use std::os::unix::process::CommandExt;
    Err(cmd.exec().into())
}

#[cfg(not(unix))]
fn exec_cmd(cmd: &mut process::Command) -> Result<process::ExitStatus> {
    cmd.status()
}
<commit_msg>main: describe arguments to "run"<commit_after>// Copyright 2016 rust-fuzz developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

extern crate toml;
extern crate clap;
extern crate term;
#[macro_use]
extern crate error_chain;

use clap::{App, Arg, SubCommand, ArgMatches, AppSettings};
use std::{env, fs, path, process};
use std::io::Write;
use std::io::Read;

#[macro_use]
mod templates;
mod utils;

error_chain! {
    foreign_links {
        Toml(toml::de::Error);
        Io(::std::io::Error);
    }
}

fn main() {
    let app = App::new("cargo-fuzz")
        .version(option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0"))
        .about(option_env!("CARGO_PKG_DESCRIPTION").unwrap_or(""))
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::GlobalVersion)
        // cargo passes in the subcommand name to the invoked executable. Use a hidden, optional
        // positional argument to deal with it?
        .arg(Arg::with_name("dummy").possible_value("fuzz").required(false).hidden(true))
        .subcommand(SubCommand::with_name("init").about("Initialize the fuzz folder"))
        .subcommand(SubCommand::with_name("run").about("Run the fuzz target in fuzz/fuzzers")
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("TARGET").required(true)
                 .help("name of the fuzz target"))
            .arg(Arg::with_name("ARGS").multiple(true)
                 .help("additional libFuzzer arguments passed to the binary"))
        )
        .subcommand(SubCommand::with_name("add").about("Add a new fuzz target")
                    .arg(Arg::with_name("TARGET").required(true)))
        .subcommand(SubCommand::with_name("list").about("List all fuzz targets"));
    let args = app.get_matches();

    ::std::process::exit(match args.subcommand() {
        ("init", _) => FuzzProject::init().map(|_| ()),
        ("add", matches) =>
            FuzzProject::new().and_then(|p| p.add_target(matches.expect("arguments present"))),
        ("list", _) => FuzzProject::new().and_then(|p| p.list_targets()),
        ("run", matches) =>
            FuzzProject::new().and_then(|p| p.exec_target(matches.expect("arguments present"))),
        (s, _) => panic!("unimplemented subcommand {}!", s),
    }.map(|_| 0).unwrap_or_else(|err| {
        utils::report_error(err);
        1
    }));
}

struct FuzzProject {
    /// Path to the root cargo project
    ///
    /// Not the project with fuzz targets, but the project being fuzzed
    root_project: path::PathBuf,
    targets: Vec<String>,
}

impl FuzzProject {
    fn new() -> Result<Self> {
        let mut project = FuzzProject {
            root_project: find_package()?,
            targets: Vec::new()
        };
        let manifest = project.manifest()?;
        if !is_fuzz_manifest(&manifest) {
            return Err(format!("manifest `{:?}` does not look a cargo-fuzz manifest. \
                                Add following lines to override:\n\
                                [package.metadata]\ncargo-fuzz = true",
                                project.manifest_path()).into());
        }
        project.targets = collect_targets(&manifest);
        Ok(project)
    }

    /// Create the fuzz project structure
    ///
    /// This will not clone libfuzzer-sys
    fn init() -> Result<Self> {
        let project = FuzzProject {
            root_project: find_package()?,
            targets: Vec::new(),
        };
        let fuzz_project = project.path();
        // TODO: check if the project is already initialized
        fs::create_dir(&fuzz_project)?;
        fs::create_dir(fuzz_project.join("fuzzers"))?;

        let mut cargo = fs::File::create(fuzz_project.join("Cargo.toml"))?;
        cargo.write_fmt(toml_template!(project.root_project_name()?))?;

        let mut ignore = fs::File::create(fuzz_project.join(".gitignore"))?;
        ignore.write_fmt(gitignore_template!())?;

        const TARGET: &'static str = "fuzzer_script_1";
        project.create_target_template(TARGET)
               .chain_err(|| format!("could not create template file for target {:?}", TARGET))?;
        Ok(project)
    }

    fn list_targets(&self) -> Result<()> {
        for bin in &self.targets {
            utils::print_message(bin, term::color::GREEN);
        }
        Ok(())
    }

    fn add_target<'a>(&self, args: &ArgMatches<'a>) -> Result<()> {
        let target: String = args.value_of_os("TARGET").expect("TARGET is required").to_os_string()
            .into_string().map_err(|_| "TARGET must be valid unicode")?;
        self.create_target_template(&target)
            .chain_err(|| format!("could not create template file for target {:?}", target))
    }

    /// Add a new fuzz target script with a given name
    fn create_target_template<'a>(&self, target: &str) -> Result<()> {
        let target_path = self.target_path(target);
        let mut script = fs::File::create(path::Path::new(&target_path))?;
        script.write_fmt(target_template!(self.root_project_name()?.replace("-", "_")))?;

        let mut cargo = fs::OpenOptions::new().append(true)
            .open(self.manifest_path())?;
        Ok(cargo.write_fmt(toml_bin_template!(target))?)
    }

    /// Fuzz a given fuzz target
    fn exec_target<'a>(&self, args: &ArgMatches<'a>) -> Result<()> {
        let target: String = args.value_of_os("TARGET").expect("TARGET is required").to_os_string()
            .into_string().map_err(|_| "TARGET must be valid unicode")?;
        let exec_args = args.values_of_os("ARGS");
        let target_triple = "x86_64-unknown-linux-gnu";

        env::set_current_dir(self.path())?;
        let mut flags = env::var("RUSTFLAGS").unwrap_or("".into());
        if !flags.is_empty() {
            flags.push(' ');
        }
        flags.push_str("-Cpasses=sancov -Cllvm-args=-sanitizer-coverage-level=3 -Zsanitizer=address -Cpanic=abort");
        let mut cmd = process::Command::new("cargo");

        fs::create_dir_all("corpus")?;
        fs::create_dir_all("artifacts")?;

        cmd.env("RUSTFLAGS", flags)
           .arg("run")
           .arg("--verbose")
           .arg("--bin")
           .arg(&target)
           .arg("--target")
           // won't pass rustflags to build scripts
           .arg(target_triple)
           .arg("--")
           .arg("-artifact_prefix=artifacts/")
           .env("ASAN_OPTIONS", "detect_odr_violation=0");
        exec_args.map(|args| for arg in args { cmd.arg(arg); });
        cmd.arg("corpus"); // must be last arg
        exec_cmd(&mut cmd).chain_err(|| format!("could not execute command: {:?}", cmd))?;
        Ok(())
    }

    fn path(&self) -> path::PathBuf {
        self.root_project.join("fuzz")
    }

    fn manifest_path(&self) -> path::PathBuf {
        self.path().join("Cargo.toml")
    }

    fn target_path(&self, target: &str) -> path::PathBuf {
        let mut root = self.path();
        root.push("fuzzers");
        root.push(target);
        root.set_extension("rs");
        root
    }

    fn manifest(&self) -> Result<toml::Value> {
        let filename = self.manifest_path();
        let mut file = fs::File::open(&filename).chain_err(||
            format!("could not read the manifest file: {:?}", filename)
        )?;
        let mut data = Vec::new();
        file.read_to_end(&mut data)?;
        toml::from_slice(&data).chain_err(||
            format!("could not decode the manifest file at {:?}", filename)
        )
    }

    fn root_project_name(&self) -> Result<String> {
        let filename = self.root_project.join("Cargo.toml");
        let mut file = fs::File::open(&filename)?;
        let mut data = Vec::new();
        file.read_to_end(&mut data)?;
        let value: toml::Value = toml::from_slice(&data)?;
        let name = value.as_table().and_then(|v| v.get("package"))
                                   .and_then(toml::Value::as_table)
                                   .and_then(|v| v.get("name"))
                                   .and_then(toml::Value::as_str);
        if let Some(name) = name {
            Ok(String::from(name))
        } else {
            Err(format!("{:?} (package.name) is malformed", filename).into())
        }
    }
}

fn collect_targets(value: &toml::Value) -> Vec<String> {
    let bins = value.as_table().and_then(|v| v.get("bin"))
                               .and_then(toml::Value::as_array);
    if let Some(bins) = bins {
        bins.iter().map(|bin|
            bin.as_table().and_then(|v| v.get("name")).and_then(toml::Value::as_str)
        ).filter_map(|name| name.map(|v| String::from(v))).collect()
    } else {
        Vec::new()
    }
}

fn is_fuzz_manifest(value: &toml::Value) -> bool {
    let is_fuzz = value.as_table().and_then(|v| v.get("package"))
                                  .and_then(toml::Value::as_table)
                                  .and_then(|v| v.get("metadata"))
                                  .and_then(toml::Value::as_table)
                                  .and_then(|v| v.get("cargo-fuzz"))
                                  .and_then(toml::Value::as_bool);
    is_fuzz == Some(true)
}

/// Returns the path for the first found non-fuzz Cargo package
fn find_package() -> Result<path::PathBuf> {
    let mut dir = env::current_dir()?;
    let mut data = Vec::new();
    loop {
        let manifest_path = dir.join("Cargo.toml");
        match fs::File::open(&manifest_path) {
            Err(_) => {},
            Ok(mut f) => {
                f.read_to_end(&mut data)?;
                let value: toml::Value = toml::from_slice(&data)
                    .chain_err(||
                        format!("could not decode the manifest file at {:?}", manifest_path)
                    )?;
                if !is_fuzz_manifest(&value) {
                    // Not a cargo-fuzz project => must be a proper cargo project :)
                    return Ok(dir);
                }
            }
        }
        if !dir.pop() { break; }
    }
    Err("could not find a cargo project".into())
}

#[cfg(unix)]
fn exec_cmd(cmd: &mut process::Command) -> Result<process::ExitStatus> {
    use std::os::unix::process::CommandExt;
    Err(cmd.exec().into())
}

#[cfg(not(unix))]
fn exec_cmd(cmd: &mut process::Command) -> Result<process::ExitStatus> {
    cmd.status()
}
<|endoftext|>


<commit_before>extern crate serde_json;
extern crate clap;

use serde_json::Value;
use serde_json::Deserializer;
use std::io;
use clap::{App, Arg};
use std::fs;
use std::result;
use std::error::{Error};
use std::vec::Vec;
use std::string::String;

type Result<T> = result::Result<T, Box<Error>>;

fn run() -> Result<()> {
    let matches = App::new("jp").version("0.1")
        .about("Extract JSON elements from a stream")
        .author("Brian McCallister <brianm@skife.org>")
        .arg(Arg::with_name("INPUT")
                .short("i")
                .long("input")
                .takes_value(true)
                .help("input file to use if not receiving on stdin"))
        .arg(Arg::with_name("POINTER")
                .index(1)
                .required(true)
                .multiple(true)
                .help("JSON Pointer expressions to match on input"))
        .get_matches();

    let input = matches.value_of("INPUT").unwrap_or("-");
    let rdr: Box<io::Read> = match input {
        "-" => Box::new(io::stdin()),
        _   => Box::new(try!(fs::File::open(input)))
    };

    let pointers_in = matches.values_of("POINTER").unwrap();
    let mut pointers = Vec::new();
    for p in pointers_in {
        pointers.push(p);
    }

    let iter = Deserializer::from_reader(rdr).into_iter::<Value>();
    for rv in iter {
        let mut line = String::new();
        let v: Value = rv.unwrap();
        for p in &pointers {
            line.push_str(&render(v.pointer(p).unwrap_or(&Value::Null)));
            line.push('\t');
        }
        line.pop();
        println!("{}", line);
    }
    Ok(())
}

fn main() {
    match run() {
        Ok(_) => {}
        Err(e) => {
            // TODO print to STDERR instead of STDOUT
            println!("{}", e);
            std::process::exit(1);
        }
    }
}

fn render(v: &Value) -> String {
    match v {
        &Value::Null => String::new(),
        &Value::Bool(ref b) => format!("{}", b),
        &Value::Number(ref b) => format!("{}", b),
        &Value::String(ref s) => format!("{}", s),
        &Value::Array(_) =>  String::from("[...]"), //format!("{}", a),
        &Value::Object(_) => String::from("{...}")  //  format!("{}", o)
    }
}
<commit_msg>print non-primitives as json<commit_after>extern crate serde_json;
extern crate clap;

use serde_json::Value;
use serde_json::Deserializer;
use std::io;
use clap::{App, Arg};
use std::fs;
use std::result;
use std::error::{Error};
use std::vec::Vec;
use std::string::String;

type Result<T> = result::Result<T, Box<Error>>;

fn run() -> Result<()> {
    let matches = App::new("jp").version("0.1")
        .about("Extract JSON elements from a stream")
        .author("Brian McCallister <brianm@skife.org>")
        .arg(Arg::with_name("INPUT")
                .short("i")
                .long("input")
                .takes_value(true)
                .help("input file to use if not receiving on stdin"))
        .arg(Arg::with_name("POINTER")
                .index(1)
                .required(true)
                .multiple(true)
                .help("JSON Pointer expressions to match on input"))
        .get_matches();

    let input = matches.value_of("INPUT").unwrap_or("-");
    let rdr: Box<io::Read> = match input {
        "-" => Box::new(io::stdin()),
        _   => Box::new(try!(fs::File::open(input)))
    };

    let pointers_in = matches.values_of("POINTER").unwrap();
    let mut pointers = Vec::new();
    for p in pointers_in {
        pointers.push(p);
    }

    let iter = Deserializer::from_reader(rdr).into_iter::<Value>();
    for rv in iter {
        let mut line = String::new();
        let v: Value = rv.unwrap();
        for p in &pointers {
            line.push_str(&render(v.pointer(p).unwrap_or(&Value::Null)));
            line.push('\t');
        }
        line.pop();
        println!("{}", line);
    }
    Ok(())
}

fn main() {
    match run() {
        Ok(_) => {}
        Err(e) => {
            // TODO print to STDERR instead of STDOUT
            println!("{}", e);
            std::process::exit(1);
        }
    }
}

fn render(v: &Value) -> String {
    match v {
        &Value::Null => String::new(),
        &Value::Bool(ref b) => format!("{}", b),
        &Value::Number(ref b) => format!("{}", b),
        &Value::String(ref s) => format!("{}", s),
        &Value::Array(_) => format!("{}", v),
        &Value::Object(_) => format!("{}", v)
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

extern crate clap;
use clap::{Arg, App, SubCommand};
extern crate toml;

extern crate reqwest;
use reqwest::header::Authorization;

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate glob;
use glob::glob;

extern crate libc;
#[derive(Deserialize, Debug)]
struct Config {
    token: String,
    org: String,
    team: String,
    #[serde(default="default_endpoint")]
    endpoint: String,
    #[serde(default="default_home")]
    home: String,
    #[serde(default="default_gid")]
    gid: u64,
    #[serde(default="default_sh")]
    sh: String,
    group: Option<String>,
    #[serde(default="default_cache_duration")]
    cache_duration: u64,
    #[serde(default="default_cert_path")]
    cert_path: String
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_gid() -> u64 { 2000 }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Team {
    id: u64,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    id: u64,
    login: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct PublicKey {
    id: u64,
    key: String,
}

#[derive(Debug)]
enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error),
}

impl From<serde_json::Error> for CliError {fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }}
impl From<reqwest::Error> for CliError {fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }}
impl From<std::io::Error> for CliError {fn from(err: std::io::Error) -> CliError { CliError::Io(err) }}

fn main() {

    let matches = App::new("ghteam-auth")
                      .version("0.1")
                      .author("Yasuyuki YAMADA <yasuyuki.ymd@gmail.com>")
                      .about("")
                      .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                      .subcommand(SubCommand::with_name("key")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(true)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("pam")
                                             .about("execute pam check")
                                             .arg(Arg::with_name("USER")
                                                      .required(false)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("passwd")
                                             .about("get passwd"))
                      .subcommand(SubCommand::with_name("shadow")
                                             .about("get shadow"))
                      .subcommand(SubCommand::with_name("group")
                                             .about("get group"))
                      .subcommand(SubCommand::with_name("refresh")
                                             .about("refresh cache"))
                      .get_matches();

    let configpath = std::env::var("GHTEAMAUTH_CONFIG").unwrap_or(String::from("/etc/ghteam-auth.conf"));
    let client = create_github_client(configpath.as_str()).unwrap();

    if let Some(matches) = matches.subcommand_matches("key") {
        client.print_user_public_key(matches.value_of("USER").unwrap()).unwrap();
    } else if let Some(_) = matches.subcommand_matches("passwd") {
        client.print_passwd().unwrap();
    } else if let Some(_) = matches.subcommand_matches("shadow") {
        client.print_shadow().unwrap();
    } else if let Some(_) = matches.subcommand_matches("group") {
        client.print_group().unwrap();
    } else if let Some(_) = matches.subcommand_matches("refresh") {
        client.clear_all_caches().unwrap();
    } else if let Some(_) = matches.subcommand_matches("pam") {
        match std::env::var("PAM_USER") {
            Ok(user) => {
                if client.check_pam(&user).unwrap() { std::process::exit(0); }
                else { std::process::exit(1) }
            }
            Err(e) => println!("couldn't interpret PAM_USER: {}", e),
        }
    }

}

fn create_github_client( configpath: &str ) -> Result<GithubClient, CliError> {
    let mut file = File::open(configpath)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let config = toml::from_str::<Config>(contents.as_str()).unwrap();
    match std::env::var("SSL_CERT_FILE") {
        Ok(_) => (),
        Err(_) => std::env::set_var("SSL_CERT_FILE", config.cert_path.clone()),
    }
    let client = reqwest::Client::new()?;
    Ok(GithubClient::new(client, config))
}

struct GithubClient {
    client: reqwest::Client,
    conf: Config
}

impl GithubClient {
    fn new(client:reqwest::Client, conf:Config) -> GithubClient {
        GithubClient {client:client, conf:conf}
    }

    fn load_content_from_cache(&self, url:&String) -> Result<(std::fs::Metadata,String),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata,content))
    }

    fn store_content_to_cache(&self, url:&String, content:&String) -> Result<(),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let mut dirpath = path.clone(); dirpath.pop();
        std::fs::create_dir_all(dirpath)?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url:&String) -> Result<String,CliError> {
        let token = String::from("token ") + self.conf.token.clone().as_str();
        let res = self.client.get(url.as_str()).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url,&content)?;
        Ok(content)
    }

    fn get_content(&self, url:&String) -> Result<String,CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata,cache_content)) => {
                match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                    Ok(caching_duration) => {
                        if caching_duration.as_secs() > self.conf.cache_duration {
                            match self.get_content_from_url(url) {
                                Ok(content_from_url) => Ok(content_from_url),
                                Err(_) => Ok(cache_content)
                            }
                        } else {
                            Ok(cache_content)
                        }
                    },
                    Err(_) => {
                        Ok(cache_content)
                    }
                }
            },
            Err(_) => self.get_content_from_url(url)
        }
    }

    fn print_user_public_key(&self, user:&str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user:&str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url);
        let keys = serde_json::from_str::<Vec<PublicKey>>(content?.as_str())?;
        Ok(keys.iter().map(|k|{k.key.clone()}).collect::<Vec<String>>().join("\n"))
    }

    fn check_pam(&self, user:&String) -> Result<bool, CliError> {
        let (_,members) = self.get_team_members()?;
        Ok(members.contains_key(user))
    }

    fn print_passwd(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_passwd_line(&member));
        }
        Ok(())
    }

    fn print_shadow(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_shadow_line(&member));
        }
        Ok(())
    }

    fn print_group(&self) -> Result<(), CliError> {
        let (team,members) = self.get_team_members()?;
        println!("{}", self.create_group_line(&team.name, self.conf.gid, &members));
        Ok(())
    }

    fn create_passwd_line(&self, member:&Member) -> String {
        format!("{login}:x:{uid}:{gid}:user@{org}:{home}:{sh}",
                login=member.login,
                uid=member.id,
                gid=self.conf.gid,
                org=self.conf.org,
                home=self.conf.home.replace("{}",member.login.as_str()),
                sh=self.conf.sh,
                )
    }

    fn create_shadow_line(&self, member:&Member) -> String {
        format!("{login}:!!:::::::", login=member.login )
    }

    fn create_group_line(&self, name:&String, id:u64, members:&HashMap<String,Member>) -> String {
        format!("{name}:x:{id}:{members}", name=name, id=id,
                members=members.values().map(|m|{m.login.clone()}).collect::<Vec<String>>().join(","))
    }

    fn get_team_members(&self) -> Result<(Team,HashMap<String,Member>),CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            Ok((team.clone(),self.get_members(team.id)?))
        } else {
            Err(CliError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "Team not found")))
        }
    }

    fn get_teams(&self) -> Result<HashMap<String,Team>, CliError> {
        let url = format!("{}/orgs/{}/teams",self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(content.as_str())?;
        let mut team_map = HashMap::new();
        for team in teams { team_map.insert(team.name.clone(), team); }
        Ok(team_map)
    }

    fn get_members(&self, mid:u64) -> Result<HashMap<String,Member>, CliError> {
        let url = format!("{}/teams/{}/members",self.conf.endpoint.clone(), mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(content.as_str())?;
        let mut member_map = HashMap::new();
        for member in members { member_map.insert(member.login.clone(), member); }
        Ok(member_map)
    }

    fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push("**");
        path.push("*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {if path.is_file() {std::fs::remove_file(path)?}},
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }

}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct Passwd {
    name:   *mut libc::c_char,
    pass:   *mut libc::c_char,
    uid:    libc::uid_t,
    gid:    libc::gid_t,
    gecos:  *mut libc::c_char,
    home:   *mut libc::c_char,
    sh:     *mut libc::c_char,
}
<commit_msg>Change client to be static<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

extern crate clap;
use clap::{Arg, App, SubCommand};
extern crate toml;

extern crate reqwest;
use reqwest::header::Authorization;

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate glob;
use glob::glob;

#[macro_use]
extern crate lazy_static;

extern crate libc;
#[derive(Deserialize, Debug)]
struct Config {
    token: String,
    org: String,
    team: String,
    #[serde(default="default_endpoint")]
    endpoint: String,
    #[serde(default="default_home")]
    home: String,
    #[serde(default="default_gid")]
    gid: u64,
    #[serde(default="default_sh")]
    sh: String,
    group: Option<String>,
    #[serde(default="default_cache_duration")]
    cache_duration: u64,
    #[serde(default="default_cert_path")]
    cert_path: String
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_gid() -> u64 { 2000 }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Team {
    id: u64,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    id: u64,
    login: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct PublicKey {
    id: u64,
    key: String,
}

#[derive(Debug)]
enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error),
}

impl From<serde_json::Error> for CliError {fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }}
impl From<reqwest::Error> for CliError {fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }}
impl From<std::io::Error> for CliError {fn from(err: std::io::Error) -> CliError { CliError::Io(err) }}

lazy_static! {
    static ref CLIENT:GithubClient = create_github_client(
        std::env::var("GHTEAMAUTH_CONFIG")
                 .unwrap_or(String::from("/etc/ghteam-auth.conf"))
                 .as_str()
    ).unwrap();
}

fn main() {

    let matches = App::new("ghteam-auth")
                      .version("0.1")
                      .author("Yasuyuki YAMADA <yasuyuki.ymd@gmail.com>")
                      .about("")
                      .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                      .subcommand(SubCommand::with_name("key")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(true)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("pam")
                                             .about("execute pam check")
                                             .arg(Arg::with_name("USER")
                                                      .required(false)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("passwd")
                                             .about("get passwd"))
                      .subcommand(SubCommand::with_name("shadow")
                                             .about("get shadow"))
                      .subcommand(SubCommand::with_name("group")
                                             .about("get group"))
                      .subcommand(SubCommand::with_name("refresh")
                                             .about("refresh cache"))
                      .get_matches();

    if let Some(matches) = matches.subcommand_matches("key") {
        CLIENT.print_user_public_key(matches.value_of("USER").unwrap()).unwrap();
    } else if let Some(_) = matches.subcommand_matches("passwd") {
        CLIENT.print_passwd().unwrap();
    } else if let Some(_) = matches.subcommand_matches("shadow") {
        CLIENT.print_shadow().unwrap();
    } else if let Some(_) = matches.subcommand_matches("group") {
        CLIENT.print_group().unwrap();
    } else if let Some(_) = matches.subcommand_matches("refresh") {
        CLIENT.clear_all_caches().unwrap();
    } else if let Some(_) = matches.subcommand_matches("pam") {
        match std::env::var("PAM_USER") {
            Ok(user) => {
                if CLIENT.check_pam(&user).unwrap() { std::process::exit(0); }
                else { std::process::exit(1) }
            }
            Err(e) => println!("couldn't interpret PAM_USER: {}", e),
        }
    }

}

fn create_github_client( configpath: &str ) -> Result<GithubClient, CliError> {
    let mut file = File::open(configpath)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let config = toml::from_str::<Config>(contents.as_str()).unwrap();
    match std::env::var("SSL_CERT_FILE") {
        Ok(_) => (),
        Err(_) => std::env::set_var("SSL_CERT_FILE", config.cert_path.clone()),
    }
    let client = reqwest::Client::new()?;
    Ok(GithubClient::new(client, config))
}

struct GithubClient {
    client: reqwest::Client,
    conf: Config
}

impl GithubClient {
    fn new(client:reqwest::Client, conf:Config) -> GithubClient {
        GithubClient {client:client, conf:conf}
    }

    fn load_content_from_cache(&self, url:&String) -> Result<(std::fs::Metadata,String),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata,content))
    }

    fn store_content_to_cache(&self, url:&String, content:&String) -> Result<(),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let mut dirpath = path.clone(); dirpath.pop();
        std::fs::create_dir_all(dirpath)?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url:&String) -> Result<String,CliError> {
        let token = String::from("token ") + self.conf.token.clone().as_str();
        let res = self.client.get(url.as_str()).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url,&content)?;
        Ok(content)
    }

    fn get_content(&self, url:&String) -> Result<String,CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata,cache_content)) => {
                match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                    Ok(caching_duration) => {
                        if caching_duration.as_secs() > self.conf.cache_duration {
                            match self.get_content_from_url(url) {
                                Ok(content_from_url) => Ok(content_from_url),
                                Err(_) => Ok(cache_content)
                            }
                        } else {
                            Ok(cache_content)
                        }
                    },
                    Err(_) => {
                        Ok(cache_content)
                    }
                }
            },
            Err(_) => self.get_content_from_url(url)
        }
    }

    fn print_user_public_key(&self, user:&str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user:&str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url);
        let keys = serde_json::from_str::<Vec<PublicKey>>(content?.as_str())?;
        Ok(keys.iter().map(|k|{k.key.clone()}).collect::<Vec<String>>().join("\n"))
    }

    fn check_pam(&self, user:&String) -> Result<bool, CliError> {
        let (_,members) = self.get_team_members()?;
        Ok(members.contains_key(user))
    }

    fn print_passwd(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_passwd_line(&member));
        }
        Ok(())
    }

    fn print_shadow(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_shadow_line(&member));
        }
        Ok(())
    }

    fn print_group(&self) -> Result<(), CliError> {
        let (team,members) = self.get_team_members()?;
        println!("{}", self.create_group_line(&team.name, self.conf.gid, &members));
        Ok(())
    }

    fn create_passwd_line(&self, member:&Member) -> String {
        format!("{login}:x:{uid}:{gid}:user@{org}:{home}:{sh}",
                login=member.login,
                uid=member.id,
                gid=self.conf.gid,
                org=self.conf.org,
                home=self.conf.home.replace("{}",member.login.as_str()),
                sh=self.conf.sh,
                )
    }

    fn create_shadow_line(&self, member:&Member) -> String {
        format!("{login}:!!:::::::", login=member.login )
    }

    fn create_group_line(&self, name:&String, id:u64, members:&HashMap<String,Member>) -> String {
        format!("{name}:x:{id}:{members}", name=name, id=id,
                members=members.values().map(|m|{m.login.clone()}).collect::<Vec<String>>().join(","))
    }

    fn get_team_members(&self) -> Result<(Team,HashMap<String,Member>),CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            Ok((team.clone(),self.get_members(team.id)?))
        } else {
            Err(CliError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "Team not found")))
        }
    }

    fn get_teams(&self) -> Result<HashMap<String,Team>, CliError> {
        let url = format!("{}/orgs/{}/teams",self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(content.as_str())?;
        let mut team_map = HashMap::new();
        for team in teams { team_map.insert(team.name.clone(), team); }
        Ok(team_map)
    }

    fn get_members(&self, mid:u64) -> Result<HashMap<String,Member>, CliError> {
        let url = format!("{}/teams/{}/members",self.conf.endpoint.clone(), mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(content.as_str())?;
        let mut member_map = HashMap::new();
        for member in members { member_map.insert(member.login.clone(), member); }
        Ok(member_map)
    }

    fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push("**");
        path.push("*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {if path.is_file() {std::fs::remove_file(path)?}},
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }

}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct Passwd {
    name:   *mut libc::c_char,
    pass:   *mut libc::c_char,
    uid:    libc::uid_t,
    gid:    libc::gid_t,
    gecos:  *mut libc::c_char,
    home:   *mut libc::c_char,
    sh:     *mut libc::c_char,
}
<|endoftext|>