<commit_before>use parking_lot::Mutex;

use ctxt::VM;
use driver::cmd::Args;
use gc::{formatted_size, Address, Collector, GcReason, Region};
use os;

pub struct SweepCollector {
    heap: Region,
    alloc: Mutex<SweepAllocator>,
}

impl SweepCollector {
    pub fn new(args: &Args) -> SweepCollector {
        let heap_size = args.max_heap_size();
        let ptr = os::mmap(heap_size, os::Writable);

        if ptr.is_null() {
            panic!("could not allocate heap of size {} bytes", heap_size);
        }

        let heap_start = Address::from_ptr(ptr);
        let heap_end = heap_start.offset(heap_size);
        let heap = Region::new(heap_start, heap_end);

        if args.flag_gc_verbose {
            println!("GC: {} {}", heap, formatted_size(heap_size));
        }

        let alloc = SweepAllocator {
            top: heap_start,
            limit: heap_end,
        };

        SweepCollector {
            heap: heap,
            alloc: Mutex::new(alloc),
        }
    }
}

impl Collector for SweepCollector {
    fn supports_tlab(&self) -> bool {
        true
    }

    fn alloc_tlab_area(&self, _vm: &VM, _size: usize) -> Option<Region> {
        unimplemented!()
    }

    fn alloc(&self, _vm: &VM, _size: usize, _array_ref: bool) -> Address {
        unimplemented!()
    }

    fn collect(&self, _vm: &VM, _reason: GcReason) {
        unimplemented!();
    }

    fn minor_collect(&self, vm: &VM, reason: GcReason) {
        self.collect(vm, reason);
    }

    fn dump_summary(&self, _runtime: f32) {
        unimplemented!();
    }
}

impl Drop for SweepCollector {
    fn drop(&mut self) {
        os::munmap(self.heap.start.to_ptr(), self.heap.size());
    }
}

struct SweepAllocator {
    top: Address,
    limit: Address,
}
<commit_msg>sweep: started implementing collection<commit_after>use parking_lot::Mutex;

use ctxt::VM;
use driver::cmd::Args;
use gc::root::{get_rootset, Slot};
use gc::space::Space;
use gc::tlab;
use gc::{formatted_size, Address, CollectionStats, Collector, GcReason, Region};
use os;
use safepoint;
use timer::Timer;

pub struct SweepCollector {
    heap: Region,
    alloc: Mutex<SweepAllocator>,
    stats: Mutex<CollectionStats>,
}

impl SweepCollector {
    pub fn new(args: &Args) -> SweepCollector {
        let heap_size = args.max_heap_size();
        let ptr = os::mmap(heap_size, os::Writable);

        if ptr.is_null() {
            panic!("could not allocate heap of size {} bytes", heap_size);
        }

        let heap_start = Address::from_ptr(ptr);
        let heap_end = heap_start.offset(heap_size);
        let heap = Region::new(heap_start, heap_end);

        if args.flag_gc_verbose {
            println!("GC: {} {}", heap, formatted_size(heap_size));
        }

        let alloc = SweepAllocator {
            top: heap_start,
            limit: heap_end,
        };

        SweepCollector {
            heap: heap,
            alloc: Mutex::new(alloc),
            stats: Mutex::new(CollectionStats::new()),
        }
    }
}

impl Collector for SweepCollector {
    fn supports_tlab(&self) -> bool {
        true
    }

    fn alloc_tlab_area(&self, _vm: &VM, _size: usize) -> Option<Region> {
        unimplemented!()
    }

    fn alloc(&self, _vm: &VM, _size: usize, _array_ref: bool) -> Address {
        unimplemented!()
    }

    fn collect(&self, vm: &VM, reason: GcReason) {
        let mut timer = Timer::new(vm.args.flag_gc_stats);

        safepoint::stop_the_world(vm, |threads| {
            vm.perf_counters.stop();
            tlab::make_iterable_all(vm, threads);
            let rootset = get_rootset(vm, threads);
            self.mark_sweep(vm, &rootset, reason);
            vm.perf_counters.start();
        });

        if vm.args.flag_gc_stats {
            let duration = timer.stop();
            let mut stats = self.stats.lock();
            stats.add(duration);
        }
    }

    fn minor_collect(&self, vm: &VM, reason: GcReason) {
        self.collect(vm, reason);
    }

    fn dump_summary(&self, runtime: f32) {
        let stats = self.stats.lock();
        let (mutator, gc) = stats.percentage(runtime);

        println!("GC stats: total={:.1}", runtime);
        println!("GC stats: mutator={:.1}", stats.mutator(runtime));
        println!("GC stats: collection={:.1}", stats.pause());

        println!("");
        println!("GC stats: collection-count={}", stats.collections());
        println!("GC stats: collection-pauses={}", stats.pauses());

        println!(
            "GC summary: {:.1}ms collection ({}), {:.1}ms mutator, {:.1}ms total ({}% mutator, {}% GC)",
            stats.pause(),
            stats.collections(),
            stats.mutator(runtime),
            runtime,
            mutator,
            gc,
        );
    }
}

impl Drop for SweepCollector {
    fn drop(&mut self) {
        os::munmap(self.heap.start.to_ptr(), self.heap.size());
    }
}

impl SweepCollector {
    fn mark_sweep(&self, vm: &VM, rootset: &[Slot], reason: GcReason) {
        let start = self.heap.start;
        let top = self.alloc.lock().top;

        let mut collector = MarkSweep {
            vm: vm,
            heap: Region::new(start, top),
            perm_space: &vm.gc.perm_space,

            rootset: rootset,
            reason: reason,
        };

        collector.collect();
    }
}

struct MarkSweep<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    heap: Region,
    perm_space: &'a Space,

    rootset: &'a [Slot],
    reason: GcReason,
}

impl<'a, 'ast> MarkSweep<'a, 'ast> {
    fn collect(&mut self) {
        self.mark();
        self.sweep();
    }

    fn mark(&mut self) {
        let mut marking_stack: Vec<Address> = Vec::new();

        for root in self.rootset {
            let root_ptr = root.get();

            if self.heap.contains(root_ptr) {
                let root_obj = root_ptr.to_mut_obj();

                if !root_obj.header().is_marked_non_atomic() {
                    marking_stack.push(root_ptr);
                    root_obj.header_mut().mark_non_atomic();
                }
            } else {
                debug_assert!(root_ptr.is_null() || self.perm_space.contains(root_ptr));
            }
        }

        while marking_stack.len() > 0 {
            let object_addr = marking_stack.pop().expect("stack already empty");
            let object = object_addr.to_mut_obj();

            object.visit_reference_fields(|field| {
                let field_addr = field.get();

                if self.heap.contains(field_addr) {
                    let field_obj = field_addr.to_mut_obj();

                    if !field_obj.header().is_marked_non_atomic() {
                        marking_stack.push(field_addr);
                        field_obj.header_mut().mark_non_atomic();
                    }
                } else {
                    debug_assert!(field_addr.is_null() || self.perm_space.contains(field_addr));
                }
            });
        }
    }

    fn sweep(&mut self) {
        let start = self.heap.start;
        let end = self.heap.end;

        let mut scan = start;

        while scan < end {
            let object = scan.to_mut_obj();

            if object.header().vtblptr().is_null() {
                scan = scan.add_ptr(1);
                continue;
            }

            let object_size = object.size();

            if object.header().is_marked_non_atomic() {
                object.header_mut().unmark_non_atomic();
            } else {
                unimplemented!();
            }

            scan = scan.offset(object_size);
        }
    }
}

struct SweepAllocator {
    top: Address,
    limit: Address,
}
<|endoftext|>


<commit_before>#![crate_id="gdal#0.1"]
#![crate_type="lib"]
#![feature(unsafe_destructor)]

extern crate sync;
extern crate libc;
#[cfg(test)] extern crate test;

use std::str::raw;
use libc::c_char;

pub mod raster;
pub mod vector;
pub mod proj;
pub mod geom;
pub mod warp;

#[link(name="gdal")]
extern {
    fn GDALVersionInfo(key: *const c_char) -> *const c_char;
}

pub fn version_info(key: &str) -> String {
    let info = key.with_c_str(|c_key| {
        unsafe {
            let rv = GDALVersionInfo(c_key);
            return raw::from_c_str(rv);
        };
    });
    return info;
}

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

    #[test]
    fn test_version_info() {
        let release_date = version_info("RELEASE_DATE");
        let release_name = version_info("RELEASE_NAME");
        let version_text = version_info("--version");

        let expected_text: String = format!(
            "GDAL {}, released {}/{}/{}",
            release_name,
            release_date.as_slice().slice(0, 4),
            release_date.as_slice().slice(4, 6),
            release_date.as_slice().slice(6, 8),
        );

        assert_eq!(version_text.into_string(), expected_text);
    }
}
<commit_msg>rename tests module<commit_after>#![crate_id="gdal#0.1"]
#![crate_type="lib"]
#![feature(unsafe_destructor)]

extern crate sync;
extern crate libc;
#[cfg(test)] extern crate test;

use std::str::raw;
use libc::c_char;

pub mod raster;
pub mod vector;
pub mod proj;
pub mod geom;
pub mod warp;

#[link(name="gdal")]
extern {
    fn GDALVersionInfo(key: *const c_char) -> *const c_char;
}

