<file_sep>use notify;
use notify::{RecommendedWatcher, Watcher, RecursiveMode, op};
use notify::DebouncedEvent::*;
use std::sync::mpsc::channel;
use std::time::Duration;
use std::net::UdpSocket;
use std::net;
use std::env;
use std::path::PathBuf;
use std::process::exit;

pub fn listen_and_broadcast(socket: &UdpSocket, bind_addr: &net::SocketAddrV4) {
    println!("It's stream time");

    if let Err(e) = watch(socket, bind_addr) {
        println!("error: {:?}", e)
    }
}

fn watch(socket: &UdpSocket, bind_addr: &net::SocketAddrV4) -> notify::Result<()> {
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = try!(Watcher::new(tx, Duration::from_secs(2)));
    let path: PathBuf = env::current_dir().expect("Current directory is invalid for watching");

    try!(watcher.watch(path.as_path(), RecursiveMode::Recursive));

    loop {
        match rx.recv() {
            Ok(event) => {
                let data = data(event);
                println!("sending data to {}: {:?}", &bind_addr, &data);
                socket.send_to(data.as_bytes(), bind_addr).expect("couldn't send message");
            },
            Err(e) => println!("watch error: {:?}", e)
        }
    }
}

fn data(event: notify::DebouncedEvent) -> String {
    match event {
        NoticeWrite(x) => data_format(op::WRITE, x),
        NoticeRemove(x) => data_format(op::REMOVE, x),
        Create(x) => data_format(op::CREATE, x),
        Write(x) => data_format(op::WRITE, x),
        Chmod(x) => data_format(op::CHMOD, x),
        Remove(x) => data_format(op::REMOVE, x),
        Rename(_, x) => data_format(op::RENAME, x),
        Rescan => exit(1),
        Error(_, _) => exit(1)
   }
}

fn data_format(event: notify::Op, path: PathBuf) -> String {
    format!("{:?}|{}", event, path.display())
}
<file_sep>[package]
name = "ven"
description = "Virtual Event Network, to recreate file system events from a host machine onto a virtual machine."
version = "0.1.0"
authors = ["<NAME>"]

[dependencies]
docopt = "0.8.1"
serde = "1.0.16"
serde_derive = "1.0.16"
notify = "4.0.0"
<file_sep>#![feature(slice_patterns)]
#[macro_use]

extern crate serde_derive;
extern crate docopt;
extern crate notify;

mod actor;
mod stream;

use docopt::Docopt;
use std::process::exit;
use std::str::FromStr;
use std::net::UdpSocket;
use std::net;

static USAGE: &'static str = "
Usage:
    ven [--stream | --actor] -b <bind_address>
    ven [-s | -a]
    ven [-s | -a] -b <bind_address>
    ven (-h | --help)
    ven (-v | --version)

Flags:
    -s, --stream        Stream mode to stream OS events to a bound connection [default]
    -a, --actor         Actor mode for reading events form the port and triggering events
    -h, --help          Prints help information
    -v, --version       Prints version information

Options:
    -b <bind_address>       The address and port you want to send to and listen from [default: 127.0.0.1:34254]
    -r <broadcast_address>  A host socket to send from if broadcasting and receiving from the same machine. Not normally required [default: 127.0.0.1:45243]
";

#[derive(Debug, Deserialize)]
struct Args {
    flag_s: bool,
    flag_a: bool,
    flag_v: bool,
    flag_b: Option<String>,
    flag_r: Option<String>
}

fn addr_parser(maybe_addr: Option<String>) -> net::SocketAddrV4 {
    match net::SocketAddrV4::from_str(&maybe_addr.unwrap()) {
        Ok(x) => x,
        Err(e) => {
          println!("Could not parse the provided address as a valid IPv4: {}", e);
          exit(1)
        }
    }
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    let bind_addr = addr_parser(args.flag_b);
    let broadcast_addr = addr_parser(args.flag_r);

    if args.flag_v {
        println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
        exit(0);
    } else if args.flag_a {
        let socket = UdpSocket::bind(bind_addr).expect("couldn't bind to address");
        actor::listen_and_mimic(&socket);
    } else {
        let socket = UdpSocket::bind(broadcast_addr).expect("couldn't bind to address");
        stream::listen_and_broadcast(&socket, &bind_addr);
    };
}
<file_sep>ven - A virtual event network
=====

<img src="https://raw.githubusercontent.com/brianp/ven/master/ven.png" alt="A Venn Diagram containing nothing at all" align="right" />

## We're here to recreate file system events from a host machine onto a virtual machine. Quickly.
Ever ran a vm with a shared directory? Ever made a change to a file on the host
machine, expecting a fs event to trigger in the VM?

Then *ven* is for you.
(I'm looking at you vagrant shared directory and guard | grunt | gulp | watcher systems.)

## Setup

### It takes two

The program runs in one of two modes. Stream, or Actor. And requires you to run each
mode in a differnt place.

#### Stream

The stream mode binds to a local udp port (broadcast address) and sits around
listening for OS events. When it receives one it broadcasts the event to the
bind address. That's it. Listen, and yell.

#### Actor

The actor hangs around bound to the bind address just waiting for a juicy
event to come in. When it gets one, it tries its hardest to mimic it.
Recreating a local fs event on a different system.

### Setup the Stream on the system you'd like to capture events _from_

`ven --stream`

### Setup the Actor on the system you'd like to to trigger events _on_

`ven --actor`

Now sit back and watch it play out

## Usage Options

```shell
Usage:
    ven [--stream | --actor] -b <bind_address>
    ven [-s | -a]
    ven [-s | -a] -b <bind_address>
    ven (-h | --help)
    ven (-v | --version)

Flags:
    -s, --stream        Stream mode to stream OS events to a bound connection [default]
    -a, --actor         Actor mode for reading events form the port and triggering events
    -h, --help          Prints help information
    -v, --version       Prints version information

Options:
    -b <bind_address>       The address and port you want to send to and listen from [default: 127.0.0.1:34254]
    -r <broadcast_address>  A host socket to send from if broadcasting and receiving from the same machine. Not normally required [default: 127.0.0.1:45243]
```

## Copyright
Copyright (c) 2017-2018 <NAME>. See [LICENSE](https://github.com/brianp/ven/blob/master/LICENSE) for further details.
<file_sep>use std::net::UdpSocket;
use std::path::PathBuf;
use std::process::{Command, exit};
use notify::op;
use notify;

pub fn listen_and_mimic(socket: &UdpSocket) {
    println!("listening started, ready to accept");

    let mut buf = [0; 64000];
    println!("Reading data");

    loop {
        let result = socket.recv_from(&mut buf);

        drop(socket);

        match result {
            Ok((amt, src)) => {
                let data = Vec::from(&buf[..amt]);
                let string = String::from_utf8_lossy(&data);
                println!("received data from {}: {}", src, &string);
                execute(string.into_owned());
            },
            Err(err) => panic!("Read error: {}", err)
        }
     }
}

fn execute(data: String) {
    let vec: Vec<&str> = data.split("|").collect();
    let file: PathBuf = PathBuf::from(vec[1]);
    let file_str: &str = file.to_str().expect("Couldn't parse file path");

    match vec[0] {
        "CREATE" => {
            Command::new("sed")
                    .args(&["-n", "", "-i", file_str])
                    .spawn()
                    .expect("sed command failed to start");
        },
        &_ => {
            Command::new("touch")
                    .args(&["-r", file_str, file_str])
                    .spawn()
                    .expect("touch command failed to start");
        }
    }

}
