<commit_before>use consts::*;
use proto::*;

use std::old_io::{IoResult, MemReader, MemWriter, Reader, TcpStream};
use std::old_io::net::ip::SocketAddr;
use std::old_io::timer::Timer;
use std::num::FromPrimitive;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender, SyncSender, sync_channel};
use std::time::Duration;
use std::thread;

macro_rules! fetch_result(
    ($res:ident, $enu:ident::$hack:ident($item:ident)) => (
        match $res {
            $enu::$hack(response) => Ok(response.$item),
            Response::Error(e) => Err(e),
            _ => Err(ZkError::SystemError)
        }
    )
);

macro_rules! fetch_empty_result(
    ($res:ident, $enu:ident::$hack:ident) => (
        match $res {
            $enu::$hack => Ok(()),
            Response::Error(e) => Err(e),
            _ => Err(ZkError::SystemError)
        }
    )
);

#[derive(Debug)]
enum OpCode {
    Auth = 100,
    Create = 1,
    Delete = 2,
    Exists = 3,
    GetAcl = 6,
    SetAcl = 7,
    GetChildren = 8,
    GetData = 4,
    SetData = 5,
    Ping = 11,
    CloseSession = -11
}

impl Copy for OpCode {}

pub trait Watcher: Send {
    fn handle(&self, &WatchedEvent);
}

pub type ZkResult<T> = Result<T, ZkError>;

struct Packet {
    opcode: OpCode,
    data: Vec<u8>,
    resp_tx: SyncSender<Response>
}

#[derive(Clone)]
pub struct ZooKeeper {
    xid: Arc<AtomicIsize>,
    running: Arc<AtomicBool>,
    packet_tx: Sender<Packet> // sending Packets from methods to writer task
}

impl ZooKeeper {

    pub fn connect<'a, W: 'static + Watcher>(connect_string: &'a str, timeout: Duration, watcher: W) -> ZkResult<ZooKeeper> {

        // communicating reader socket from writer to reader task
        let (reader_sock_tx, reader_sock_rx) = sync_channel(0);
        // comminucating requests (as Packets) from instance methods to writer task
        let (packet_tx, packet_rx): (Sender<Packet>, Receiver<Packet>) = channel();
        // communicating sent Packets from writer task to the reader task
        let (written_tx, written_rx) = channel();
        // event channel for passing WatchedEvents to watcher on a seperate task
        let (event_tx, event_rx) = channel();
        // event channel for passing WatchedEvents that the client should generate
        let event_tx_manual = event_tx.clone();

        let running = Arc::new(AtomicBool::new(true));
        let running1 = running.clone();

        let hosts = connect_string.split(',').map(|host| host.parse::<SocketAddr>().unwrap()).collect();

        thread::spawn(move || {
            println!("Event: thread started");

            loop {
                match event_rx.recv() {
                    Ok(event) => watcher.handle(&event),
                    Err(_) => return
                }
            }
        });

        thread::spawn(move || {
            println!("Writer: thread started");

            let mut timer = Timer::new().unwrap();
            let ping_timeout = timer.periodic(timeout);
            let mut writer_sock;
            let mut conn_resp = ConnectResponse::initial(timeout);

            loop {
                println!("Writer: connecting, trying to get new writer_sock");
                let (new_writer_sock, new_conn_resp) = match running.load(Ordering::SeqCst) {
                    true => ZooKeeper::reconnect(&hosts, conn_resp),
                    false => {
                        println!("Writer: exiting");
                        return
                    }
                };
                writer_sock = new_writer_sock;
                conn_resp = new_conn_resp;

                // pass a clone of the writer socket to the reader thread, will be used for reading
                match reader_sock_tx.send(writer_sock.clone()) {
                    Err(_) => panic!("Writer: failed to pass new socket to reader thread"),
                    _ => ()
                }

                // Send Connected event through event thread to the user
                let keeper_state = if conn_resp.read_only {
                    KeeperState::ConnectedReadOnly
                } else {
                    KeeperState::SyncConnected
                };
                event_tx_manual.send(
                    WatchedEvent{
                        event_type: WatchedEventType::None,
                        keeper_state: keeper_state,
                        path: None}
                ).unwrap();

                loop {
                    // do we have something to send or do we need to ping?
                    select! {
                        res = packet_rx.recv() => {
                            let packet = match res {
                                Ok(packet) => packet,
                                Err(_) => return
                            };
                            let res = writer_sock.write_buffer(&packet.data);
                            if res.is_err() {
                                println!("Writer: failed to send to server, trying to reconnect");
                                break;
                            }
                            let opcode = packet.opcode;
                            match written_tx.send(packet) {
                                Err(_) => panic!("Writer: failed to communicate with reader thread"),
                                _ => ()
                            }
                            match opcode {
                                OpCode::CloseSession => {
                                    println!("Writer: exiting");
                                    return
                                },
                                _ => ()
                            }
                        },
                        _ = ping_timeout.recv() => {
                            println!("Writer: Pinging {}", writer_sock.peer_name().unwrap());
                            let ping = RequestHeader{xid: -2, opcode: OpCode::Ping as i32}.to_byte_vec();
                            let res = writer_sock.write_buffer(&ping);
                            if res.is_err() {
                                println!("Writer: failed to ping server, trying to reconnect");
                                break;
                            }
                        }
                    };
                }
            }
        });

        thread::spawn(move || {
            println!("Reader: thread started");

            loop {
                println!("Reader: connecting, trying to get new reader_sock");
                let mut reader_sock = match reader_sock_rx.recv() {
                    Ok(sock) => sock,
                    Err(_) => {
                        println!("Reader: writer thread died, exiting");
                        return
                    }
                };

                loop {
                    let reply = ZooKeeper::read_reply(&mut reader_sock);
                    if reply.is_err() {
                        println!("Reader: read_reply {:?}", reply.err());
                        break;
                    }
                    let (reply_header, mut buf) = reply.unwrap();
                    match reply_header.xid {
                        -1 => event_tx.send(WatchedEvent::read_from(&mut buf)).unwrap(),
                        -2 => println!("Reader: got ping event"),
                        _xid => {
                            let packet = written_rx.recv().unwrap();
                            let result = ZooKeeper::parse_reply(reply_header.err, &packet, &mut buf);

                            match packet.resp_tx.send(result) {
                                Err(_) => println!("Reader: failed to pass back result to client thread"),
                                _ => ()
                            }
                         }
                    }
                }
            }
        });

        Ok(ZooKeeper{xid: Arc::new(AtomicIsize::new(1)), running: running1, packet_tx: packet_tx})
    }

    fn read_reply<R: Reader>(sock: &mut R) -> IoResult<(ReplyHeader, MemReader)> {
        let buf = try!(sock.read_buffer());
        let mut reader = MemReader::new(buf);
        Ok((ReplyHeader::read_from(&mut reader), reader))
    }

    fn parse_reply<R: Reader>(err: i32, packet: &Packet, buf: &mut R) -> Response {
        match err {
            0 => match packet.opcode {
                OpCode::Auth => Response::Auth,
                OpCode::CloseSession => Response::Close,
                OpCode::Create => Response::Create(CreateResponse::read_from(buf)),
                OpCode::Delete => Response::Delete,
                OpCode::Exists => Response::Exists(StatResponse::read_from(buf)),
                OpCode::GetAcl => Response::GetAcl(GetAclResponse::read_from(buf)),
                OpCode::SetAcl => Response::SetAcl(StatResponse::read_from(buf)),
                OpCode::GetChildren => Response::GetChildren(GetChildrenResponse::read_from(buf)),
                OpCode::GetData => Response::GetData(GetDataResponse::read_from(buf)),
                OpCode::SetData => Response::SetData(StatResponse::read_from(buf)),
                ref opcode => panic!("{:?}Response not implemented yet", opcode)
            },
            e => {
                Response::Error(FromPrimitive::from_i32(e).unwrap())
            }
        }
    }