pub fn version_info(key: &str) -> String {
    let info = key.with_c_str(|c_key| {
        unsafe {
            let rv = GDALVersionInfo(c_key);
            return raw::from_c_str(rv);
        };
    });
    return info;
}

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

    #[test]
    fn test_version_info() {
        let release_date = version_info("RELEASE_DATE");
        let release_name = version_info("RELEASE_NAME");
        let version_text = version_info("--version");

        let expected_text: String = format!(
            "GDAL {}, released {}/{}/{}",
            release_name,
            release_date.as_slice().slice(0, 4),
            release_date.as_slice().slice(4, 6),
            release_date.as_slice().slice(6, 8),
        );

        assert_eq!(version_text.into_string(), expected_text);
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write};
use std::fmt::Display;
use std::fs::{PathExt, File, create_dir_all, set_permissions};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{symlink, MetadataExt, PermissionsExt};
use std::path::Path;
use std::collections::{BTreeMap, BTreeSet};

use cronparse::Limited;
use cronparse::crontab::{CrontabEntry, SystemCrontabEntry, UserCrontabEntry};
use cronparse::schedule::{Schedule, Period, Calendar};
use cronparse::interval::Interval;

use pgs_files::passwd;

use super::REBOOT_FILE;

pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String>, path: &Path, dstdir: &Path) -> io::Result<()> {
    use cronparse::crontab::CrontabEntry::*;

    info!("generating units for {}: \"{}\", {:?}", path.display(), entry, env);

    let owner = try!(path.metadata()).uid();

    let mut persistent = env.get("PERSISTENT").and_then(|v| match &**v {
        "yes" | "true" | "1" => Some(true),
        "auto" | "" => None,
        _ => Some(false)
    }).unwrap_or_else(|| match entry {
        Anacron(_) | User(UserCrontabEntry { sched: Schedule::Period(_), .. }) | System(SystemCrontabEntry { sched: Schedule::Period(_), .. }) => true,
        _ => false
    });

    let batch = env.get("BATCH").map(|v| match &**v {
        "yes" | "true" | "1" => true,
        _ => false
    }).unwrap_or(false);

    let random_delay = env.get("RANDOM_DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(1);
    let mut delay = env.get("DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
    let hour = env.get("START_HOURS_RANGE").and_then(|v| v.splitn(1, '-').next().and_then(|v| v.parse::<u64>().ok())).unwrap_or(0);
    let shell = env.get("SHELL").map(|v| &**v).unwrap_or("/bin/sh");
    let daemon_reload = Path::new(REBOOT_FILE).is_file();

    let schedule = entry.period().and_then(|period| match *period {
        Period::Reboot => {
            persistent = false;
            if delay == 0 {
                delay = 1;
            }
            None
        },
        Period::Minutely => {
            persistent = false;
            Some("@minutely".to_owned())
        },
        Period::Hourly => {
            if delay == 0 {
                Some("@hourly".to_owned())
            } else {
                Some(format!("*-*-* *:{}:0", delay))
            }
        },
        Period::Midnight => {
            if delay == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* 0:{}:0", delay))
            }
        },
        Period::Daily => {
            if delay == 0 && hour == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Weekly => {
            if delay == 0 && hour == 0 {
                Some("@weekly".to_owned())
            } else {
                Some(format!("Mon *-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Monthly => {
            if delay == 0 && hour == 0 {
                Some("@monthly".to_owned())
            } else {
                Some(format!("*-*-1 {}:{}:0", hour, delay))
            }
        },
        Period::Quaterly => {
            if delay == 0 && hour == 0 {
                Some("@quaterly".to_owned())
            } else {
                Some(format!("*-1,4,7,10-1 {}:{}:0", hour, delay))
            }
        },
        Period::Biannually => {
            if delay == 0 && hour == 0 {
                Some("@semi-annually".to_owned())
            } else {
                Some(format!("*-1,7-1 {}:{}:0", hour, delay))
            }
        },
        Period::Yearly => {
            if delay == 0 && hour == 0 {
                Some("@yearly".to_owned())
            } else {
                Some(format!("*-1-1 {}:{}:0", hour, delay))
            }
        },
        Period::Days(days) => {
            // workaround for anacrontab
            if days > 31 {
                Some(format!("*-1/{}-1 {}:{}:0", days / 30, hour, delay))
            } else {
                Some(format!("*-*-1/{} {}:{}:0", days, hour, delay))
            }
        },
    }).or_else(|| entry.calendar().and_then(|cal| {
        let Calendar {
            ref dows,
            ref days,
            ref mons,
            ref hrs,
            ref mins
        } = *cal;

        Some(format!("{} *-{}-{} {}:{}:00",
                     linearize(&**dows),
                     linearize(&**mons),
                     linearize(&**days),
                     linearize(&**hrs),
                     linearize(&**mins)))
    }));

    if daemon_reload && schedule.is_none() {
        warn!("skipping job from {}: \"{}\"", path.display(), entry);
        return Ok(());
    }

    if let Some(cmd) = entry.command() {

        // make sure we know the user
        let user = try!(entry.user().and_then(passwd::get_entry_by_name).or_else(|| passwd::get_entry_by_uid(owner))
            .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown user")));

        // generate unique cron job id
        let mut md5ctx = ::md5::Context::new();
        md5ctx.consume(path.as_os_str().as_bytes());
        if let Some(ref schedule) = schedule {
            md5ctx.consume(schedule.as_bytes());
        }
        md5ctx.consume(cmd.as_bytes());
        let md5hex = tohex(&md5ctx.compute());

        // create service and timer unit names
        let service_unit_name = format!("cronjob-{}.service", md5hex);
        let timer_unit_name = format!("cronjob-{}.timer", md5hex);

        // unit paths
        let service_unit_path = dstdir.join(&service_unit_name);
        let timer_unit_path = dstdir.join(&timer_unit_name);

        // make sure cron.target.wants dir exists
        let cron_target_wants_path = dstdir.join("cron.target.wants");
        try!(create_dir_all(&cron_target_wants_path));

        // process command in case it should be put into script
        let command = if Path::new(cmd).is_file() {
            cmd.to_owned()
        } else {
            let script_command_path = dstdir.join(format!("cronjob-{}.sh", md5hex));

            debug!("generating script {:?} from {:?}", script_command_path, path);
            {
                let mut script_command_file = try!(File::create(&script_command_path));
                try!(writeln!(script_command_file, "#!{}", shell));
                try!(writeln!(script_command_file, "{}", cmd));
            }

            let mut perms = try!(script_command_path.metadata()).permissions();
            perms.set_mode(0o755);
            try!(set_permissions(&script_command_path, perms));
            script_command_path.to_str().unwrap().to_owned()
        };

        debug!("generating service {:?} from {:?}", service_unit_path, path);
        {
            let mut service_unit_file = try!(File::create(service_unit_path));

            try!(writeln!(service_unit_file, r###"[Unit]
Description=[Cron] "{entry}"
Documentation=man:systemd-crontab-generator(8)
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}"###,
                entry = entry,
                source_crontab_path = path.display(),
                ));

            if env.contains_key("MAILTO") {
                try!(writeln!(service_unit_file, "OnFailure=cron-failure@%i.service"));
            }

            try!(writeln!(service_unit_file, r###"
[Service]
Type=oneshot
IgnoreSIGPIPE=false
ExecStart={command}"###,
                command = command,
                ));

            if user.uid != 0 {
                try!(writeln!(service_unit_file, "User={}", user.name));
            }

            if let Some(group) = entry.group() {
                try!(writeln!(service_unit_file, "Group={}", group));
            }
            if batch {
                try!(writeln!(service_unit_file, "CPUSchedulingPolicy=idle"));
                try!(writeln!(service_unit_file, "IOSchedulingClass=idle"));
            }

            if !env.is_empty() {
                try!(write!(service_unit_file, "Environment="));
                for (name, value) in env.iter() {
                    try!(write!(service_unit_file, r#""{}={}""#, name, value));
                }
                try!(write!(service_unit_file, "\n"));
            }
        }

        debug!("generating timer {:?} from {:?}", timer_unit_path, path);
        {
            let mut timer_unit_file = try!(File::create(&timer_unit_path));

            try!(writeln!(timer_unit_file, r###"[Unit]
Description=[Timer] "{entry}"
Documentation=man:systemd-crontab-generator(8)
PartOf=cron.target
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}

[Timer]
Unit={service_unit_name}
Persistent={persistent}"###,
                entry = entry,
                source_crontab_path = path.display(),
                service_unit_name = service_unit_name,
                persistent = persistent,
                ));

            if let Some(schedule) = schedule {
                try!(writeln!(timer_unit_file, "OnCalendar={}", schedule));
            } else {
                try!(writeln!(timer_unit_file, "OnBootSec={}m", delay));
            }

            if random_delay != 1 {
                try!(writeln!(timer_unit_file, "AccuracySec={}m", random_delay));
            }
        }
        try!(symlink(timer_unit_path, cron_target_wants_path.join(timer_unit_name)));
    }

    Ok(())
}

fn linearize<T: Limited + Display>(input: &[Interval<T>]) -> String {
    if input.len() == 1 && input[0] == Interval::Full(1) {
        "*".to_owned()
    } else {
        let mut output = String::new();
        for part in input.iter().flat_map(|v| v.iter()).collect::<BTreeSet<_>>().iter() {
            output.push_str(&*part.to_string());
            output.push(',');
        }
        output.pop();
        output
    }
}

fn tohex(input: &[u8]) -> String {
    #[inline]
    fn hex(d: u8) -> char {
        match d {
            0...9 => (d + 0x30) as char,
            10...15 => (d + 0x57) as char,
            _ => unreachable!("unexpected value: {}", d)
        }
    }

    let mut buf = String::with_capacity(32);
    for b in input.into_iter() {
        buf.push(hex(b >> 4));
        buf.push(hex(b & 0xf));
    }
    buf
}

<commit_msg>don't generate star for dows<commit_after>use std::io::{self, Write};
use std::fmt::Display;
use std::fs::{PathExt, File, create_dir_all, set_permissions};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{symlink, MetadataExt, PermissionsExt};
use std::path::Path;
use std::collections::{BTreeMap, BTreeSet};

use cronparse::Limited;
use cronparse::crontab::{CrontabEntry, SystemCrontabEntry, UserCrontabEntry};
use cronparse::schedule::{Schedule, Period, Calendar};
use cronparse::interval::Interval;

use pgs_files::passwd;

use super::REBOOT_FILE;

pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String>, path: &Path, dstdir: &Path) -> io::Result<()> {
    use cronparse::crontab::CrontabEntry::*;

    info!("generating units for {}: \"{}\", {:?}", path.display(), entry, env);

    let owner = try!(path.metadata()).uid();

    let mut persistent = env.get("PERSISTENT").and_then(|v| match &**v {
        "yes" | "true" | "1" => Some(true),
        "auto" | "" => None,
        _ => Some(false)
    }).unwrap_or_else(|| match entry {
        Anacron(_) | User(UserCrontabEntry { sched: Schedule::Period(_), .. }) | System(SystemCrontabEntry { sched: Schedule::Period(_), .. }) => true,
        _ => false
    });

    let batch = env.get("BATCH").map(|v| match &**v {
        "yes" | "true" | "1" => true,
        _ => false
    }).unwrap_or(false);

    let random_delay = env.get("RANDOM_DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(1);
    let mut delay = env.get("DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
    let hour = env.get("START_HOURS_RANGE").and_then(|v| v.splitn(1, '-').next().and_then(|v| v.parse::<u64>().ok())).unwrap_or(0);
    let shell = env.get("SHELL").map(|v| &**v).unwrap_or("/bin/sh");
    let daemon_reload = Path::new(REBOOT_FILE).is_file();

    let schedule = entry.period().and_then(|period| match *period {
        Period::Reboot => {
            persistent = false;
            if delay == 0 {
                delay = 1;
            }
            None
        },
        Period::Minutely => {
            persistent = false;
            Some("@minutely".to_owned())
        },
        Period::Hourly => {
            if delay == 0 {
                Some("@hourly".to_owned())
            } else {
                Some(format!("*-*-* *:{}:0", delay))
            }
        },
        Period::Midnight => {
            if delay == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* 0:{}:0", delay))
            }
        },
        Period::Daily => {
            if delay == 0 && hour == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Weekly => {
            if delay == 0 && hour == 0 {
                Some("@weekly".to_owned())
            } else {
                Some(format!("Mon *-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Monthly => {
            if delay == 0 && hour == 0 {
                Some("@monthly".to_owned())
            } else {
                Some(format!("*-*-1 {}:{}:0", hour, delay))
            }
        },
        Period::Quaterly => {
            if delay == 0 && hour == 0 {
                Some("@quaterly".to_owned())
            } else {
                Some(format!("*-1,4,7,10-1 {}:{}:0", hour, delay))
            }
        },
        Period::Biannually => {
            if delay == 0 && hour == 0 {
                Some("@semi-annually".to_owned())
            } else {
                Some(format!("*-1,7-1 {}:{}:0", hour, delay))
            }
        },
        Period::Yearly => {
            if delay == 0 && hour == 0 {
                Some("@yearly".to_owned())
            } else {
                Some(format!("*-1-1 {}:{}:0", hour, delay))
            }
        },
        Period::Days(days) => {
            // workaround for anacrontab
            if days > 31 {
                Some(format!("*-1/{}-1 {}:{}:0", days / 30, hour, delay))
            } else {
                Some(format!("*-*-1/{} {}:{}:0", days, hour, delay))
            }
        },
    }).or_else(|| entry.calendar().and_then(|cal| {
        let Calendar {
            ref dows,
            ref days,
            ref mons,
            ref hrs,
            ref mins
        } = *cal;

        Some(format!("{} *-{}-{} {}:{}:00",
                     linearize(&**dows, ""),
                     linearize(&**mons, "*"),
                     linearize(&**days, "*"),
                     linearize(&**hrs, "*"),
                     linearize(&**mins, "*")))
    }));

    if daemon_reload && schedule.is_none() {
        warn!("skipping job from {}: \"{}\"", path.display(), entry);
        return Ok(());
    }

    if let Some(cmd) = entry.command() {

        // make sure we know the user
        let user = try!(entry.user().and_then(passwd::get_entry_by_name).or_else(|| passwd::get_entry_by_uid(owner))
            .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown user")));

        // generate unique cron job id
        let mut md5ctx = ::md5::Context::new();
        md5ctx.consume(path.as_os_str().as_bytes());
        if let Some(ref schedule) = schedule {
            md5ctx.consume(schedule.as_bytes());
        }
        md5ctx.consume(cmd.as_bytes());
        let md5hex = tohex(&md5ctx.compute());

        // create service and timer unit names
        let service_unit_name = format!("cronjob-{}.service", md5hex);
        let timer_unit_name = format!("cronjob-{}.timer", md5hex);

        // unit paths
        let service_unit_path = dstdir.join(&service_unit_name);
        let timer_unit_path = dstdir.join(&timer_unit_name);

        // make sure cron.target.wants dir exists
        let cron_target_wants_path = dstdir.join("cron.target.wants");
        try!(create_dir_all(&cron_target_wants_path));

        // process command in case it should be put into script
        let command = if Path::new(cmd).is_file() {
            cmd.to_owned()
        } else {
            let script_command_path = dstdir.join(format!("cronjob-{}.sh", md5hex));

            debug!("generating script {:?} from {:?}", script_command_path, path);
            {
                let mut script_command_file = try!(File::create(&script_command_path));
                try!(writeln!(script_command_file, "#!{}", shell));
                try!(writeln!(script_command_file, "{}", cmd));
            }

            let mut perms = try!(script_command_path.metadata()).permissions();
            perms.set_mode(0o755);
            try!(set_permissions(&script_command_path, perms));
            script_command_path.to_str().unwrap().to_owned()
        };

        debug!("generating service {:?} from {:?}", service_unit_path, path);
        {
            let mut service_unit_file = try!(File::create(service_unit_path));

            try!(writeln!(service_unit_file, r###"[Unit]
Description=[Cron] "{entry}"
Documentation=man:systemd-crontab-generator(8)
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}"###,
                entry = entry,
                source_crontab_path = path.display(),
                ));

            if env.contains_key("MAILTO") {
                try!(writeln!(service_unit_file, "OnFailure=cron-failure@%i.service"));
            }

            try!(writeln!(service_unit_file, r###"
[Service]
Type=oneshot
IgnoreSIGPIPE=false
ExecStart={command}"###,
                command = command,
                ));

            if user.uid != 0 {
                try!(writeln!(service_unit_file, "User={}", user.name));
            }

            if let Some(group) = entry.group() {
                try!(writeln!(service_unit_file, "Group={}", group));
            }
            if batch {
                try!(writeln!(service_unit_file, "CPUSchedulingPolicy=idle"));
                try!(writeln!(service_unit_file, "IOSchedulingClass=idle"));
            }

            if !env.is_empty() {
                try!(write!(service_unit_file, "Environment="));
                for (name, value) in env.iter() {
                    try!(write!(service_unit_file, r#""{}={}""#, name, value));
                }
                try!(write!(service_unit_file, "\n"));
            }
        }

        debug!("generating timer {:?} from {:?}", timer_unit_path, path);
        {
            let mut timer_unit_file = try!(File::create(&timer_unit_path));

            try!(writeln!(timer_unit_file, r###"[Unit]
Description=[Timer] "{entry}"
Documentation=man:systemd-crontab-generator(8)
PartOf=cron.target
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}

[Timer]
Unit={service_unit_name}
Persistent={persistent}"###,
                entry = entry,
                source_crontab_path = path.display(),
                service_unit_name = service_unit_name,
                persistent = persistent,
                ));

            if let Some(schedule) = schedule {
                try!(writeln!(timer_unit_file, "OnCalendar={}", schedule));
            } else {
                try!(writeln!(timer_unit_file, "OnBootSec={}m", delay));
            }

            if random_delay != 1 {
                try!(writeln!(timer_unit_file, "AccuracySec={}m", random_delay));
            }
        }
        try!(symlink(timer_unit_path, cron_target_wants_path.join(timer_unit_name)));
    }

    Ok(())
}

fn linearize<T: Limited + Display>(input: &[Interval<T>], star: &str) -> String {
    if input.len() == 1 && input[0] == Interval::Full(1) {
        star.to_owned()
    } else {
        let mut output = String::new();
        for part in input.iter().flat_map(|v| v.iter()).collect::<BTreeSet<_>>().iter() {
            output.push_str(&*part.to_string());
            output.push(',');
        }
        output.pop();
        output
    }
}

fn tohex(input: &[u8]) -> String {
    #[inline]
    fn hex(d: u8) -> char {
        match d {
            0...9 => (d + 0x30) as char,
            10...15 => (d + 0x57) as char,
            _ => unreachable!("unexpected value: {}", d)
        }
    }

    let mut buf = String::with_capacity(32);
    for b in input.into_iter() {
        buf.push(hex(b >> 4));
        buf.push(hex(b & 0xf));
    }
    buf
}

<|endoftext|>


<commit_before>use super::errors::*;
use cargo_metadata::Package;
use std::convert::TryInto;
use std::path::Path;

/// Takes a pkgid and attempts to find the path to it's `Cargo.toml`, using `cargo`'s metadata
pub fn manifest_from_pkgid(manifest_path: Option<&Path>, pkgid: &str) -> CargoResult<Package> {
    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.no_deps();
    if let Some(manifest_path) = manifest_path {
        cmd.manifest_path(manifest_path);
    }
    let result = cmd.exec().with_context(|| "Invalid manifest")?;
    let packages = result.packages;
    let package = packages
        .into_iter()
        .find(|pkg| pkg.name == pkgid)
        .with_context(|| {
            "Found virtual manifest, but this command requires running against an \
             actual package in this workspace. Try adding `--workspace`."
        })?;
    Ok(package)
}

/// Lookup all members of the current workspace
pub fn workspace_members(manifest_path: Option<&Path>) -> CargoResult<Vec<Package>> {
    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.no_deps();
    if let Some(manifest_path) = manifest_path {
        cmd.manifest_path(manifest_path);
    }
    let result = cmd.exec().with_context(|| "Invalid manifest")?;
    let workspace_members: std::collections::BTreeSet<_> =
        result.workspace_members.into_iter().collect();
    let workspace_members: Vec<_> = result
        .packages
        .into_iter()
        .filter(|p| workspace_members.contains(&p.id))
        .map(|mut p| {
            p.manifest_path = canonicalize_path(p.manifest_path);
            for dep in p.dependencies.iter_mut() {
                dep.path = dep.path.take().map(canonicalize_path);
            }
            p
        })
        .collect();
    Ok(workspace_members)
}

fn canonicalize_path(
    path: cargo_metadata::camino::Utf8PathBuf,
) -> cargo_metadata::camino::Utf8PathBuf {
    if let Ok(path) = dunce::canonicalize(&path) {
        if let Ok(path) = path.try_into() {
            return path;
        }
    }

    path
}

/// Determine packages selected by user
pub fn resolve_manifests(
    manifest_path: Option<&Path>,
    workspace: bool,
    pkgid: Vec<&str>,
) -> CargoResult<Vec<Package>> {
    let manifest_path = manifest_path.map(|p| Ok(p.to_owned())).unwrap_or_else(|| {
        find_manifest_path(
            &std::env::current_dir().with_context(|| "Failed to get current directory")?,
        )
    })?;
    let manifest_path = dunce::canonicalize(manifest_path)?;

    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.no_deps();
    cmd.manifest_path(&manifest_path);
    let result = cmd.exec().with_context(|| "Invalid manifest")?;
    let pkgs = if workspace {
        result
            .packages
            .into_iter()
            .map(|package| package)
            .collect::<Vec<_>>()
    } else if !pkgid.is_empty() {
        pkgid
            .into_iter()
            .map(|id| {
                result
                    .packages
                    .iter()
                    .find(|pkg| pkg.name == id)
                    .map(|p| p.to_owned())
                    .with_context(|| format!("could not find pkgid {}", id))
            })
            .collect::<Result<Vec<_>, anyhow::Error>>()?
    } else {
        result
            .packages
            .iter()
            .find(|p| p.manifest_path == manifest_path)
            .map(|p| vec![(p.to_owned())])
            .unwrap_or_else(|| {
                result
                    .packages
                    .into_iter()
                    .map(|package| package)
                    .collect::<Vec<_>>()
            })
    };
    Ok(pkgs)
}

/// Search for Cargo.toml in this directory and recursively up the tree until one is found.
pub(crate) fn find_manifest_path(dir: &Path) -> CargoResult<std::path::PathBuf> {
    const MANIFEST_FILENAME: &str = "Cargo.toml";
    for path in dir.ancestors() {
        let manifest = path.join(MANIFEST_FILENAME);
        if std::fs::metadata(&manifest).is_ok() {
            return Ok(manifest);
        }
    }
    anyhow::bail!("Unable to find Cargo.toml for {}", dir.display());
}
<commit_msg>style: Make clippy happy<commit_after>use super::errors::*;
use cargo_metadata::Package;
use std::convert::TryInto;
use std::path::Path;

/// Takes a pkgid and attempts to find the path to it's `Cargo.toml`, using `cargo`'s metadata
pub fn manifest_from_pkgid(manifest_path: Option<&Path>, pkgid: &str) -> CargoResult<Package> {
    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.no_deps();
    if let Some(manifest_path) = manifest_path {
        cmd.manifest_path(manifest_path);
    }
    let result = cmd.exec().with_context(|| "Invalid manifest")?;
    let packages = result.packages;
    let package = packages
        .into_iter()
        .find(|pkg| pkg.name == pkgid)
        .with_context(|| {
            "Found virtual manifest, but this command requires running against an \
             actual package in this workspace. Try adding `--workspace`."
        })?;
    Ok(package)
}

/// Lookup all members of the current workspace
pub fn workspace_members(manifest_path: Option<&Path>) -> CargoResult<Vec<Package>> {
    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.no_deps();
    if let Some(manifest_path) = manifest_path {
        cmd.manifest_path(manifest_path);
    }
    let result = cmd.exec().with_context(|| "Invalid manifest")?;
    let workspace_members: std::collections::BTreeSet<_> =
        result.workspace_members.into_iter().collect();
    let workspace_members: Vec<_> = result
        .packages
        .into_iter()
        .filter(|p| workspace_members.contains(&p.id))
        .map(|mut p| {
            p.manifest_path = canonicalize_path(p.manifest_path);
            for dep in p.dependencies.iter_mut() {
                dep.path = dep.path.take().map(canonicalize_path);
            }
            p
        })
        .collect();
    Ok(workspace_members)
}

fn canonicalize_path(
    path: cargo_metadata::camino::Utf8PathBuf,
) -> cargo_metadata::camino::Utf8PathBuf {
    if let Ok(path) = dunce::canonicalize(&path) {
        if let Ok(path) = path.try_into() {
            return path;
        }
    }

    path
}

/// Determine packages selected by user
pub fn resolve_manifests(
    manifest_path: Option<&Path>,
    workspace: bool,
    pkgid: Vec<&str>,
) -> CargoResult<Vec<Package>> {
    let manifest_path = manifest_path.map(|p| Ok(p.to_owned())).unwrap_or_else(|| {
        find_manifest_path(
            &std::env::current_dir().with_context(|| "Failed to get current directory")?,
        )
    })?;
    let manifest_path = dunce::canonicalize(manifest_path)?;

    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.no_deps();
    cmd.manifest_path(&manifest_path);
    let result = cmd.exec().with_context(|| "Invalid manifest")?;
    let pkgs = if workspace {
        result.packages
    } else if !pkgid.is_empty() {
        pkgid
            .into_iter()
            .map(|id| {
                result
                    .packages
                    .iter()
                    .find(|pkg| pkg.name == id)
                    .map(|p| p.to_owned())
                    .with_context(|| format!("could not find pkgid {}", id))
            })
            .collect::<Result<Vec<_>, anyhow::Error>>()?
    } else {
        result
            .packages
            .iter()
            .find(|p| p.manifest_path == manifest_path)
            .map(|p| vec![(p.to_owned())])
            .unwrap_or_else(|| result.packages)
    };
    Ok(pkgs)
}

/// Search for Cargo.toml in this directory and recursively up the tree until one is found.
pub(crate) fn find_manifest_path(dir: &Path) -> CargoResult<std::path::PathBuf> {
    const MANIFEST_FILENAME: &str = "Cargo.toml";
    for path in dir.ancestors() {
        let manifest = path.join(MANIFEST_FILENAME);
        if std::fs::metadata(&manifest).is_ok() {
            return Ok(manifest);
        }
    }
    anyhow::bail!("Unable to find Cargo.toml for {}", dir.display());
}
<|endoftext|>


<commit_before>use fuse;
use fuse::{FileAttr, FileType, ReplyAttr, ReplyDirectory, ReplyEntry, Request};

use rusqlite::Connection;

use time::Timespec;

use libc::ENOENT;

use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};

struct FSEntry {
    name: OsString,
    time: Timespec,
    kind: FileType,
}

pub struct Filesystem {
    conn: Connection,
    cache: Vec<(PathBuf, Vec<FSEntry>)>,
}

impl Filesystem {
    pub fn new(db: &Path) -> Self {
        info!("Opening database: {}", db.display());

        let conn = Connection::open(db).expect("Could not open database");

        let artists;

        {
            let mut req = conn.prepare("SELECT DISTINCT albumartist FROM albums")
                .unwrap();

            artists = req.query_map(&[], |row| {
                    FSEntry {
                        name: OsString::from((row.get(0): String).replace("/", "_")),
                        time: Timespec::new(1, 0),
                        kind: FileType::Directory,
                    }
                })
                .unwrap()
                .collect::<Result<_, _>>()
                .unwrap();
        }

        Self {
            conn: conn,
            cache: vec![(PathBuf::from("/"), artists)],
        }
    }

    pub fn get_path_content(&mut self, path: &Path) -> Option<&Vec<FSEntry>> {
        if path == Path::new("/") {
            self.cache.get(0).map(|&(_, ref entries)| entries)
        } else {
            None
        }
    }

    pub fn get_ino_content(&mut self, ino: u64) -> Option<&Vec<FSEntry>> {
            self.cache.get((ino - 1) as usize).map(|&(_, ref entries)| entries)
    }

    pub fn path_from_ino(&self, ino: u64) -> Option<&Path> {
        self.cache
            .get(ino as usize)
            .map(|&(ref path, _)| path.as_path())
    }
}

impl fuse::Filesystem for Filesystem {
    fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) {
        if parent == 1 {
            reply.entry(&Timespec::new(0, 0),
                        &FileAttr {
                            ino: 2,
                            size: 0,
                            blocks: 0,
                            atime: Timespec::new(1, 0),
                            mtime: Timespec::new(1, 0),
                            ctime: Timespec::new(1, 0),
                            crtime: Timespec::new(1, 0),
                            kind: FileType::Directory,
                            perm: 0o755,
                            nlink: 0,
                            uid: 1000,
                            gid: 1000,
                            rdev: 0,
                            flags: 0,
                        },
                        0)
        } else {
            reply.error(ENOENT)
        }
    }

    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        if ino == 1 || ino == 2 {
            reply.attr(&Timespec::new(1, 0),
                       &FileAttr {
                           ino: 1,
                           size: 0,
                           blocks: 0,
                           atime: Timespec::new(1, 0),
                           mtime: Timespec::new(1, 0),
                           ctime: Timespec::new(1, 0),
                           crtime: Timespec::new(1, 0),
                           kind: FileType::Directory,
                           perm: 0o755,
                           nlink: 0,
                           uid: 1000,
                           gid: 1000,
                           rdev: 0,
                           flags: 0,
                       })
        } else {
            reply.error(ENOENT)
        }
    }

    fn readdir(&mut self,
               _req: &Request,
               ino: u64,
               _fh: u64,
               mut offset: u64,
               mut reply: ReplyDirectory) {

        offset += 1;

        match self.get_ino_content(ino) {
            None => {
                info!("No such directory to list");
                reply.error(ENOENT)
            }
            Some(entries) => {
                for entry in &entries[offset as usize..] {
                    if !reply.add(offset + 2, offset, entry.kind, &entry.name) {
                        info!("Added at offset {}: {:?}", offset, entry.name);
                        offset += 1;
                    } else {
                        info!("Fill buffer full");
                        break;
                    }
                }

                info!("Replying ok");
                reply.ok();
            }
        }
    }
}
<commit_msg>Preparing for directories with real inodes<commit_after>use fuse::{self, FileAttr, FileType, ReplyAttr, ReplyDirectory, ReplyEntry, Request};

use rusqlite::Connection;

use time::Timespec;

use libc::ENOENT;

use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};

#[derive(Debug, Clone)]
struct FSEntry {
    name: OsString,
    inode: u64,
    time: Timespec,
    kind: FileType,
}

pub struct Filesystem {
    conn: Connection,
    cache: Vec<(PathBuf, Vec<FSEntry>)>,
}

impl Filesystem {
    pub fn new(db: &Path) -> Self {
        info!("Opening database: {}", db.display());

        let conn = Connection::open(db).expect("Could not open database");

        let mut cache;

        {
            let mut req = conn.prepare("SELECT DISTINCT albumartist FROM albums")
                .unwrap();

            let mut count = 0;

            let artists = req.query_map(&[], |row| {
                    count += 1;
                    FSEntry {
                        name: OsString::from((row.get(0): String).replace("/", "_")),
                        inode: count,
                        time: Timespec::new(1, 0),
                        kind: FileType::Directory,
                    }
                })
                .unwrap()
                .collect::<Result<_, _>>()
                .unwrap();

            cache = vec![(PathBuf::from("/"), artists)];
            cache.reserve(count as usize + 1);
        }

        Self {
            conn: conn,
            cache: cache,
        }
    }

    pub fn get_path_content(&mut self, path: &Path) -> Option<&Vec<FSEntry>> {
        if path == Path::new("/") {
            self.cache.get(0).map(|&(_, ref entries)| entries)
        } else {
            None
        }
    }

    pub fn get_ino_content(&mut self, ino: u64) -> Option<&Vec<FSEntry>> {
        self.cache
            .get((ino - 1) as usize)
            .map(|&(_, ref entries)| entries)
    }

    pub fn path_from_ino(&self, ino: u64) -> Option<&Path> {
        self.cache
            .get(ino as usize)
            .map(|&(ref path, _)| path.as_path())
    }
}

impl fuse::Filesystem for Filesystem {
    fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) {
        if parent == 1 {
            match self.get_ino_content(parent)
                      .and_then(|entries| entries.iter().find(|entry| entry.name == name))
                      .map(|entry| entry.inode) {
                None => reply.error(ENOENT),
                Some(inode) => {
                    reply.entry(&Timespec::new(0, 0),
                                &FileAttr {
                                    ino: 2,
                                    size: 0,
                                    blocks: 0,
                                    atime: Timespec::new(1, 0),
                                    mtime: Timespec::new(1, 0),
                                    ctime: Timespec::new(1, 0),
                                    crtime: Timespec::new(1, 0),
                                    kind: FileType::Directory,
                                    perm: 0o755,
                                    nlink: 0,
                                    uid: 1000,
                                    gid: 1000,
                                    rdev: 0,
                                    flags: 0,
                                },
                                0)
                }
            }
        } else {
            reply.error(ENOENT)
        }
    }

    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        if ino == 1 || ino == 2 {
            reply.attr(&Timespec::new(1, 0),
                       &FileAttr {
                           ino: 2,
                           size: 0,
                           blocks: 0,
                           atime: Timespec::new(1, 0),
                           mtime: Timespec::new(1, 0),
                           ctime: Timespec::new(1, 0),
                           crtime: Timespec::new(1, 0),
                           kind: FileType::Directory,
                           perm: 0o755,
                           nlink: 0,
                           uid: 1000,
                           gid: 1000,
                           rdev: 0,
                           flags: 0,
                       })
        } else {
            reply.error(ENOENT)
        }
    }

    fn readdir(&mut self,
               _req: &Request,
               ino: u64,
               _fh: u64,
               mut offset: u64,
               mut reply: ReplyDirectory) {

        offset += 1;

        match self.get_ino_content(ino) {
            None => {
                info!("Trying to list nonexistant directory with inode: {:?}", ino);
                reply.error(ENOENT)
            }
            Some(entries) => {
                for entry in &entries[offset as usize..] {
                    if !reply.add(entry.inode, offset, entry.kind, &entry.name) {
                        debug!("Added at offset {}: {:?}", offset, entry.name);
                        offset += 1;
                    } else {
                        debug!("Fill buffer full");
                        break;
                    }
                }

                reply.ok();
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::Metadata;
use std::path::{Path, PathBuf};

use log::warn;

pub fn walk<P: AsRef<Path>>(root: P) -> std::io::Result<Walk> {
    let iter = std::fs::read_dir(root)?;
    Ok(Walk {
        pending: vec!(iter),
    })
}

pub struct Walk {
    // TODO: Add support for stopping at device boundaries.
    pending: Vec<std::fs::ReadDir>,
}

pub struct WalkEntry {
    path: PathBuf,
    metadata: Metadata,
}

impl WalkEntry {

    fn from_dir_entry(entry: std::fs::DirEntry) -> Option<WalkEntry> {
        let path = entry.path();

        let metadata = match entry.metadata() {
            Ok(metadata) => metadata,
            Err(error) => {
                warn!("failed to stat file '{}': {}", path.display(), error);
                return None;
            },
        };

        Some(WalkEntry {
            path: path,
            metadata: metadata,
        })
    }
}

impl Walk {

    fn push(&mut self, entry: &WalkEntry) {
        match std::fs::read_dir(&entry.path) {
            Ok(iter) => {
                self.pending.push(iter);
            },
            Err(error) => {
                warn!("failed to read '{}': {}", entry.path.display(), error);
            },
        }
    }

    fn pop(&mut self) -> Option<WalkEntry> {
        while let Some(iter) = self.pending.last_mut() {
            for entry in iter {
                match entry {
                    Ok(entry) => {
                        let entry = WalkEntry::from_dir_entry(entry);
                        if entry.is_some() {
                            return entry;
                        }
                    },
                    Err(error) => warn!("directory iteration error: {}", error),
                }
            }

            self.pending.pop();
        }

        None
    }
}

impl std::iter::Iterator for Walk {

    type Item = WalkEntry;

    fn next(&mut self) -> Option<WalkEntry> {
        let entry = self.pop()?;
        self.push(&entry);
        Some(entry)
    }
}
<commit_msg>Flatten the walking logic.<commit_after>use std::fs::Metadata;
use std::path::{Path, PathBuf};

use log::warn;

pub fn walk<P: AsRef<Path>>(root: P) -> std::io::Result<Walk> {
    let iter = std::fs::read_dir(root)?;
    Ok(Walk {
        pending: vec!(WalkDir::from_read_dir(iter)),
    })
}

pub struct Walk {
    // TODO: Add support for stopping at device boundaries.
    pending: Vec<WalkDir>,
}

pub struct WalkEntry {
    path: PathBuf,
    metadata: Metadata,
}

impl Walk {

    fn push(&mut self, entry: &WalkEntry) {
        match std::fs::read_dir(&entry.path) {
            Ok(iter) => {
                self.pending.push(WalkDir::from_read_dir(iter));
            },
            Err(error) => {
                warn!("failed to read '{}': {}", entry.path.display(), error);
            },
        }
    }

    fn pop(&mut self) -> Option<WalkEntry> {
        while let Some(iter) = self.pending.last_mut() {
            for entry in iter {
                return Some(entry);
            }

            self.pending.pop();
        }

        None
    }
}

impl std::iter::Iterator for Walk {

    type Item = WalkEntry;

    fn next(&mut self) -> Option<WalkEntry> {
        let entry = self.pop()?;
        self.push(&entry);
        Some(entry)
    }
}

struct WalkDir {
    iter: std::fs::ReadDir,
}

impl WalkDir {

    fn from_read_dir(iter: std::fs::ReadDir) -> WalkDir {
        WalkDir {
            iter: iter,
        }
    }
}

impl std::iter::Iterator for WalkDir {

    type Item = WalkEntry;

    fn next(&mut self) -> Option<WalkEntry> {
        for entry in &mut self.iter {
            let entry = match entry {
                Ok(entry) => entry,
                Err(error) => {
                    warn!("directory iteration error: {}", error);
                    continue
                },
            };

            let path = entry.path();
            let metadata = match entry.metadata() {
                Ok(metadata) => metadata,
                Err(error) => {
                    warn!("failed to stat '{}': {}", path.display(), error);
                    continue
                },
            };

            return Some(WalkEntry {
                path: path,
                metadata: metadata,
            });
        }

        None
    }
}
<|endoftext|>


<commit_before>extern crate reqwest;
extern crate serde_json;

use self::reqwest::header::{Authorization, Link, UserAgent};
use self::reqwest::{Error, Response, Url};

use std::io::Read;
use std::collections::HashMap;

static USERAGENT: &'static str = "release-party-br";

#[derive(Deserialize, Debug)]
pub struct GithubRepo {
    id: i32,
    pub name: String,
    pub url: String,
}

#[derive(Deserialize, Debug)]
pub struct CompareCommitsResponse {
    pub status: String,
    pub behind_by: i32,
}

#[derive(Deserialize, Debug)]
pub struct GithubPullRequest {
    id: i32,
    pub url: String,
    pub html_url: String,
    pub head: Commit,
    pub base: Commit,
}

#[derive(Deserialize, Debug)]
pub struct Commit {
    pub sha: String,
    pub label: String,
}

pub fn is_release_up_to_date_with_master(repo_url: &str, token: &str, client: &reqwest::Client) -> bool {
    let repo_pr_url = format!("{}/{}/{}...{}", repo_url, "compare", "master", "release");
    let url = match Url::parse(&repo_pr_url) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for compare page: {}", e);
            return true;
        }
    };
    let mut res = match client
        .get(url.clone())
        .expect("Couldn't make a request builder for comparison page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
    {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for compare page: {}", e);
            return true;
        }
    };
    // Check rate limit headers in response
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error checking commit diff for {}: {}", repo_url, e),
    }

    let commits_diff: CompareCommitsResponse = match serde_json::from_str(&buffer) {
        Ok(compare_response) => compare_response,
        Err(_) => return true,
    };

    if commits_diff.behind_by > 0 {
        return false;
    }

    true
}

fn response_has_a_next_link(response_headers: &reqwest::header::Headers) -> bool {
    if response_headers.get::<Link>().is_none() {
        return false;
    }
    for link in response_headers
        .get::<Link>()
        .expect("links in has next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    return true;
                }
            }
            None => return false,
        }
    }
    false
}

// Expects caller to check to ensure the `next` link is present
fn response_next_link(response_headers: &reqwest::header::Headers) -> Result<Url, String> {
    for link in response_headers
        .get::<Link>()
        .expect("links in response next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    let uri = Url::parse(link.link()).expect("Should have been able to parse the nextlink");
                    return Ok(uri);
                }
            }
            None => (),
        }
    }
    Err("Couldn't find a next link: does it exist?".to_string())
}

pub fn get_repos_at(repos_url: &str, token: &str, client: &reqwest::Client) -> Result<Vec<GithubRepo>, String> {
    // We need to pass in the URL from the link headers to github API docs.
    // We'll construct it this first time.
    let url = match Url::parse_with_params(repos_url, &[("per_page", "50")]) {
        Ok(new_url) => new_url,
        Err(e) => return Err(format!("Couldn't parse uri {:?} : {:?}", repos_url, e)),
    };
    let mut response = get_repos_at_url(url, token, client).expect("request failed");
    // Check rate limit headers in response
    let mut buffer = String::new();
    match response.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error reading response from github when getting repo list: {}", e),
    }
    let mut repos = repo_list_from_string(&buffer).expect("expected repos");

    if response_has_a_next_link(response.headers()) {
        loop {
            let paging_url = response_next_link(response.headers()).expect("a thing");
            response = get_repos_at_url(paging_url, token, client).expect("request failed");
            // Check rate limit headers in response
            buffer = String::new();
            match response.read_to_string(&mut buffer) {
                Ok(_) => (),
                Err(e) => println!("error reading response from github when getting repo list: {}", e),
            }
            repos.append(&mut repo_list_from_string(&buffer).expect("expected repos"));
            if !response_has_a_next_link(response.headers()) {
                break;
            }
        }
    }
    println!("Number of repos to check: {:?}", repos.len());
    Ok(repos)
}