    fn reconnect(hosts: &Vec<SocketAddr>, conn_resp: ConnectResponse) -> (TcpStream, ConnectResponse) {
        let conn_req = ConnectRequest::from(conn_resp, 0).to_byte_vec();

        loop {
            for host in hosts.iter() {
                println!("Writer: Connecting to {}...", host);
                let mut sock = TcpStream::connect_timeout(*host, Duration::seconds(1));
                if sock.is_err() {
                    println!("Writer: Failed to connect to {}", host);
                    continue;
                }

                let write = sock.write_buffer(&conn_req);
                if write.is_err() {
                    continue;
                }

                let read = sock.read_buffer();
                if read.is_err() {
                    continue;
                }

                let mut buf = MemReader::new(read.unwrap());
                let conn_resp = ConnectResponse::read_from(&mut buf);

                println!("Writer: Connection: {:?}", conn_resp);

                return (sock.unwrap(), conn_resp)
            }
        }
    }

    fn xid(&self) -> i32 {
        self.xid.fetch_add(1, Ordering::SeqCst) as i32
    }

    fn request<T: Archive>(&self, opcode: OpCode, xid: i32, req: T) -> Response {
        let rh = RequestHeader{xid: xid, opcode: opcode as i32};

        let mut buf = MemWriter::new();
        let _ = rh.write_to(&mut buf);
        let _ = req.write_to(&mut buf);

        let (resp_tx, resp_rx) = sync_channel(0);
        let packet = Packet{opcode: opcode, data: buf.into_inner(), resp_tx: resp_tx};

        if self.packet_tx.send(packet).is_err() {
            // writer thread died
            return Response::Error(ZkError::SystemError)
        }

        match resp_rx.recv() {
            Err(_) => Response::Error(ZkError::SystemError),
            Ok(response) => response
        }
    }

    pub fn add_auth(&self, scheme: &str, auth: Vec<u8>) -> ZkResult<()> {
        let req = AuthRequest{typ: 0, scheme: scheme.to_string(), auth: auth};

        let result = self.request(OpCode::Auth, -4, req);

        fetch_empty_result!(result, Response::Auth)
    }

    pub fn create(&self, path: &str, data: Vec<u8>, acl: Vec<Acl>, mode: CreateMode) -> ZkResult<String> {
        let req = CreateRequest{path: path.to_string(), data: data, acl: acl, flags: mode as i32};

        let result = self.request(OpCode::Create, self.xid(), req);

        fetch_result!(result, Response::Create(path))
    }

    pub fn delete(&self, path: &str, version: i32) -> ZkResult<()> {
        let req = DeleteRequest{path: path.to_string(), version: version};

        let result = self.request(OpCode::Delete, self.xid(), req);

        fetch_empty_result!(result, Response::Delete)
    }

    pub fn exists(&self, path: &str, watch: bool) -> ZkResult<Stat> {
        let req = ExistsRequest{path: path.to_string(), watch: watch};

        let result = self.request(OpCode::Exists, self.xid(), req);

        fetch_result!(result, Response::Exists(stat))
    }

    pub fn get_acl(&self, path: &str) -> ZkResult<(Vec<Acl>, Stat)> {
        let req = GetAclRequest{path: path.to_string()};

        let result = self.request(OpCode::GetAcl, self.xid(), req);

        fetch_result!(result, Response::GetAcl(acl_stat))
    }

    pub fn get_children(&self, path: &str, watch: bool) -> ZkResult<Vec<String>> {
        let req = GetChildrenRequest{path: path.to_string(), watch: watch};

        let result = self.request(OpCode::GetChildren, self.xid(), req);

        fetch_result!(result, Response::GetChildren(children))
    }

    pub fn get_data(&self, path: &str, watch: bool) -> ZkResult<(Vec<u8>, Stat)> {
        let req = GetDataRequest{path: path.to_string(), watch: watch};

        let result = self.request(OpCode::GetData, self.xid(), req);

        fetch_result!(result, Response::GetData(data_stat))
    }

    pub fn set_acl(&self, path: &str, acl: Vec<Acl>, version: i32) -> ZkResult<Stat> {
        let req = SetAclRequest{path: path.to_string(), acl: acl, version: version};

        let result = self.request(OpCode::SetAcl, self.xid(), req);

        fetch_result!(result, Response::SetAcl(stat))
    }

    pub fn set_data(&self, path: &str, data: Vec<u8>, version: i32) -> ZkResult<Stat> {
        let req = SetDataRequest{path: path.to_string(), data: data, version: version};

        let result = self.request(OpCode::SetData, self.xid(), req);

        fetch_result!(result, Response::SetData(stat))
    }

    pub fn close(&self) {
        self.request(OpCode::CloseSession, 0, EmptyRequest);

        self.running.store(false, Ordering::SeqCst);
    }
}
<commit_msg>removed handcrafted impl Copy, using derive<commit_after>use consts::*;
use proto::*;

use std::old_io::{IoResult, MemReader, MemWriter, Reader, TcpStream};
use std::old_io::net::ip::SocketAddr;
use std::old_io::timer::Timer;
use std::num::FromPrimitive;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender, SyncSender, sync_channel};
use std::time::Duration;
use std::thread;

macro_rules! fetch_result(
    ($res:ident, $enu:ident::$hack:ident($item:ident)) => (
        match $res {
            $enu::$hack(response) => Ok(response.$item),
            Response::Error(e) => Err(e),
            _ => Err(ZkError::SystemError)
        }
    )
);

macro_rules! fetch_empty_result(
    ($res:ident, $enu:ident::$hack:ident) => (
        match $res {
            $enu::$hack => Ok(()),
            Response::Error(e) => Err(e),
            _ => Err(ZkError::SystemError)
        }
    )
);

#[derive(Clone, Copy, Debug)]
enum OpCode {
    Auth = 100,
    Create = 1,
    Delete = 2,
    Exists = 3,
    GetAcl = 6,
    SetAcl = 7,
    GetChildren = 8,
    GetData = 4,
    SetData = 5,
    Ping = 11,
    CloseSession = -11
}

pub trait Watcher: Send {
    fn handle(&self, &WatchedEvent);
}

pub type ZkResult<T> = Result<T, ZkError>;

struct Packet {
    opcode: OpCode,
    data: Vec<u8>,
    resp_tx: SyncSender<Response>
}

#[derive(Clone)]
pub struct ZooKeeper {
    xid: Arc<AtomicIsize>,
    running: Arc<AtomicBool>,
    packet_tx: Sender<Packet> // sending Packets from methods to writer task
}

impl ZooKeeper {