fn get_repos_at_url(url: reqwest::Url, token: &str, client: &reqwest::Client) -> Result<Response, Error> {
    client
        .get(url)
        .expect("Couldn't make a request builder for repos page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
}

fn repo_list_from_string(json_str: &str) -> Result<Vec<GithubRepo>, String> {
    // This looks a bit weird due to supplying type hints to deserialize:
    let _: Vec<GithubRepo> = match serde_json::from_str(json_str) {
        Ok(v) => return Ok(v),
        Err(e) => return Err(format!("Couldn't deserialize repos from github: {}", e)),
    };
}

pub fn existing_release_pr_location(repo: &GithubRepo, token: &str, client: &reqwest::Client) -> Option<String> {
    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let url = match Url::parse_with_params(&repo_pr_url, &[("head", "master"), ("base", "release")]) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for existing pr location: {}", e);
            return None;
        }
    };
    let mut res = match client
        .get(url)
        .expect("Couldn't make a request builder for existing PRs page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
    {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for existing PR location: {}", e);
            return None;
        }
    };
    // Check rate limit headers in response
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error finding existing pr for {}: {}", repo.name, e),
    }

    let pull_reqs: Vec<GithubPullRequest> = match serde_json::from_str(&buffer) {
        Ok(v) => v,
        Err(_) => Vec::new(),
    };

    if !pull_reqs.is_empty() {
        return Some(pull_reqs[0].html_url.clone());
    }

    None
}

// Try to create the release PR and return the URL of it:
pub fn create_release_pull_request(repo: &GithubRepo, token: &str, client: &reqwest::Client) -> Result<String, String> {
    let mut pr_body = HashMap::new();
    pr_body.insert("title", "automated release partay");
    pr_body.insert("head", "master");
    pr_body.insert("base", "release");

    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let mut res = match client
        .post(&repo_pr_url)
        .expect("Couldn't make a request builder for creating PR url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .json(&pr_body)
        .expect("Couldn't make the JSON payload for creating a PR")
        .send()
    {
        Ok(response) => response,
        Err(e) => return Err(format!("Error in request to github creating new PR: {}", e)),
    };

    // Check rate limit headers in response
    if res.status().is_success() {
        let mut buffer = String::new();
        match res.read_to_string(&mut buffer) {
            Ok(_) => (),
            Err(e) => println!("error reading response after creating new release PR for {}: {}", repo.name, e),
        }
        let pull_req: GithubPullRequest = match serde_json::from_str(&buffer) {
            Ok(v) => v,
            Err(e) => {
                return Err(format!(
                    "Couldn't deserialize create pull req response for {}: {}",
                    repo.name,
                    e
                ))
            }
        };
        return Ok(pull_req.html_url);
    }
    // 422 unprocessable means it's there already
    // 422 unprocessable also means the branch is up to date

    Err("Release branch already up to date?".to_string())
}
<commit_msg>Installs skeleton of respecting rate limits.<commit_after>extern crate reqwest;
extern crate serde_json;

use self::reqwest::header::{Authorization, Link, UserAgent};
use self::reqwest::{Error, Response, Url};

use std::io::Read;
use std::collections::HashMap;
use std::{thread, time};

static USERAGENT: &'static str = "release-party-br";

#[derive(Deserialize, Debug)]
pub struct GithubRepo {
    id: i32,
    pub name: String,
    pub url: String,
}

#[derive(Deserialize, Debug)]
pub struct CompareCommitsResponse {
    pub status: String,
    pub behind_by: i32,
}

#[derive(Deserialize, Debug)]
pub struct GithubPullRequest {
    id: i32,
    pub url: String,
    pub html_url: String,
    pub head: Commit,
    pub base: Commit,
}

#[derive(Deserialize, Debug)]
pub struct Commit {
    pub sha: String,
    pub label: String,
}

pub fn is_release_up_to_date_with_master(repo_url: &str, token: &str, client: &reqwest::Client) -> bool {
    let repo_pr_url = format!("{}/{}/{}...{}", repo_url, "compare", "master", "release");
    let url = match Url::parse(&repo_pr_url) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for compare page: {}", e);
            return true;
        }
    };
    let mut res = match client
        .get(url.clone())
        .expect("Couldn't make a request builder for comparison page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
    {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for compare page: {}", e);
            return true;
        }
    };
    // Check rate limit headers in response
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error checking commit diff for {}: {}", repo_url, e),
    }

    let commits_diff: CompareCommitsResponse = match serde_json::from_str(&buffer) {
        Ok(compare_response) => compare_response,
        Err(_) => return true,
    };

    if commits_diff.behind_by > 0 {
        return false;
    }

    true
}