    pub fn connect<'a, W: 'static + Watcher>(connect_string: &'a str, timeout: Duration, watcher: W) -> ZkResult<ZooKeeper> {

        // communicating reader socket from writer to reader task
        let (reader_sock_tx, reader_sock_rx) = sync_channel(0);
        // comminucating requests (as Packets) from instance methods to writer task
        let (packet_tx, packet_rx): (Sender<Packet>, Receiver<Packet>) = channel();
        // communicating sent Packets from writer task to the reader task
        let (written_tx, written_rx) = channel();
        // event channel for passing WatchedEvents to watcher on a seperate task
        let (event_tx, event_rx) = channel();
        // event channel for passing WatchedEvents that the client should generate
        let event_tx_manual = event_tx.clone();

        let running = Arc::new(AtomicBool::new(true));
        let running1 = running.clone();

        let hosts = connect_string.split(',').map(|host| host.parse::<SocketAddr>().unwrap()).collect();

        thread::spawn(move || {
            println!("Event: thread started");

            loop {
                match event_rx.recv() {
                    Ok(event) => watcher.handle(&event),
                    Err(_) => return
                }
            }
        });

        thread::spawn(move || {
            println!("Writer: thread started");

            let mut timer = Timer::new().unwrap();
            let ping_timeout = timer.periodic(timeout);
            let mut writer_sock;
            let mut conn_resp = ConnectResponse::initial(timeout);

            loop {
                println!("Writer: connecting, trying to get new writer_sock");
                let (new_writer_sock, new_conn_resp) = match running.load(Ordering::SeqCst) {
                    true => ZooKeeper::reconnect(&hosts, conn_resp),
                    false => {
                        println!("Writer: exiting");
                        return
                    }
                };
                writer_sock = new_writer_sock;
                conn_resp = new_conn_resp;

                // pass a clone of the writer socket to the reader thread, will be used for reading
                match reader_sock_tx.send(writer_sock.clone()) {
                    Err(_) => panic!("Writer: failed to pass new socket to reader thread"),
                    _ => ()
                }

                // Send Connected event through event thread to the user
                let keeper_state = if conn_resp.read_only {
                    KeeperState::ConnectedReadOnly
                } else {
                    KeeperState::SyncConnected
                };
                event_tx_manual.send(
                    WatchedEvent{
                        event_type: WatchedEventType::None,
                        keeper_state: keeper_state,
                        path: None}
                ).unwrap();

                loop {
                    // do we have something to send or do we need to ping?
                    select! {
                        res = packet_rx.recv() => {
                            let packet = match res {
                                Ok(packet) => packet,
                                Err(_) => return
                            };
                            let res = writer_sock.write_buffer(&packet.data);
                            if res.is_err() {
                                println!("Writer: failed to send to server, trying to reconnect");
                                break;
                            }
                            let opcode = packet.opcode;
                            match written_tx.send(packet) {
                                Err(_) => panic!("Writer: failed to communicate with reader thread"),
                                _ => ()
                            }
                            match opcode {
                                OpCode::CloseSession => {
                                    println!("Writer: exiting");
                                    return
                                },
                                _ => ()
                            }
                        },
                        _ = ping_timeout.recv() => {
                            println!("Writer: Pinging {}", writer_sock.peer_name().unwrap());
                            let ping = RequestHeader{xid: -2, opcode: OpCode::Ping as i32}.to_byte_vec();
                            let res = writer_sock.write_buffer(&ping);
                            if res.is_err() {
                                println!("Writer: failed to ping server, trying to reconnect");
                                break;
                            }
                        }
                    };
                }
            }
        });

        thread::spawn(move || {
            println!("Reader: thread started");

            loop {
                println!("Reader: connecting, trying to get new reader_sock");
                let mut reader_sock = match reader_sock_rx.recv() {
                    Ok(sock) => sock,
                    Err(_) => {
                        println!("Reader: writer thread died, exiting");
                        return
                    }
                };

                loop {
                    let reply = ZooKeeper::read_reply(&mut reader_sock);
                    if reply.is_err() {
                        println!("Reader: read_reply {:?}", reply.err());
                        break;
                    }
                    let (reply_header, mut buf) = reply.unwrap();
                    match reply_header.xid {
                        -1 => event_tx.send(WatchedEvent::read_from(&mut buf)).unwrap(),
                        -2 => println!("Reader: got ping event"),
                        _xid => {
                            let packet = written_rx.recv().unwrap();
                            let result = ZooKeeper::parse_reply(reply_header.err, &packet, &mut buf);

                            match packet.resp_tx.send(result) {
                                Err(_) => println!("Reader: failed to pass back result to client thread"),
                                _ => ()
                            }
                         }
                    }
                }
            }
        });

        Ok(ZooKeeper{xid: Arc::new(AtomicIsize::new(1)), running: running1, packet_tx: packet_tx})
    }

    fn read_reply<R: Reader>(sock: &mut R) -> IoResult<(ReplyHeader, MemReader)> {
        let buf = try!(sock.read_buffer());
        let mut reader = MemReader::new(buf);
        Ok((ReplyHeader::read_from(&mut reader), reader))
    }

    fn parse_reply<R: Reader>(err: i32, packet: &Packet, buf: &mut R) -> Response {
        match err {
            0 => match packet.opcode {
                OpCode::Auth => Response::Auth,
                OpCode::CloseSession => Response::Close,
                OpCode::Create => Response::Create(CreateResponse::read_from(buf)),
                OpCode::Delete => Response::Delete,
                OpCode::Exists => Response::Exists(StatResponse::read_from(buf)),
                OpCode::GetAcl => Response::GetAcl(GetAclResponse::read_from(buf)),
                OpCode::SetAcl => Response::SetAcl(StatResponse::read_from(buf)),
                OpCode::GetChildren => Response::GetChildren(GetChildrenResponse::read_from(buf)),
                OpCode::GetData => Response::GetData(GetDataResponse::read_from(buf)),
                OpCode::SetData => Response::SetData(StatResponse::read_from(buf)),
                ref opcode => panic!("{:?}Response not implemented yet", opcode)
            },
            e => {
                Response::Error(FromPrimitive::from_i32(e).unwrap())
            }
        }
    }

    fn reconnect(hosts: &Vec<SocketAddr>, conn_resp: ConnectResponse) -> (TcpStream, ConnectResponse) {
        let conn_req = ConnectRequest::from(conn_resp, 0).to_byte_vec();

        loop {
            for host in hosts.iter() {
                println!("Writer: Connecting to {}...", host);
                let mut sock = TcpStream::connect_timeout(*host, Duration::seconds(1));
                if sock.is_err() {
                    println!("Writer: Failed to connect to {}", host);
                    continue;
                }

                let write = sock.write_buffer(&conn_req);
                if write.is_err() {
                    continue;
                }

                let read = sock.read_buffer();
                if read.is_err() {
                    continue;
                }

                let mut buf = MemReader::new(read.unwrap());
                let conn_resp = ConnectResponse::read_from(&mut buf);

                println!("Writer: Connection: {:?}", conn_resp);

                return (sock.unwrap(), conn_resp)
            }
        }
    }

    fn xid(&self) -> i32 {
        self.xid.fetch_add(1, Ordering::SeqCst) as i32
    }

    fn request<T: Archive>(&self, opcode: OpCode, xid: i32, req: T) -> Response {
        let rh = RequestHeader{xid: xid, opcode: opcode as i32};

        let mut buf = MemWriter::new();
        let _ = rh.write_to(&mut buf);
        let _ = req.write_to(&mut buf);

        let (resp_tx, resp_rx) = sync_channel(0);
        let packet = Packet{opcode: opcode, data: buf.into_inner(), resp_tx: resp_tx};

        if self.packet_tx.send(packet).is_err() {
            // writer thread died
            return Response::Error(ZkError::SystemError)
        }

        match resp_rx.recv() {
            Err(_) => Response::Error(ZkError::SystemError),
            Ok(response) => response
        }
    }

    pub fn add_auth(&self, scheme: &str, auth: Vec<u8>) -> ZkResult<()> {
        let req = AuthRequest{typ: 0, scheme: scheme.to_string(), auth: auth};

        let result = self.request(OpCode::Auth, -4, req);

        fetch_empty_result!(result, Response::Auth)
    }

    pub fn create(&self, path: &str, data: Vec<u8>, acl: Vec<Acl>, mode: CreateMode) -> ZkResult<String> {
        let req = CreateRequest{path: path.to_string(), data: data, acl: acl, flags: mode as i32};

        let result = self.request(OpCode::Create, self.xid(), req);

        fetch_result!(result, Response::Create(path))
    }

    pub fn delete(&self, path: &str, version: i32) -> ZkResult<()> {
        let req = DeleteRequest{path: path.to_string(), version: version};

        let result = self.request(OpCode::Delete, self.xid(), req);

        fetch_empty_result!(result, Response::Delete)
    }

    pub fn exists(&self, path: &str, watch: bool) -> ZkResult<Stat> {
        let req = ExistsRequest{path: path.to_string(), watch: watch};

        let result = self.request(OpCode::Exists, self.xid(), req);

        fetch_result!(result, Response::Exists(stat))
    }

    pub fn get_acl(&self, path: &str) -> ZkResult<(Vec<Acl>, Stat)> {
        let req = GetAclRequest{path: path.to_string()};

        let result = self.request(OpCode::GetAcl, self.xid(), req);

        fetch_result!(result, Response::GetAcl(acl_stat))
    }

    pub fn get_children(&self, path: &str, watch: bool) -> ZkResult<Vec<String>> {
        let req = GetChildrenRequest{path: path.to_string(), watch: watch};

        let result = self.request(OpCode::GetChildren, self.xid(), req);

        fetch_result!(result, Response::GetChildren(children))
    }

    pub fn get_data(&self, path: &str, watch: bool) -> ZkResult<(Vec<u8>, Stat)> {
        let req = GetDataRequest{path: path.to_string(), watch: watch};

        let result = self.request(OpCode::GetData, self.xid(), req);

        fetch_result!(result, Response::GetData(data_stat))
    }

    pub fn set_acl(&self, path: &str, acl: Vec<Acl>, version: i32) -> ZkResult<Stat> {
        let req = SetAclRequest{path: path.to_string(), acl: acl, version: version};

        let result = self.request(OpCode::SetAcl, self.xid(), req);

        fetch_result!(result, Response::SetAcl(stat))
    }

    pub fn set_data(&self, path: &str, data: Vec<u8>, version: i32) -> ZkResult<Stat> {
        let req = SetDataRequest{path: path.to_string(), data: data, version: version};

        let result = self.request(OpCode::SetData, self.xid(), req);

        fetch_result!(result, Response::SetData(stat))
    }

    pub fn close(&self) {
        self.request(OpCode::CloseSession, 0, EmptyRequest);

        self.running.store(false, Ordering::SeqCst);
    }
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

use utils;
use DiskExt;
use DiskType;

use libc::{c_char, c_void, statfs};
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::mem;
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStringExt;
use std::path::{Path, PathBuf};
use std::ptr;
use sys::ffi;

/// Struct containing a disk information.
pub struct Disk {
    type_: DiskType,
    name: OsString,
    file_system: Vec<u8>,
    mount_point: PathBuf,
    total_space: u64,
    available_space: u64,
}

impl DiskExt for Disk {
    fn get_type(&self) -> DiskType {
        self.type_
    }

    fn get_name(&self) -> &OsStr {
        &self.name
    }

    fn get_file_system(&self) -> &[u8] {
        &self.file_system
    }

    fn get_mount_point(&self) -> &Path {
        &self.mount_point
    }

    fn get_total_space(&self) -> u64 {
        self.total_space
    }

    fn get_available_space(&self) -> u64 {
        self.available_space
    }

    fn refresh(&mut self) -> bool {
        unsafe {
            let mut stat: statfs = mem::zeroed();
            let mount_point_cpath = utils::to_cpath(&self.mount_point);
            if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
                self.available_space = u64::from(stat.f_bsize) * stat.f_bavail;
                true
            } else {
                false
            }
        }
    }
}

macro_rules! unwrapper {
    ($b:expr, $ret:expr) => {{
        match $b {
            Ok(x) => x,
            _ => return $ret,
        }
    }};
}

static DISK_TYPES: once_cell::sync::Lazy<HashMap<OsString, DiskType>> =
    once_cell::sync::Lazy::new(get_disk_types);

fn get_disk_types() -> HashMap<OsString, DiskType> {
    let mut master_port: ffi::mach_port_t = 0;
    let mut media_iterator: ffi::io_iterator_t = 0;
    let mut ret = HashMap::with_capacity(1);

    unsafe {
        ffi::IOMasterPort(ffi::MACH_PORT_NULL, &mut master_port);

        let matching_dictionary = ffi::IOServiceMatching(b"IOMedia\0".as_ptr() as *const i8);
        let result = ffi::IOServiceGetMatchingServices(
            master_port,
            matching_dictionary,
            &mut media_iterator,
        );
        if result != ffi::KERN_SUCCESS as i32 {
            //println!("Error: IOServiceGetMatchingServices() = {}", result);
            return ret;
        }

        loop {
            let next_media = ffi::IOIteratorNext(media_iterator);
            if next_media == 0 {
                break;
            }
            let mut props = MaybeUninit::<ffi::CFMutableDictionaryRef>::uninit();
            let result = ffi::IORegistryEntryCreateCFProperties(
                next_media,
                props.as_mut_ptr(),
                ffi::kCFAllocatorDefault,
                0,
            );
            let props = props.assume_init();
            if result == ffi::KERN_SUCCESS as i32 && check_value(props, b"Whole\0") {
                let mut name: ffi::io_name_t = mem::zeroed();
                if ffi::IORegistryEntryGetName(next_media, name.as_mut_ptr() as *mut c_char)
                    == ffi::KERN_SUCCESS as i32
                {
                    ret.insert(
                        make_name(&name),
                        if check_value(props, b"RAID\0") {
                            DiskType::Unknown(-1)
                        } else {
                            DiskType::SSD
                        },
                    );
                }
                ffi::CFRelease(props as *mut _);
            }
            ffi::IOObjectRelease(next_media);
        }
        ffi::IOObjectRelease(media_iterator);
    }
    ret
}

fn make_name(v: &[u8]) -> OsString {
    for (pos, x) in v.iter().enumerate() {
        if *x == 0 {
            return OsStringExt::from_vec(v[0..pos].to_vec());
        }
    }
    OsStringExt::from_vec(v.to_vec())
}

pub(crate) fn get_disks() -> Vec<Disk> {
    unwrapper!(fs::read_dir("/Volumes"), Vec::new())
        .flat_map(|x| {
            if let Ok(ref entry) = x {
                let mount_point = utils::realpath(&entry.path());
                if mount_point.as_os_str().is_empty() {
                    None
                } else {
                    let name = entry.path().file_name()?.to_owned();
                    let type_ = DISK_TYPES
                        .get(&name)
                        .cloned()
                        .unwrap_or(DiskType::Unknown(-2));
                    Some(new_disk(name, &mount_point, type_))
                }
            } else {
                None
            }
        })
        .collect()
}

unsafe fn check_value(dict: ffi::CFMutableDictionaryRef, key: &[u8]) -> bool {
    let key = ffi::CFStringCreateWithCStringNoCopy(
        ptr::null_mut(),
        key.as_ptr() as *const c_char,
        ffi::kCFStringEncodingMacRoman,
        ffi::kCFAllocatorNull as *mut c_void,
    );
    let ret = ffi::CFDictionaryContainsKey(dict as ffi::CFDictionaryRef, key as *const c_void) != 0
        && *(ffi::CFDictionaryGetValue(dict as ffi::CFDictionaryRef, key as *const c_void)
            as *const ffi::Boolean)
            != 0;
    ffi::CFRelease(key as *const c_void);
    ret
}

fn new_disk(name: OsString, mount_point: &Path, type_: DiskType) -> Disk {
    let mount_point_cpath = utils::to_cpath(mount_point);
    let mut total_space = 0;
    let mut available_space = 0;
    let mut file_system = None;
    unsafe {
        let mut stat: statfs = mem::zeroed();
        if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
            total_space = u64::from(stat.f_bsize) * stat.f_blocks;
            available_space = stat.f_bfree * stat.f_blocks;
            let mut vec = Vec::with_capacity(stat.f_fstypename.len());
            for x in &stat.f_fstypename {
                if *x == 0 {
                    break;
                }
                vec.push(*x as u8);
            }
            file_system = Some(vec);
        }
    }
    Disk {
        type_,
        name,
        file_system: file_system.unwrap_or_else(|| b"<Unknown>".to_vec()),
        mount_point: mount_point.to_owned(),
        total_space,
        available_space,
    }
}
<commit_msg>Remove unneeded macro in mac disk file<commit_after>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