fn delay_if_running_out_of_requests(response_headers: &reqwest::header::Headers) {
    if close_to_running_out_of_requests(response_headers) {
        thread::sleep(time::Duration::from_millis(1000));
    }
}

fn close_to_running_out_of_requests(response_headers: &reqwest::header::Headers) -> bool {
    // do a get_raw for X-RateLimit-Remaining ?
    false
}

fn response_has_a_next_link(response_headers: &reqwest::header::Headers) -> bool {
    if response_headers.get::<Link>().is_none() {
        return false;
    }
    for link in response_headers
        .get::<Link>()
        .expect("links in has next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    return true;
                }
            }
            None => return false,
        }
    }
    false
}

// Expects caller to check to ensure the `next` link is present
fn response_next_link(response_headers: &reqwest::header::Headers) -> Result<Url, String> {
    for link in response_headers
        .get::<Link>()
        .expect("links in response next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    let uri = Url::parse(link.link()).expect("Should have been able to parse the nextlink");
                    return Ok(uri);
                }
            }
            None => (),
        }
    }
    Err("Couldn't find a next link: does it exist?".to_string())
}

pub fn get_repos_at(repos_url: &str, token: &str, client: &reqwest::Client) -> Result<Vec<GithubRepo>, String> {
    // We need to pass in the URL from the link headers to github API docs.
    // We'll construct it this first time.
    let url = match Url::parse_with_params(repos_url, &[("per_page", "50")]) {
        Ok(new_url) => new_url,
        Err(e) => return Err(format!("Couldn't parse uri {:?} : {:?}", repos_url, e)),
    };
    let mut response = get_repos_at_url(url, token, client).expect("request failed");
    delay_if_running_out_of_requests(response.headers());
    let mut buffer = String::new();
    match response.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error reading response from github when getting repo list: {}", e),
    }
    let mut repos = repo_list_from_string(&buffer).expect("expected repos");

    if response_has_a_next_link(response.headers()) {
        loop {
            let paging_url = response_next_link(response.headers()).expect("a thing");
            response = get_repos_at_url(paging_url, token, client).expect("request failed");
            delay_if_running_out_of_requests(response.headers());
            buffer = String::new();
            match response.read_to_string(&mut buffer) {
                Ok(_) => (),
                Err(e) => println!("error reading response from github when getting repo list: {}", e),
            }
            repos.append(&mut repo_list_from_string(&buffer).expect("expected repos"));
            if !response_has_a_next_link(response.headers()) {
                break;
            }
        }
    }
    println!("Number of repos to check: {:?}", repos.len());
    Ok(repos)
}