use utils;
use DiskExt;
use DiskType;

use libc::{c_char, c_void, statfs};
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::mem;
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStringExt;
use std::path::{Path, PathBuf};
use std::ptr;
use sys::ffi;

/// Struct containing a disk information.
pub struct Disk {
    type_: DiskType,
    name: OsString,
    file_system: Vec<u8>,
    mount_point: PathBuf,
    total_space: u64,
    available_space: u64,
}

impl DiskExt for Disk {
    fn get_type(&self) -> DiskType {
        self.type_
    }

    fn get_name(&self) -> &OsStr {
        &self.name
    }

    fn get_file_system(&self) -> &[u8] {
        &self.file_system
    }

    fn get_mount_point(&self) -> &Path {
        &self.mount_point
    }

    fn get_total_space(&self) -> u64 {
        self.total_space
    }

    fn get_available_space(&self) -> u64 {
        self.available_space
    }

    fn refresh(&mut self) -> bool {
        unsafe {
            let mut stat: statfs = mem::zeroed();
            let mount_point_cpath = utils::to_cpath(&self.mount_point);
            if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
                self.available_space = u64::from(stat.f_bsize) * stat.f_bavail;
                true
            } else {
                false
            }
        }
    }
}

static DISK_TYPES: once_cell::sync::Lazy<HashMap<OsString, DiskType>> =
    once_cell::sync::Lazy::new(get_disk_types);

fn get_disk_types() -> HashMap<OsString, DiskType> {
    let mut master_port: ffi::mach_port_t = 0;
    let mut media_iterator: ffi::io_iterator_t = 0;
    let mut ret = HashMap::with_capacity(1);

    unsafe {
        ffi::IOMasterPort(ffi::MACH_PORT_NULL, &mut master_port);

        let matching_dictionary = ffi::IOServiceMatching(b"IOMedia\0".as_ptr() as *const i8);
        let result = ffi::IOServiceGetMatchingServices(
            master_port,
            matching_dictionary,
            &mut media_iterator,
        );
        if result != ffi::KERN_SUCCESS as i32 {
            //println!("Error: IOServiceGetMatchingServices() = {}", result);
            return ret;
        }

        loop {
            let next_media = ffi::IOIteratorNext(media_iterator);
            if next_media == 0 {
                break;
            }
            let mut props = MaybeUninit::<ffi::CFMutableDictionaryRef>::uninit();
            let result = ffi::IORegistryEntryCreateCFProperties(
                next_media,
                props.as_mut_ptr(),
                ffi::kCFAllocatorDefault,
                0,
            );
            let props = props.assume_init();
            if result == ffi::KERN_SUCCESS as i32 && check_value(props, b"Whole\0") {
                let mut name: ffi::io_name_t = mem::zeroed();
                if ffi::IORegistryEntryGetName(next_media, name.as_mut_ptr() as *mut c_char)
                    == ffi::KERN_SUCCESS as i32
                {
                    ret.insert(
                        make_name(&name),
                        if check_value(props, b"RAID\0") {
                            DiskType::Unknown(-1)
                        } else {
                            DiskType::SSD
                        },
                    );
                }
                ffi::CFRelease(props as *mut _);
            }
            ffi::IOObjectRelease(next_media);
        }
        ffi::IOObjectRelease(media_iterator);
    }
    ret
}

fn make_name(v: &[u8]) -> OsString {
    for (pos, x) in v.iter().enumerate() {
        if *x == 0 {
            return OsStringExt::from_vec(v[0..pos].to_vec());
        }
    }
    OsStringExt::from_vec(v.to_vec())
}

pub(crate) fn get_disks() -> Vec<Disk> {
    match fs::read_dir("/Volumes") {
        Ok(d) => d
            .flat_map(|x| {
                if let Ok(ref entry) = x {
                    let mount_point = utils::realpath(&entry.path());
                    if mount_point.as_os_str().is_empty() {
                        None
                    } else {
                        let name = entry.path().file_name()?.to_owned();
                        let type_ = DISK_TYPES
                            .get(&name)
                            .cloned()
                            .unwrap_or(DiskType::Unknown(-2));
                        Some(new_disk(name, &mount_point, type_))
                    }
                } else {
                    None
                }
            })
            .collect(),
        _ => Vec::new(),
    }
}

unsafe fn check_value(dict: ffi::CFMutableDictionaryRef, key: &[u8]) -> bool {
    let key = ffi::CFStringCreateWithCStringNoCopy(
        ptr::null_mut(),
        key.as_ptr() as *const c_char,
        ffi::kCFStringEncodingMacRoman,
        ffi::kCFAllocatorNull as *mut c_void,
    );
    let ret = ffi::CFDictionaryContainsKey(dict as ffi::CFDictionaryRef, key as *const c_void) != 0
        && *(ffi::CFDictionaryGetValue(dict as ffi::CFDictionaryRef, key as *const c_void)
            as *const ffi::Boolean)
            != 0;
    ffi::CFRelease(key as *const c_void);
    ret
}

fn new_disk(name: OsString, mount_point: &Path, type_: DiskType) -> Disk {
    let mount_point_cpath = utils::to_cpath(mount_point);
    let mut total_space = 0;
    let mut available_space = 0;
    let mut file_system = None;
    unsafe {
        let mut stat: statfs = mem::zeroed();
        if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
            total_space = u64::from(stat.f_bsize) * stat.f_blocks;
            available_space = stat.f_bfree * stat.f_blocks;
            let mut vec = Vec::with_capacity(stat.f_fstypename.len());
            for x in &stat.f_fstypename {
                if *x == 0 {
                    break;
                }
                vec.push(*x as u8);
            }
            file_system = Some(vec);
        }
    }
    Disk {
        type_,
        name,
        file_system: file_system.unwrap_or_else(|| b"<Unknown>".to_vec()),
        mount_point: mount_point.to_owned(),
        total_space,
        available_space,
    }
}
<|endoftext|>


<commit_before>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = num_cpus::get();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "rules/casile.mk"
    )));
    let rules = status::get_rules()?;
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "rules/rules.mk"
    )));
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&target);
    let mut targets: Vec<_> = target.into_iter().collect();
    if status::is_gha()? {
        targets.push(String::from("_gha"));
        if targets.len() == 1 {
            targets.push(String::from("default"));
        }
        targets.push(String::from("install-dist"));
    }
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!(errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if targets.contains(&"debug".into()) {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<commit_msg>fix(actions): Inject GHA specific targets in time to execute<commit_after>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = num_cpus::get();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "rules/casile.mk"
    )));
    let rules = status::get_rules()?;
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if status::is_gha()? {
        targets.push(String::from("_gha"));
        if targets.len() == 1 {
            targets.push(String::from("default"));
        }
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!(errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if targets.contains(&"debug".into()) {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::errors;
use crate::providers;
use crate::providers::aws::AwsProvider;
use crate::providers::azure::Azure;
use crate::providers::cloudstack::configdrive::ConfigDrive;
use crate::providers::cloudstack::network::CloudstackNetwork;
use crate::providers::digitalocean::DigitalOceanProvider;
use crate::providers::gcp::GcpProvider;
use crate::providers::openstack::network::OpenstackProvider;
use crate::providers::packet::PacketProvider;
use crate::providers::vagrant_virtualbox::VagrantVirtualboxProvider;

macro_rules! box_result {
    ($exp:expr) => {
        Ok(Box::new($exp))
    };
}

/// `fetch_metadata` is the generic, top-level function that is used by the main
/// function to fetch metadata. The configured provider is passed in and this
/// function dispatches the call to the correct provider-specific fetch function
pub fn fetch_metadata(provider: &str) -> errors::Result<Box<providers::MetadataProvider>> {
    match provider {
        #[cfg(not(feature = "cl-legacy"))]
        "aws" => box_result!(AwsProvider::try_new()?),
        "azure" => box_result!(Azure::try_new()?),
        "cloudstack-metadata" => box_result!(CloudstackNetwork::try_new()?),
        "cloudstack-configdrive" => box_result!(ConfigDrive::try_new()?),
        "digitalocean" => box_result!(DigitalOceanProvider::try_new()?),
        #[cfg(feature = "cl-legacy")]
        "ec2" => box_result!(AwsProvider::try_new()?),
        #[cfg(feature = "cl-legacy")]
        "gce" => box_result!(GcpProvider::try_new()?),
        #[cfg(not(feature = "cl-legacy"))]
        "gcp" => box_result!(GcpProvider::try_new()?),
        "openstack-metadata" => box_result!(OpenstackProvider::try_new()?),
        "packet" => box_result!(PacketProvider::try_new()?),
        "vagrant-virtualbox" => box_result!(VagrantVirtualboxProvider::new()),
        _ => Err(errors::ErrorKind::UnknownProvider(provider.to_owned()).into()),
    }
}
<commit_msg>metadata.rs: Add `dyn` to remove warning<commit_after>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::errors;
use crate::providers;
use crate::providers::aws::AwsProvider;
use crate::providers::azure::Azure;
use crate::providers::cloudstack::configdrive::ConfigDrive;
use crate::providers::cloudstack::network::CloudstackNetwork;
use crate::providers::digitalocean::DigitalOceanProvider;
use crate::providers::gcp::GcpProvider;
use crate::providers::openstack::network::OpenstackProvider;
use crate::providers::packet::PacketProvider;
use crate::providers::vagrant_virtualbox::VagrantVirtualboxProvider;

macro_rules! box_result {
    ($exp:expr) => {
        Ok(Box::new($exp))
    };
}

/// `fetch_metadata` is the generic, top-level function that is used by the main
/// function to fetch metadata. The configured provider is passed in and this
/// function dispatches the call to the correct provider-specific fetch function
pub fn fetch_metadata(provider: &str) -> errors::Result<Box<dyn providers::MetadataProvider>> {
    match provider {
        #[cfg(not(feature = "cl-legacy"))]
        "aws" => box_result!(AwsProvider::try_new()?),
        "azure" => box_result!(Azure::try_new()?),
        "cloudstack-metadata" => box_result!(CloudstackNetwork::try_new()?),
        "cloudstack-configdrive" => box_result!(ConfigDrive::try_new()?),
        "digitalocean" => box_result!(DigitalOceanProvider::try_new()?),
        #[cfg(feature = "cl-legacy")]
        "ec2" => box_result!(AwsProvider::try_new()?),
        #[cfg(feature = "cl-legacy")]
        "gce" => box_result!(GcpProvider::try_new()?),
        #[cfg(not(feature = "cl-legacy"))]
        "gcp" => box_result!(GcpProvider::try_new()?),
        "openstack-metadata" => box_result!(OpenstackProvider::try_new()?),
        "packet" => box_result!(PacketProvider::try_new()?),
        "vagrant-virtualbox" => box_result!(VagrantVirtualboxProvider::new()),
        _ => Err(errors::ErrorKind::UnknownProvider(provider.to_owned()).into()),
    }
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/N-queens_problem

extern crate test;

use std::vec::Vec;
use std::iter::AdditiveIterator;

#[cfg(test)]
use test::Bencher;

#[cfg(not(test))]
fn main() {
    for num in range(0i32, 16i32) {
        println!("Sequential: {}: {}", num, nQueens(num));
    }
    for num in range(0i32, 16i32) {
        println!("Parallel: {}: {}", num, semiParallelNQueens(num));
    }
}

/*           _
   ___  ___ | |_   _____ _ __
  / __|/ _ \| \ \ / / _ \ '__/
  \__ \ (_) | |\ V /  __/ |
  |___/\___/|_| \_/ \___|_|

*/

// Solves n-queens using a depth-first, backtracking
// solution. Returns the number of solutions for a
// given n.
fn nQueens(n: i32) -> uint {
    // Pass off to our helper function.
    return nQueensHelper((1 << n) -1, 0, 0, 0);
}

// The meat of the algorithm is in here, a recursive helper function
// that actually computes the answer using a depth-first, backtracking
// algorithm.
//
// The 30,000 foot overview is as follows:
//
// This function takes only 3 important parameters: three integers
// which represent the spots on the current row that are blocked
// by previous queens.
//
// The "secret sauce" here is that we can avoid passing around the board
// or even the locations of the previous queens and instead we use this
// information to infer the conflicts for the next row.
//
// Once we know the conflicts in our current row we can simply recurse
// over all of the open spots and profit.
//
// This implementation is optimized for speed and memory by using
// integers and bit shifting instead of arrays for storing the conflicts.
fn nQueensHelper(allOnes: i32, leftDiags: i32, columns: i32, rightDiags: i32) -> uint {
    // allOnes is a special value that simply has all 1s in the first
    // n positions and 0s elsewhere. We can use it to clear out
    // areas that we don't care about.

    // Our solution count.
    // This will be updated by the recursive calls to our helper.
    let mut solutions = 0;

    // We get validSpots with some bit trickery. Effectively, each
    // of the parameters can be ORed together to create
    // an integer with all the conflicts together, which we then
    // invert and limit by ANDing with allOnes, our special value from
    // earlier.
    let mut validSpots = !(leftDiags | columns | rightDiags) & allOnes;

    // Since validSpots contains 1s in all of the locations that
    // are conflict-free, we know we have gone through all of
    // those locations when validSpots is all 0s, i.e. when it is
    // 0.
    while validSpots != 0 {
        // This is just bit trickery. For reasons involving the weird
        // behavior of two's complement integers, this creates an integer
        // which is all 0s except for a single 1 in the position of the
        // LSB of validSpots.
        let spot = -validSpots & validSpots;

        // We then XOR that integer with the validSpots to flip it to 0
        // in validSpots.
        validSpots = validSpots ^ spot;

        // Make a recursive call. This is where we infer the conflicts
        // for the next row.
        solutions += nQueensHelper(
            allOnes,
            // We add a conflict in the current spot and then shift left,
            // which has the desired effect of moving all of the conflicts
            // that are created by left diagonals to the left one square.
            (leftDiags | spot) << 1,

            // For columns we simply mark this column as filled by ORing
            // in the currentSpot.
            (columns | spot),

            // This is the same as the leftDiag shift, except we shift
            // right because these conflicts are caused by right
            // diagonals.
            (rightDiags | spot) >> 1);
    }

    // If columns is all blocked (i.e. if it is all ones) then we
    // have arrived at a solution because we have placed n queens.
    return solutions + ((columns == allOnes) as uint)
}

// This is the same as the regular nQueens except it creates
// n threads in which to to do the work.
//
// This is much slower for smaller numbers (under 16~17) but overcomes
// the sequential algorithm after that.
fn semiParallelNQueens(n: i32) -> uint {
    let allOnes = (1 << n) - 1;
    let columns = 0;
    let leftDiags = 0;
    let rightDiags = 0;

    let mut receivers = Vec::new();
    let mut validSpots = !(leftDiags | columns | rightDiags) & allOnes;
    while validSpots != 0 {
        let spot = -validSpots & validSpots;
        validSpots = validSpots ^ spot;
        let (tx, rx) = channel();
        receivers.push(rx);
        spawn(proc() {
            tx.send(nQueensHelper(
                allOnes,
                (leftDiags | spot) << 1,
                (columns | spot),
                (rightDiags | spot) >> 1));
        });
    }

    let mut results = Vec::new();
    for receiver in receivers.iter() {
        results.push(receiver.recv());
    }
    return results.iter().map(|&x| x).sum() + ((columns == allOnes) as uint)
}

// Tests

#[test]
fn test_nQueens() {
    let real = vec!(1, 1, 0, 0, 2, 10, 4, 40, 92u);
    for num in range(0, 9i32) {
        assert!(nQueens(num) == *real.get(num as uint));
    }
}

#[test]
fn test_parallel_nQueens() {
    let real = vec!(1, 1, 0, 0, 2, 10, 4, 40, 92u);
    for num in range(0, 9i32) {
        assert!(semiParallelNQueens(num) == *real.get(num as uint));
    }
}

#[bench]
fn bench_nQueens(b: &mut Bencher) {
    b.iter(|| { test::black_box(nQueens(16)); });
}

#[bench]
fn bench_semiParallelNQueens(b: &mut Bencher) {
    b.iter(|| { test::black_box(semiParallelNQueens(16)); });
}
<commit_msg>n_queens: Stylistic updates.<commit_after>// Implements http://rosettacode.org/wiki/N-queens_problem

extern crate test;

use std::vec::Vec;
use std::iter::AdditiveIterator;

#[cfg(test)]
use test::Bencher;

#[cfg(not(test))]
fn main() {
    for num in range(0i32, 16) {
        println!("Sequential: {}: {}", num, n_queens(num));
    }
    for num in range(0i32, 16) {
        println!("Parallel: {}: {}", num, semi_parallel_n_queens(num));
    }
}

/*           _
   ___  ___ | |_   _____ _ __
  / __|/ _ \| \ \ / / _ \ '__/
  \__ \ (_) | |\ V /  __/ |
  |___/\___/|_| \_/ \___|_|

*/

// Solves n-queens using a depth-first, backtracking solution.
// Returns the number of solutions for a given n.
fn n_queens(n: i32) -> uint {
    // Pass off to our helper function.
    return n_queens_helper((1 << n) -1, 0, 0, 0);
}

// The meat of the algorithm is in here, a recursive helper function
// that actually computes the answer using a depth-first, backtracking
// algorithm.
//
// The 30,000 foot overview is as follows:
//
// This function takes only 3 important parameters: three integers
// which represent the spots on the current row that are blocked
// by previous queens.
//
// The "secret sauce" here is that we can avoid passing around the board
// or even the locations of the previous queens and instead we use this
// information to infer the conflicts for the next row.
//
// Once we know the conflicts in our current row we can simply recurse
// over all of the open spots and profit.
//
// This implementation is optimized for speed and memory by using
// integers and bit shifting instead of arrays for storing the conflicts.
fn n_queens_helper(all_ones: i32, left_diags: i32, columns: i32, right_diags: i32) -> uint {
    // all_ones is a special value that simply has all 1s in the first n positions
    // and 0s elsewhere. We can use it to clear out areas that we don't care about.

    // Our solution count.
    // This will be updated by the recursive calls to our helper.
    let mut solutions = 0;

    // We get validSpots with some bit trickery. Effectively, each of the parameters
    // can be ORed together to create an integer with all the conflicts together,
    // which we then invert and limit by ANDing with all_ones, our special value
    //from earlier.
    let mut valid_spots = !(left_diags | columns | right_diags) & all_ones;

    // Since valid_spots contains 1s in all of the locations that
    // are conflict-free, we know we have gone through all of
    // those locations when valid_spots is all 0s, i.e. when it is 0.
    while valid_spots != 0 {
        // This is just bit trickery. For reasons involving the weird
        // behavior of two's complement integers, this creates an integer
        // which is all 0s except for a single 1 in the position of the
        // LSB of valid_spots.
        let spot = -valid_spots & valid_spots;

        // We then XOR that integer with the validSpots to flip it to 0
        // in valid_spots.
        valid_spots = valid_spots ^ spot;

        // Make a recursive call. This is where we infer the conflicts
        // for the next row.
        solutions += n_queens_helper(
            all_ones,
            // We add a conflict in the current spot and then shift left,
            // which has the desired effect of moving all of the conflicts
            // that are created by left diagonals to the left one square.
            (left_diags | spot) << 1,

            // For columns we simply mark this column as filled by ORing
            // in the currentSpot.
            (columns | spot),

            // This is the same as the left_diag shift, except we shift
            // right because these conflicts are caused by right diagonals.
            (right_diags | spot) >> 1);
    }

    // If columns is all blocked (i.e. if it is all ones) then we
    // have arrived at a solution because we have placed n queens.
    solutions + ((columns == all_ones) as uint)
}

// This is the same as the regular nQueens except it creates
// n threads in which to to do the work.
//
// This is much slower for smaller numbers (under 16~17) but outperforms
// the sequential algorithm after that.
fn semi_parallel_n_queens(n: i32) -> uint {
    let all_ones = (1 << n) - 1;
    let (columns, left_diags, right_diags) = (0, 0, 0);

    let mut receivers = Vec::new();
    let mut valid_spots = !(left_diags | columns | right_diags) & all_ones;
    while valid_spots != 0 {
        let (tx, rx) = channel();
        let spot = -valid_spots & valid_spots;
        valid_spots = valid_spots ^ spot;
        receivers.push(rx);

        spawn(proc() {
            tx.send(n_queens_helper(all_ones,
                                    (left_diags | spot) << 1,
                                    (columns | spot),
                                    (right_diags | spot) >> 1));
        });
    }

    receivers.iter().map(|r| r.recv()).sum() + ((columns == all_ones) as uint)
}

// Tests

#[test]
fn test_n_queens() {
    let real = vec!(1, 1, 0, 0, 2, 10, 4, 40, 92u);
    for num in range(0, 9i32) {
        assert_eq!(n_queens(num), *real.get(num as uint));
    }
}

#[test]
fn test_parallel_n_queens() {
    let real = vec!(1, 1, 0, 0, 2, 10, 4, 40, 92u);
    for num in range(0, 9i32) {
        assert_eq!(semi_parallel_n_queens(num), *real.get(num as uint));
    }
}

#[bench]
fn bench_n_queens(b: &mut Bencher) {
    b.iter(|| { test::black_box(n_queens(16)); });
}

#[bench]
fn bench_semi_parallel_n_queens(b: &mut Bencher) {
    b.iter(|| { test::black_box(semi_parallel_n_queens(16)); });
}
<|endoftext|>


<commit_before>use std::fmt::{self, Display, Formatter};
use std::str;

#[derive(Debug, PartialEq)]
pub enum MarkupDisplay<T>
where
    T: Display,
{
    Safe(T),
    Unsafe(T),
}

impl<T> MarkupDisplay<T>
where
    T: Display,
{
    pub fn mark_safe(self) -> MarkupDisplay<T> {
        match self {
            MarkupDisplay::Unsafe(t) => MarkupDisplay::Safe(t),
            _ => self,
        }
    }
}

impl<T> From<T> for MarkupDisplay<T>
where
    T: Display,
{
    fn from(t: T) -> MarkupDisplay<T> {
        MarkupDisplay::Unsafe(t)
    }
}

impl<T> Display for MarkupDisplay<T>
where
    T: Display,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match *self {
            MarkupDisplay::Unsafe(ref t) => escape(&t.to_string()).fmt(f),
            MarkupDisplay::Safe(ref t) => t.fmt(f),
        }
    }
}

pub fn escape(s: &str) -> Escaped<'_> {
    Escaped {
        bytes: s.as_bytes(),
    }
}

macro_rules! escaping_body {
    ($start:ident, $i:ident, $fmt:ident, $_self:ident, $quote:expr) => {{
        if $start < $i {
            $fmt.write_str(unsafe { str::from_utf8_unchecked(&$_self.bytes[$start..$i]) })?;
        }
        $fmt.write_str($quote)?;
        $start = $i + 1;
    }};
}

pub struct Escaped<'a> {
    bytes: &'a [u8],
}