fn get_repos_at_url(url: reqwest::Url, token: &str, client: &reqwest::Client) -> Result<Response, Error> {
    client
        .get(url)
        .expect("Couldn't make a request builder for repos page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
}

fn repo_list_from_string(json_str: &str) -> Result<Vec<GithubRepo>, String> {
    // This looks a bit weird due to supplying type hints to deserialize:
    let _: Vec<GithubRepo> = match serde_json::from_str(json_str) {
        Ok(v) => return Ok(v),
        Err(e) => return Err(format!("Couldn't deserialize repos from github: {}", e)),
    };
}

pub fn existing_release_pr_location(repo: &GithubRepo, token: &str, client: &reqwest::Client) -> Option<String> {
    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let url = match Url::parse_with_params(&repo_pr_url, &[("head", "master"), ("base", "release")]) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for existing pr location: {}", e);
            return None;
        }
    };
    let mut res = match client
        .get(url)
        .expect("Couldn't make a request builder for existing PRs page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .send()
    {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for existing PR location: {}", e);
            return None;
        }
    };
    delay_if_running_out_of_requests(res.headers());
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error finding existing pr for {}: {}", repo.name, e),
    }

    let pull_reqs: Vec<GithubPullRequest> = match serde_json::from_str(&buffer) {
        Ok(v) => v,
        Err(_) => Vec::new(),
    };

    if !pull_reqs.is_empty() {
        return Some(pull_reqs[0].html_url.clone());
    }

    None
}