impl<'a> ::std::fmt::Display for Escaped<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut start = 0;
        for (i, b) in self.bytes.iter().enumerate() {
            if b.wrapping_sub(b'"') <= FLAG {
                match *b {
                    b'<' => escaping_body!(start, i, fmt, self, "<"),
                    b'>' => escaping_body!(start, i, fmt, self, ">"),
                    b'&' => escaping_body!(start, i, fmt, self, "&"),
                    b'"' => escaping_body!(start, i, fmt, self, """),
                    b'\'' => escaping_body!(start, i, fmt, self, "'"),
                    b'/' => escaping_body!(start, i, fmt, self, "/"),
                    _ => (),
                }
            }
        }
        fmt.write_str(unsafe { str::from_utf8_unchecked(&self.bytes[start..]) })?;
        Ok(())
    }
}

const FLAG: u8 = b'>' - b'"';

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_escape() {
        assert_eq!(escape("").to_string(), "");
        assert_eq!(escape("<&>").to_string(), "<&>");
        assert_eq!(escape("bla&").to_string(), "bla&");
        assert_eq!(escape("<foo").to_string(), "<foo");
        assert_eq!(escape("bla&h").to_string(), "bla&h");
    }
}
<commit_msg>Improved rendering time (#190)<commit_after>use std::fmt::{self, Display, Formatter};
use std::io::{self, prelude::*};
use std::str;

#[derive(Debug, PartialEq)]
pub enum MarkupDisplay<T>
where
    T: Display,
{
    Safe(T),
    Unsafe(T),
}

impl<T> MarkupDisplay<T>
where
    T: Display,
{
    pub fn mark_safe(self) -> MarkupDisplay<T> {
        match self {
            MarkupDisplay::Unsafe(t) => MarkupDisplay::Safe(t),
            _ => self,
        }
    }
}

impl<T> From<T> for MarkupDisplay<T>
where
    T: Display,
{
    fn from(t: T) -> MarkupDisplay<T> {
        MarkupDisplay::Unsafe(t)
    }
}

impl<T> Display for MarkupDisplay<T>
where
    T: Display,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match *self {
            MarkupDisplay::Unsafe(ref t) => {
                let mut w = EscapeWriter { fmt: f };
                write!(w, "{}", t).map_err(|_e| fmt::Error)
            }
            MarkupDisplay::Safe(ref t) => t.fmt(f),
        }
    }
}

pub struct EscapeWriter<'a, 'b: 'a> {
    fmt: &'a mut fmt::Formatter<'b>,
}

impl io::Write for EscapeWriter<'_, '_> {
    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
        let escaped = Escaped { bytes };
        escaped
            .fmt(self.fmt)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
        Ok(bytes.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

pub fn escape(s: &str) -> Escaped<'_> {
    Escaped {
        bytes: s.as_bytes(),
    }
}

macro_rules! escaping_body {
    ($start:ident, $i:ident, $fmt:ident, $_self:ident, $quote:expr) => {{
        if $start < $i {
            $fmt.write_str(unsafe { str::from_utf8_unchecked(&$_self.bytes[$start..$i]) })?;
        }
        $fmt.write_str($quote)?;
        $start = $i + 1;
    }};
}

pub struct Escaped<'a> {
    bytes: &'a [u8],
}

impl<'a> ::std::fmt::Display for Escaped<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut start = 0;
        for (i, b) in self.bytes.iter().enumerate() {
            if b.wrapping_sub(b'"') <= FLAG {
                match *b {
                    b'<' => escaping_body!(start, i, fmt, self, "<"),
                    b'>' => escaping_body!(start, i, fmt, self, ">"),
                    b'&' => escaping_body!(start, i, fmt, self, "&"),
                    b'"' => escaping_body!(start, i, fmt, self, """),
                    b'\'' => escaping_body!(start, i, fmt, self, "'"),
                    b'/' => escaping_body!(start, i, fmt, self, "/"),
                    _ => (),
                }
            }
        }
        fmt.write_str(unsafe { str::from_utf8_unchecked(&self.bytes[start..]) })?;
        Ok(())
    }
}

const FLAG: u8 = b'>' - b'"';

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_escape() {
        assert_eq!(escape("").to_string(), "");
        assert_eq!(escape("<&>").to_string(), "<&>");
        assert_eq!(escape("bla&").to_string(), "bla&");
        assert_eq!(escape("<foo").to_string(), "<foo");
        assert_eq!(escape("bla&h").to_string(), "bla&h");
    }
}
<|endoftext|>


<commit_before><commit_msg>Yeet flushes from Set/Del operations<commit_after><|endoftext|>


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

use crate::{shared::fake_natives::transaction as TXN, shared::Address};
use regex::{NoExpand, Regex};
use std::fs;
use std::path::Path;

fn txn(n: &str) -> String {
    format!("{}::{}::{}(", Address::LIBRA_CORE, TXN::MOD, n)
}

macro_rules! replace {
    ($input:ident, $pat:expr, $replacer:expr) => {{
        let regex = Regex::new($pat).unwrap();
        regex.replace_all(&$input, $replacer)
    }};
}

#[allow(clippy::trivial_regex)]
pub fn fix_syntax_and_write(out_path: &Path, contents: String) {
    let contents = replace!(contents, r"get_txn_sender\(", NoExpand(&txn(TXN::SENDER)));
    let contents = replace!(contents, r"assert\(", NoExpand(&txn(TXN::ASSERT)));
    let contents = replace!(contents, r"move\((\w+)\)", "move $1");
    let contents = replace!(contents, r"copy\((\w+)\)", "copy $1");
    let contents = replace!(contents, r"resource\s+(\w)", "resource struct $1");
    let contents = replace!(contents, r"import", NoExpand("use"));
    let contents = replace!(contents, r"Self\.", NoExpand(""));
    let contents = replace!(contents, r"(([A-Z]\w*)|(\}\})|(0x\d+))\.", "$1::");
    fs::write(out_path, contents.as_bytes()).unwrap();
}
<commit_msg>[move source language] Updated IR translation regex<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{shared::fake_natives::transaction as TXN, shared::Address};
use regex::{Captures, NoExpand, Regex};
use std::fs;
use std::path::Path;

fn txn(n: &str) -> String {
    format!("{}::{}::{}(", Address::LIBRA_CORE, TXN::MOD, n)
}

macro_rules! replace {
    ($input:ident, $pat:expr, $replacer:expr) => {{
        let regex = Regex::new($pat).unwrap();
        regex.replace_all(&$input, $replacer)
    }};
}

#[allow(clippy::trivial_regex)]
pub fn fix_syntax_and_write(out_path: &Path, contents: String) {
    // get_txn_sender() ~> 0x0::Transaction::sender()
    let contents = replace!(contents, r"get_txn_sender\(", NoExpand(&txn(TXN::SENDER)));
    // assert(c, u) ~> 0x0::Transaction::assert(c, u)
    let contents = replace!(contents, r"assert\(", NoExpand(&txn(TXN::ASSERT)));
    // move(x) ~> move x
    let contents = replace!(contents, r"move\((\w+)\)", "move $1");
    // copy(x) ~> copy x
    let contents = replace!(contents, r"copy\((\w+)\)", "copy $1");
    // resource StructName ~> resource struct StructName
    let contents = replace!(contents, r"resource\s+(\w)", "resource struct $1");
    // unrestricted ~> copyable
    let contents = replace!(contents, r":\s*unrestricted", NoExpand(": copyable"));
    // import ~> use
    let contents = replace!(contents, r"import", NoExpand("use"));
    // Self. is unnecessary
    let contents = replace!(contents, r"Self\.", NoExpand(""));
    // Module|Address. ~> Module|Address::
    let contents = replace!(contents, r"(([A-Z]\w*)|(\}\})|(0x\d+))\.", "$1::");
    // add fun keyword to functions
    let contents = replace!(
        contents,
        r"(((public|native| )*))(\w+\(.*\).*\{)",
        |cap: &Captures| format!("{}fun {}", &cap[1], &cap[4])
    );
    fs::write(out_path, contents.as_bytes()).unwrap();
}
<|endoftext|>