// Try to create the release PR and return the URL of it:
pub fn create_release_pull_request(repo: &GithubRepo, token: &str, client: &reqwest::Client) -> Result<String, String> {
    let mut pr_body = HashMap::new();
    pr_body.insert("title", "automated release partay");
    pr_body.insert("head", "master");
    pr_body.insert("base", "release");

    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let mut res = match client
        .post(&repo_pr_url)
        .expect("Couldn't make a request builder for creating PR url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .json(&pr_body)
        .expect("Couldn't make the JSON payload for creating a PR")
        .send()
    {
        Ok(response) => response,
        Err(e) => return Err(format!("Error in request to github creating new PR: {}", e)),
    };

    delay_if_running_out_of_requests(res.headers());
    if res.status().is_success() {
        let mut buffer = String::new();
        match res.read_to_string(&mut buffer) {
            Ok(_) => (),
            Err(e) => println!("error reading response after creating new release PR for {}: {}", repo.name, e),
        }
        let pull_req: GithubPullRequest = match serde_json::from_str(&buffer) {
            Ok(v) => v,
            Err(e) => {
                return Err(format!(
                    "Couldn't deserialize create pull req response for {}: {}",
                    repo.name,
                    e
                ))
            }
        };
        return Ok(pull_req.html_url);
    }
    // 422 unprocessable means it's there already
    // 422 unprocessable also means the branch is up to date

    Err("Release branch already up to date?".to_string())
}
<|endoftext|>


<commit_before>use {ErrorCode, IndyHandle};

use std::ffi::CString;
use std::time::Duration;

use utils::callbacks::ClosureHandler;
use utils::results::ResultHandler;

use ffi::pairwise;
use ffi::{ResponseEmptyCB,
          ResponseStringCB,
          ResponseBoolCB};

pub struct Pairwise {}

impl Pairwise {
    pub fn does_exist(wallet_handle: IndyHandle, their_did: &str) -> Result<bool, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_bool();

        let err = Pairwise::_does_exist(command_handle, wallet_handle, their_did, cb);

        ResultHandler::one(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn does_exist_timeout(wallet_handle: IndyHandle, their_did: &str, timeout: Duration) -> Result<bool, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_bool();

        let err = Pairwise::_does_exist(command_handle, wallet_handle, their_did, cb);

        ResultHandler::one_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn does_exist_async<F: 'static>(wallet_handle: IndyHandle, their_did: &str, closure: F) -> ErrorCode where F: FnMut(ErrorCode, bool) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec_bool(Box::new(closure));

        Pairwise::_does_exist(command_handle, wallet_handle, their_did, cb)
    }

    fn _does_exist(command_handle: IndyHandle, wallet_handle: IndyHandle, their_did: &str, cb: Option<ResponseBoolCB>) -> ErrorCode {
        let their_did = c_str!(their_did);

        ErrorCode::from(unsafe {
            pairwise::indy_is_pairwise_exists(command_handle, wallet_handle, their_did.as_ptr(), cb)
        })
    }

    pub fn create(wallet_handle: IndyHandle, their_did: &str, my_did: &str, metadata: &str) -> Result<(), ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec();

        let err = Pairwise::_create(command_handle, wallet_handle, their_did, my_did, metadata, cb);

        ResultHandler::empty(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn create_timeout(wallet_handle: IndyHandle, their_did: &str, my_did: &str, metadata: &str, timeout: Duration) -> Result<(), ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec();

        let err = Pairwise::_create(command_handle, wallet_handle, their_did, my_did, metadata, cb);

        ResultHandler::empty_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn create_async<F: 'static>(wallet_handle: IndyHandle, their_did: &str, my_did: &str, metadata: &str, closure: F) -> ErrorCode where F: FnMut(ErrorCode) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec(Box::new(closure));

        Pairwise::_create(command_handle, wallet_handle, their_did, my_did, metadata, cb)
    }

    fn _create(command_handle: IndyHandle, wallet_handle: IndyHandle, their_did: &str, my_did: &str, metadata: &str, cb: Option<ResponseEmptyCB>) -> ErrorCode {
        let their_did = c_str!(their_did);
        let my_did = c_str!(my_did);
        let metadata = c_str!(metadata);

        ErrorCode::from(unsafe {
            pairwise::indy_create_pairwise(command_handle, wallet_handle, their_did.as_ptr(), my_did.as_ptr(), metadata.as_ptr(), cb)
        })
    }

    pub fn list(wallet_handle: IndyHandle) -> Result<String, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string();

        let err = Pairwise::_list(command_handle, wallet_handle, cb);

        ResultHandler::one(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn list_timeout(wallet_handle: IndyHandle, timeout: Duration) -> Result<String, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string();

        let err = Pairwise::_list(command_handle, wallet_handle, cb);

        ResultHandler::one_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn list_async<F: 'static>(wallet_handle: IndyHandle, closure: F) -> ErrorCode where F: FnMut(ErrorCode, String) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec_string(Box::new(closure));

        Pairwise::_list(command_handle, wallet_handle, cb)
    }

    fn _list(command_handle: IndyHandle, wallet_handle: IndyHandle, cb: Option<ResponseStringCB>) -> ErrorCode {
        ErrorCode::from(unsafe {
            pairwise::indy_list_pairwise(command_handle, wallet_handle, cb)
        })
    }

    pub fn get(wallet_handle: IndyHandle, their_did: &str) -> Result<String, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string();

        let err = Pairwise::_get(command_handle, wallet_handle, their_did, cb);

        ResultHandler::one(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn get_timeout(wallet_handle: IndyHandle, their_did: &str, timeout: Duration) -> Result<String, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string();

        let err = Pairwise::_get(command_handle, wallet_handle, their_did, cb);

        ResultHandler::one_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn get_async<F: 'static>(wallet_handle: IndyHandle, their_did: &str, closure: F) -> ErrorCode where F: FnMut(ErrorCode, String) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec_string(Box::new(closure));

        Pairwise::_get(command_handle, wallet_handle, their_did, cb)
    }

    fn _get(command_handle: IndyHandle, wallet_handle: IndyHandle, their_did: &str, cb: Option<ResponseStringCB>) -> ErrorCode {
        let their_did = c_str!(their_did);

        ErrorCode::from(unsafe {
            pairwise::indy_get_pairwise(command_handle, wallet_handle, their_did.as_ptr(), cb)
        })
    }

    pub fn set_metadata(wallet_handle: IndyHandle, their_did: &str, metadata: &str) -> Result<(), ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec();

        let err = Pairwise::_set_metadata(command_handle, wallet_handle, their_did, metadata, cb);

        ResultHandler::empty(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn set_metadata_timeout(wallet_handle: IndyHandle, their_did: &str, metadata: &str, timeout: Duration) -> Result<(), ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec();

        let err = Pairwise::_set_metadata(command_handle, wallet_handle, their_did, metadata, cb);

        ResultHandler::empty_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn set_metadata_async<F: 'static>(wallet_handle: IndyHandle, their_did: &str, metadata: &str, closure: F) -> ErrorCode where F: FnMut(ErrorCode) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec(Box::new(closure));

        Pairwise::_set_metadata(command_handle, wallet_handle, their_did, metadata, cb)
    }

    fn _set_metadata(command_handle: IndyHandle, wallet_handle: IndyHandle, their_did: &str, metadata: &str, cb: Option<ResponseEmptyCB>) -> ErrorCode {
        let their_did = c_str!(their_did);
        let metadata = c_str!(metadata);

        ErrorCode::from(unsafe {
            pairwise::indy_set_pairwise_metadata(command_handle, wallet_handle, their_did.as_ptr(), metadata.as_ptr(), cb)
        })
    }
}
<commit_msg>Added optional parameters to pairwise<commit_after>use {ErrorCode, IndyHandle};

use std::ffi::CString;
use std::time::Duration;

use utils::callbacks::ClosureHandler;
use utils::results::ResultHandler;

use ffi::pairwise;
use ffi::{ResponseEmptyCB,
          ResponseStringCB,
          ResponseBoolCB};

pub struct Pairwise {}

impl Pairwise {
    pub fn does_exist(wallet_handle: IndyHandle, their_did: &str) -> Result<bool, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_bool();

        let err = Pairwise::_does_exist(command_handle, wallet_handle, their_did, cb);

        ResultHandler::one(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn does_exist_timeout(wallet_handle: IndyHandle, their_did: &str, timeout: Duration) -> Result<bool, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_bool();

        let err = Pairwise::_does_exist(command_handle, wallet_handle, their_did, cb);

        ResultHandler::one_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn does_exist_async<F: 'static>(wallet_handle: IndyHandle, their_did: &str, closure: F) -> ErrorCode where F: FnMut(ErrorCode, bool) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec_bool(Box::new(closure));

        Pairwise::_does_exist(command_handle, wallet_handle, their_did, cb)
    }

    fn _does_exist(command_handle: IndyHandle, wallet_handle: IndyHandle, their_did: &str, cb: Option<ResponseBoolCB>) -> ErrorCode {
        let their_did = c_str!(their_did);

        ErrorCode::from(unsafe {
            pairwise::indy_is_pairwise_exists(command_handle, wallet_handle, their_did.as_ptr(), cb)
        })
    }

    pub fn create(wallet_handle: IndyHandle, their_did: &str, my_did: &str, metadata: Option<&str>) -> Result<(), ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec();

        let err = Pairwise::_create(command_handle, wallet_handle, their_did, my_did, metadata, cb);

        ResultHandler::empty(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn create_timeout(wallet_handle: IndyHandle, their_did: &str, my_did: &str, metadata: Option<&str>, timeout: Duration) -> Result<(), ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec();

        let err = Pairwise::_create(command_handle, wallet_handle, their_did, my_did, metadata, cb);

        ResultHandler::empty_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn create_async<F: 'static>(wallet_handle: IndyHandle, their_did: &str, my_did: &str, metadata: Option<&str>, closure: F) -> ErrorCode where F: FnMut(ErrorCode) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec(Box::new(closure));

        Pairwise::_create(command_handle, wallet_handle, their_did, my_did, metadata, cb)
    }

    fn _create(command_handle: IndyHandle, wallet_handle: IndyHandle, their_did: &str, my_did: &str, metadata: Option<&str>, cb: Option<ResponseEmptyCB>) -> ErrorCode {
        let their_did = c_str!(their_did);
        let my_did = c_str!(my_did);
        let metadata_str = opt_c_str!(metadata);

        ErrorCode::from(unsafe {
            pairwise::indy_create_pairwise(command_handle, wallet_handle, their_did.as_ptr(), my_did.as_ptr(), opt_c_ptr!(metadata, metadata_str), cb)
        })
    }

    pub fn list(wallet_handle: IndyHandle) -> Result<String, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string();

        let err = Pairwise::_list(command_handle, wallet_handle, cb);

        ResultHandler::one(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn list_timeout(wallet_handle: IndyHandle, timeout: Duration) -> Result<String, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string();

        let err = Pairwise::_list(command_handle, wallet_handle, cb);

        ResultHandler::one_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn list_async<F: 'static>(wallet_handle: IndyHandle, closure: F) -> ErrorCode where F: FnMut(ErrorCode, String) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec_string(Box::new(closure));

        Pairwise::_list(command_handle, wallet_handle, cb)
    }

    fn _list(command_handle: IndyHandle, wallet_handle: IndyHandle, cb: Option<ResponseStringCB>) -> ErrorCode {
        ErrorCode::from(unsafe {
            pairwise::indy_list_pairwise(command_handle, wallet_handle, cb)
        })
    }

    pub fn get(wallet_handle: IndyHandle, their_did: &str) -> Result<String, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string();

        let err = Pairwise::_get(command_handle, wallet_handle, their_did, cb);

        ResultHandler::one(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn get_timeout(wallet_handle: IndyHandle, their_did: &str, timeout: Duration) -> Result<String, ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string();

        let err = Pairwise::_get(command_handle, wallet_handle, their_did, cb);

        ResultHandler::one_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn get_async<F: 'static>(wallet_handle: IndyHandle, their_did: &str, closure: F) -> ErrorCode where F: FnMut(ErrorCode, String) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec_string(Box::new(closure));

        Pairwise::_get(command_handle, wallet_handle, their_did, cb)
    }

    fn _get(command_handle: IndyHandle, wallet_handle: IndyHandle, their_did: &str, cb: Option<ResponseStringCB>) -> ErrorCode {
        let their_did = c_str!(their_did);

        ErrorCode::from(unsafe {
            pairwise::indy_get_pairwise(command_handle, wallet_handle, their_did.as_ptr(), cb)
        })
    }

    pub fn set_metadata(wallet_handle: IndyHandle, their_did: &str, metadata: Option<&str>) -> Result<(), ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec();

        let err = Pairwise::_set_metadata(command_handle, wallet_handle, their_did, metadata, cb);

        ResultHandler::empty(err, receiver)
    }

    /// * `timeout` - the maximum time this function waits for a response
    pub fn set_metadata_timeout(wallet_handle: IndyHandle, their_did: &str, metadata: Option<&str>, timeout: Duration) -> Result<(), ErrorCode> {
        let (receiver, command_handle, cb) = ClosureHandler::cb_ec();

        let err = Pairwise::_set_metadata(command_handle, wallet_handle, their_did, metadata, cb);

        ResultHandler::empty_timeout(err, receiver, timeout)
    }

    /// * `closure` - the closure that is called when finished
    ///
    /// # Returns
    /// * `errorcode` - errorcode from calling ffi function. The closure receives the return result
    pub fn set_metadata_async<F: 'static>(wallet_handle: IndyHandle, their_did: &str, metadata: Option<&str>, closure: F) -> ErrorCode where F: FnMut(ErrorCode) + Send {
        let (command_handle, cb) = ClosureHandler::convert_cb_ec(Box::new(closure));

        Pairwise::_set_metadata(command_handle, wallet_handle, their_did, metadata, cb)
    }

    fn _set_metadata(command_handle: IndyHandle, wallet_handle: IndyHandle, their_did: &str, metadata: Option<&str>, cb: Option<ResponseEmptyCB>) -> ErrorCode {
        let their_did = c_str!(their_did);
        let metadata_str = opt_c_str!(metadata);

        ErrorCode::from(unsafe {
            pairwise::indy_set_pairwise_metadata(command_handle, wallet_handle, their_did.as_ptr(), opt_c_ptr!(metadata, metadata_str), cb)
        })
    }
}
<|endoftext|>