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

//! PCAP network scanner definitions.

use std::ptr;
use std::fmt;
use std::thread;
use std::result;

use std::error::Error;
use std::thread::JoinHandle;
use std::sync::{Arc, Mutex};
use std::ffi::CString;
use std::fmt::{Display, Formatter};

use utils;

use time;

use libc::{c_int, c_uint, c_long, c_char, c_uchar, c_void, size_t};

/// PCAP module error.
#[derive(Debug)]
pub struct PcapError {
    msg: String,
}

impl PcapError {
    unsafe fn from_cstr(msg: *const c_char) -> PcapError {
        PcapError { msg: utils::cstr_to_string(msg as *const _) }
    }

    fn from_pcap(p: pcap_t) -> PcapError {
        unsafe {
            let cstr = pcap_geterr(p);
            Self::from_cstr(cstr)
        }
    }
}

impl Error for PcapError {
    fn description(&self) -> &str {
        &self.msg
    }
}

impl Display for PcapError {
    fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> {
        f.write_str(&self.msg)
    }
}

impl<'a> From<&'a str> for PcapError {
    fn from(msg: &'a str) -> PcapError {
        PcapError { msg: msg.to_string() }
    }
}

pub type Result<T> = result::Result<T, PcapError>;

#[allow(non_camel_case_types)]
type pcap_t      = *mut c_void;
#[allow(non_camel_case_types)]
type bpf_u_int32 = c_uint;
#[allow(non_camel_case_types)]
type time_t      = c_long;
#[allow(non_camel_case_types)]
type suseconds_t = c_long;

#[repr(C)]
#[allow(non_camel_case_types)]
struct timeval {
    tv_sec:  time_t,
    tv_usec: suseconds_t,
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct pcap_pkthdr {
    ts:     timeval,
    caplen: bpf_u_int32,
    len:    bpf_u_int32,
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct bpf_program {
    bf_len:   c_uint,
    bf_insns: *mut c_void,
}

impl bpf_program {
    fn new() -> bpf_program {
        bpf_program {
            bf_len:   0,
            bf_insns: ptr::null_mut()
        }
    }
}

#[link(name = "pcap")]
extern "C" {
    fn pcap_create(source: *const c_char, errbuf: *mut c_char) -> pcap_t;
    fn pcap_activate(p: pcap_t) -> c_int;
    fn pcap_close(p: pcap_t) -> c_void;
    fn pcap_geterr(p: pcap_t) -> *const c_char;
    fn pcap_set_promisc(p: pcap_t, promisc: c_int) -> c_int;
    fn pcap_set_timeout(p: pcap_t, ms: c_int) -> c_int;
    fn pcap_next_ex(
        p: pcap_t,
        header: *mut *mut pcap_pkthdr,
        data: *mut *const c_uchar) -> c_int;
    fn pcap_compile(
        p: pcap_t,
        prog: *mut bpf_program,
        pstr: *const c_char,
        optimize: c_int,
        netmask: bpf_u_int32) -> c_int;
    fn pcap_freecode(prog: *mut bpf_program) -> c_void;
    fn pcap_setfilter(p: pcap_t, prog: *mut bpf_program) -> c_int;
    fn pcap_inject(p: pcap_t, buf: *const c_void, size: size_t) -> c_int;
}

/// PCAP context for synchronizing thread unsafe calls.
pub struct Context;

/// PCAP context for synchronizing thread unsafe calls.
pub type ThreadingContext = Arc<Mutex<Context>>;

/// Create a new PCAP context for synchronizing thread unsafe calls.
pub fn new_threading_context() -> ThreadingContext {
    Arc::new(Mutex::new(Context))
}

/// PCAP Capture builder.
pub struct CaptureBuilder {
    capture: Capture,
}

impl CaptureBuilder {
    /// Create a new CaptureBuilder for a given device.
    pub fn new(pc: ThreadingContext, dname: &str) -> Result<CaptureBuilder> {
        let mut result = CaptureBuilder {
            capture: Capture {
                pc:     pc,
                errbuf: Box::new([0; 4096]),
                h:      ptr::null_mut()
            }
        };

        let dname_cstr = CString::new(dname).unwrap();
        let dname_ptr = dname_cstr.as_ptr() as *const c_char;
        let errbuf_ptr = result.capture.errbuf.as_mut_ptr();
        result.capture.h = unsafe {
            pcap_create(dname_ptr, errbuf_ptr as *mut c_char)
        };

        if result.capture.h.is_null() {
            Err(unsafe { PcapError::from_cstr(errbuf_ptr as *const c_char) })
        } else {
            Ok(result)
        }
    }

    /// Set promiscuous mode.
    pub fn promisc(self, p: bool) -> CaptureBuilder {
        unsafe { pcap_set_promisc(self.capture.h, p as c_int); }
        self
    }

    /// Set timeout.
    pub fn timeout(self, ms: i32) -> CaptureBuilder {
        unsafe { pcap_set_timeout(self.capture.h, ms as c_int); }
        self
    }

    /// Activate.
    pub fn activate(self) -> Result<Capture> {
        let ret = unsafe { pcap_activate(self.capture.h) };
        match ret {
            0 => Ok(self.capture),
            _ => Err(PcapError::from_pcap(self.capture.h))
        }
    }
}

/// Capture result.
pub type CaptureResult = Result<Option<Vec<u8>>>;

/// PCAP capture.
pub struct Capture {
    pc:     ThreadingContext,
    errbuf: Box<[i8; 4096]>,
    h:      pcap_t,
}

impl Capture {
    /// Capture next packet.
    pub fn next(&mut self) -> CaptureResult {
        let mut header: *mut pcap_pkthdr = ptr::null_mut();
        let mut data:   *const c_uchar   = ptr::null();

        unsafe {
            match pcap_next_ex(self.h, &mut header, &mut data) {
                1 => {
                    let href = &*header;
                    let vec  = utils::vec_from_raw_parts(
                        data, href.caplen as usize);
                    Ok(Some(vec))
                },
                0 => Ok(None),
                _ => Err(PcapError::from_pcap(self.h))
            }
        }
    }

    /// Set packet filter.
    pub fn filter(&mut self, f: &str) -> Result<()> {
        unsafe {
            let mut prog = try!(self.compile_filter(f));
            let ret      = pcap_setfilter(self.h, &mut prog);

            pcap_freecode(&mut prog);

            match ret {
                0 => Ok(()),
                _ => Err(PcapError::from_pcap(self.h))
            }
        }
    }

    /// Inject a given raw packet.
    pub fn inject(&mut self, data: &[u8]) -> Result<usize> {
        let ptr = data.as_ptr() as *const c_void;
        let ret = unsafe {
            pcap_inject(self.h, ptr, data.len() as size_t)
        };

        if ret < 0 {
            Err(PcapError::from_pcap(self.h))
        } else {
            Ok(ret as usize)
        }
    }

    /// Compile a given filter string.
    unsafe fn compile_filter(&mut self, f: &str) -> Result<bpf_program> {
        let _lock = self.pc.lock()
            .unwrap();

        let f_cstr = CString::new(f).unwrap();
        let f_ptr = f_cstr.as_ptr() as *const c_char;

        let mut prog = bpf_program::new();

        match pcap_compile(self.h, &mut prog, f_ptr, 0, 0) {
            0 => Ok(prog),
            _ => Err(PcapError::from_pcap(self.h))
        }
    }
}

impl Drop for Capture {
    fn drop(&mut self) {
        if !self.h.is_null() {
            unsafe { pcap_close(self.h); }
        }
    }
}

unsafe impl Send for Capture {
}

/// Common trait for packet generators which may be used in combination with
/// the PCAP packet scanner.
pub trait PacketGenerator {
    /// Get next packet.
    fn next<'a>(&'a mut self) -> Option<&'a [u8]>;
}

/// PCAP packet scanner (implementation of a send-receive service).
pub struct Scanner {
    pc:            ThreadingContext,
    device:        String,
    end_indicator: Arc<Mutex<bool>>
}

impl Scanner {
    /// Create a new PCAP scanner for a given device.
    pub fn new(pc: ThreadingContext, device: &str) -> Scanner {
        Scanner {
            pc:            pc,
            device:        device.to_string(),
            end_indicator: Arc::new(Mutex::new(false))
        }
    }

    /// Send all packets from a given iterator and receive all packets
    /// according to a given filter.
    pub fn sr<G: PacketGenerator>(
        &mut self,
        filter: &str,
        gen: &mut G,
        timeout: u64) -> Result<Vec<Vec<u8>>> {
        self.set_end_indicator(false);

        let thread = try!(self.start_listener(filter, timeout));

        try!(self.send_requests(gen));

        self.set_end_indicator(true);

        match thread.join() {
            Err(_)  => Err(PcapError::from("listener thread panicked")),
            Ok(res) => Ok(res)
        }
    }

    /// Start packet listener thread.
    fn start_listener(
        &mut self,
        filter: &str,
        timeout: u64) -> Result<JoinHandle<Vec<Vec<u8>>>> {
        let ei = self.end_indicator.clone();

        let cap = try!(CaptureBuilder::new(self.pc.clone(), &self.device))
            .timeout((timeout / 1000000) as i32)
            .promisc(true);

        let mut cap = try!(cap.activate());

        try!(cap.filter(filter));

        let handle = thread::spawn(move || {
            Self::packet_listener(cap, ei, timeout)
        });

        Ok(handle)
    }

    /// Packet listener thread.
    fn packet_listener(
        mut cap: Capture,
        shared_end_indicator: Arc<Mutex<bool>>,
        timeout: u64) -> Vec<Vec<u8>> {
        let mut vec = Vec::new();
        let mut t   = time::precise_time_ns();
        let mut end = false;

        while !end || time::precise_time_ns() < (t + timeout) {
            match cap.next() {
                Ok(Some(data)) => vec.push(data),
                Err(error)     => panic!(error),
                _ => (),
            }

            if !end && Self::get_end_indicator_value(&shared_end_indicator) {
                t   = time::precise_time_ns();
                end = true;
            }
        }

        vec
    }

    /// Send all pending packets.
    fn send_requests<G: PacketGenerator>(
        &mut self,
        gen: &mut G) -> Result<()> {
        let cap     = try!(CaptureBuilder::new(self.pc.clone(), &self.device));
        let mut cap = try!(cap.activate());

        while let Some(pkt) = gen.next() {
            try!(cap.inject(pkt));
        }

        Ok(())
    }

    /// Set listener end indicator.
    fn set_end_indicator(&mut self, val: bool) {
        let mut end_indicator = self.end_indicator.lock()
            .unwrap();

        *end_indicator = val;
    }

    /// Get end indicator value.
    fn get_end_indicator_value(end_indicator: &Arc<Mutex<bool>>) -> bool {
        let ei = end_indicator.lock()
            .unwrap();

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

//! PCAP network scanner definitions.

use std::ptr;
use std::fmt;
use std::thread;
use std::result;
use std::slice;

use std::error::Error;
use std::thread::JoinHandle;
use std::sync::{Arc, Mutex};
use std::ffi::CString;
use std::fmt::{Display, Formatter};
use std::time::Duration;

use utils;

use time;

use libc::{c_int, c_uint, c_long, c_char, c_uchar, c_void, size_t};

/// PCAP module error.
#[derive(Debug)]
pub struct PcapError {
    msg: String,
}

impl PcapError {
    unsafe fn from_cstr(msg: *const c_char) -> PcapError {
        PcapError { msg: utils::cstr_to_string(msg as *const _) }
    }

    fn from_pcap(p: pcap_t) -> PcapError {
        unsafe {
            let cstr = pcap_geterr(p);
            Self::from_cstr(cstr)
        }
    }
}

impl Error for PcapError {
    fn description(&self) -> &str {
        &self.msg
    }
}

impl Display for PcapError {
    fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> {
        f.write_str(&self.msg)
    }
}

impl<'a> From<&'a str> for PcapError {
    fn from(msg: &'a str) -> PcapError {
        PcapError { msg: msg.to_string() }
    }
}

pub type Result<T> = result::Result<T, PcapError>;

#[allow(non_camel_case_types)]
type pcap_t      = *mut c_void;
#[allow(non_camel_case_types)]
type bpf_u_int32 = c_uint;
#[allow(non_camel_case_types)]
type time_t      = c_long;
#[allow(non_camel_case_types)]
type suseconds_t = c_long;

#[allow(non_camel_case_types)]
type pcap_handler = extern fn(*mut c_uchar, *const pcap_pkthdr, *const c_uchar);

#[repr(C)]
#[allow(non_camel_case_types)]
struct timeval {
    tv_sec:  time_t,
    tv_usec: suseconds_t,
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct pcap_pkthdr {
    ts:     timeval,
    caplen: bpf_u_int32,
    len:    bpf_u_int32,
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct bpf_program {
    bf_len:   c_uint,
    bf_insns: *mut c_void,
}

impl bpf_program {
    fn new() -> bpf_program {
        bpf_program {
            bf_len:   0,
            bf_insns: ptr::null_mut()
        }
    }
}

#[link(name = "pcap")]
extern "C" {
    fn pcap_create(source: *const c_char, errbuf: *mut c_char) -> pcap_t;
    fn pcap_activate(p: pcap_t) -> c_int;
    fn pcap_close(p: pcap_t) -> c_void;
    fn pcap_geterr(p: pcap_t) -> *const c_char;
    fn pcap_set_promisc(p: pcap_t, promisc: c_int) -> c_int;
    fn pcap_set_timeout(p: pcap_t, ms: c_int) -> c_int;
    fn pcap_setnonblock(p: pcap_t, nonblock: c_int, errbuf: *mut c_char) -> c_int;
    fn pcap_dispatch(
        p: pcap_t,
        cnt: c_int,
        callback: pcap_handler,
        misc: *mut c_uchar) -> c_int;
    fn pcap_compile(
        p: pcap_t,
        prog: *mut bpf_program,
        pstr: *const c_char,
        optimize: c_int,
        netmask: bpf_u_int32) -> c_int;
    fn pcap_freecode(prog: *mut bpf_program) -> c_void;
    fn pcap_setfilter(p: pcap_t, prog: *mut bpf_program) -> c_int;
    fn pcap_inject(p: pcap_t, buf: *const c_void, size: size_t) -> c_int;
}

/// PCAP context for synchronizing thread unsafe calls.
pub struct Context;

/// PCAP context for synchronizing thread unsafe calls.
pub type ThreadingContext = Arc<Mutex<Context>>;

/// Create a new PCAP context for synchronizing thread unsafe calls.
pub fn new_threading_context() -> ThreadingContext {
    Arc::new(Mutex::new(Context))
}

/// PCAP Capture builder.
pub struct CaptureBuilder {
    capture: Capture,
}

impl CaptureBuilder {
    /// Create a new CaptureBuilder for a given device.
    pub fn new(pc: ThreadingContext, dname: &str) -> Result<CaptureBuilder> {
        let mut result = CaptureBuilder {
            capture: Capture {
                pc:     pc,
                errbuf: Box::new([0; 4096]),
                h:      ptr::null_mut()
            }
        };

        let dname_cstr = CString::new(dname).unwrap();
        let dname_ptr = dname_cstr.as_ptr() as *const c_char;
        let errbuf_ptr = result.capture.errbuf.as_mut_ptr();
        result.capture.h = unsafe {
            pcap_create(dname_ptr, errbuf_ptr as *mut c_char)
        };

        if result.capture.h.is_null() {
            Err(unsafe { PcapError::from_cstr(errbuf_ptr as *const c_char) })
        } else {
            Ok(result)
        }
    }

    /// Set promiscuous mode.
    pub fn promisc(self, p: bool) -> CaptureBuilder {
        unsafe { pcap_set_promisc(self.capture.h, p as c_int); }
        self
    }

    /// Set timeout.
    pub fn timeout(self, ms: i32) -> CaptureBuilder {
        unsafe { pcap_set_timeout(self.capture.h, ms as c_int); }
        self
    }

    /// Activate.
    pub fn activate(self) -> Result<Capture> {
        let ret = unsafe { pcap_activate(self.capture.h) };
        match ret {
            0 => Ok(self.capture),
            _ => Err(PcapError::from_pcap(self.capture.h))
        }
    }
}

/// Capture result.
pub type CaptureResult = Result<Option<Vec<u8>>>;

/// PCAP capture.
pub struct Capture {
    pc:     ThreadingContext,
    errbuf: Box<[c_char; 4096]>,
    h:      pcap_t,
}

impl Capture {
    /// Start capturing packets (at most count) and pass them to a given callback.
    pub fn dispatch<F>(&mut self, count: usize, callback: F) -> Result<usize>
        where F: FnMut(&[u8]) {
        let mut callback: Box<FnMut(&[u8])> = Box::new(callback);

        let misc = &mut callback as *mut Box<FnMut(&[u8])>;

        let res = unsafe {
            pcap_dispatch(
                self.h,
                count as c_int,
                capture_packet_callback,
                misc as *mut c_uchar)
        };

        if res < 0 {
            Err(PcapError::from_pcap(self.h))
        } else {
            Ok(res as usize)
        }
    }

    /// Set the underlaying file descriptor into non-blocking mode.
    pub fn set_non_blocking(&mut self, non_blocking: bool) -> Result<()> {
        let mut errbuf = [0; 4096];

        unsafe {
            let eb = errbuf.as_mut_ptr();
            let nb = match non_blocking {
                true  => 1,
                false => 0,
            };

            if pcap_setnonblock(self.h, nb, eb) < 0 {
                Err(PcapError::from_cstr(eb))
            } else {
                Ok(())
            }
        }
    }

    /// Set packet filter.
    pub fn filter(&mut self, f: &str) -> Result<()> {
        unsafe {
            let mut prog = try!(self.compile_filter(f));
            let ret      = pcap_setfilter(self.h, &mut prog);

            pcap_freecode(&mut prog);

            match ret {
                0 => Ok(()),
                _ => Err(PcapError::from_pcap(self.h))
            }
        }
    }

    /// Inject a given raw packet.
    pub fn inject(&mut self, data: &[u8]) -> Result<usize> {
        let ptr = data.as_ptr() as *const c_void;
        let ret = unsafe {
            pcap_inject(self.h, ptr, data.len() as size_t)
        };

        if ret < 0 {
            Err(PcapError::from_pcap(self.h))
        } else {
            Ok(ret as usize)
        }
    }

    /// Compile a given filter string.
    unsafe fn compile_filter(&mut self, f: &str) -> Result<bpf_program> {
        let _lock = self.pc.lock()
            .unwrap();

        let f_cstr = CString::new(f).unwrap();
        let f_ptr = f_cstr.as_ptr() as *const c_char;

        let mut prog = bpf_program::new();

        match pcap_compile(self.h, &mut prog, f_ptr, 0, 0) {
            0 => Ok(prog),
            _ => Err(PcapError::from_pcap(self.h))
        }
    }
}

impl Drop for Capture {
    fn drop(&mut self) {
        if !self.h.is_null() {
            unsafe { pcap_close(self.h); }
        }
    }
}

unsafe impl Send for Capture {
}

extern "C" fn capture_packet_callback(
    misc: *mut c_uchar,
    header: *const pcap_pkthdr,
    data: *const c_uchar) {
    unsafe {
        let callback = misc as *mut Box<FnMut(&[u8])>;
        let header   = &*header;

        let data = slice::from_raw_parts(
            data as *const u8,
            header.caplen as usize);

        (*callback)(data);
    }
}

/// Common trait for packet generators which may be used in combination with
/// the PCAP packet scanner.
pub trait PacketGenerator {
    /// Get next packet.
    fn next<'a>(&'a mut self) -> Option<&'a [u8]>;
}

/// PCAP packet scanner (implementation of a send-receive service).
pub struct Scanner {
    pc:            ThreadingContext,
    device:        String,
    end_indicator: Arc<Mutex<bool>>,
}

impl Scanner {
    /// Create a new PCAP scanner for a given device.
    pub fn new(pc: ThreadingContext, device: &str) -> Scanner {
        Scanner {
            pc:            pc,
            device:        device.to_string(),
            end_indicator: Arc::new(Mutex::new(false)),
        }
    }

    /// Send all packets from a given iterator and receive all packets
    /// according to a given filter.
    pub fn sr<G: PacketGenerator>(
        &mut self,
        filter: &str,
        gen: &mut G,
        timeout: u64) -> Result<Vec<Vec<u8>>> {
        self.set_end_indicator(false);

        let thread = try!(self.start_listener(filter, timeout));

        try!(self.send_requests(gen));

        self.set_end_indicator(true);

        match thread.join() {
            Err(_)  => Err(PcapError::from("listener thread panicked")),
            Ok(res) => Ok(res)
        }
    }

    /// Start packet listener thread.
    fn start_listener(
        &mut self,
        filter: &str,
        timeout: u64) -> Result<JoinHandle<Vec<Vec<u8>>>> {
        let ei = self.end_indicator.clone();

        let cap = try!(CaptureBuilder::new(self.pc.clone(), &self.device))
            .timeout((timeout / 1000000) as i32)
            .promisc(true);

        let mut cap = try!(cap.activate());

        try!(cap.filter(filter));

        try!(cap.set_non_blocking(true));

        let handle = thread::spawn(move || {
            Self::packet_listener(cap, ei, timeout)
        });

        Ok(handle)
    }

    /// Packet listener thread.
    fn packet_listener(
        mut cap: Capture,
        shared_end_indicator: Arc<Mutex<bool>>,
        timeout: u64) -> Vec<Vec<u8>> {
        let mut vec = Vec::new();
        let mut t   = time::precise_time_ns();
        let mut end = false;

        while !end || time::precise_time_ns() < (t + timeout) {
            let callback = |data: &[u8]| {
                vec.push(data.to_vec());
            };

            let res = match cap.dispatch(1, callback) {
                Err(error) => panic!(error),
                Ok(count)  => count,
            };

            // XXX: this is a hack to avoid high CPU utilization because some
            // platforms don't support timeout of blocking capture
            if res == 0 {
                thread::sleep(Duration::from_millis(20));
            }

            if !end && Self::get_end_indicator_value(&shared_end_indicator) {
                t   = time::precise_time_ns();
                end = true;
            }
        }

        vec
    }

    /// Send all pending packets.
    fn send_requests<G: PacketGenerator>(
        &mut self,
        gen: &mut G) -> Result<()> {
        let cap     = try!(CaptureBuilder::new(self.pc.clone(), &self.device));
        let mut cap = try!(cap.activate());

        while let Some(pkt) = gen.next() {
            try!(cap.inject(pkt));
        }

        Ok(())
    }

    /// Set listener end indicator.
    fn set_end_indicator(&mut self, val: bool) {
        let mut end_indicator = self.end_indicator.lock()
            .unwrap();

        *end_indicator = val;
    }

    /// Get end indicator value.
    fn get_end_indicator_value(end_indicator: &Arc<Mutex<bool>>) -> bool {
        let ei = end_indicator.lock()
            .unwrap();

        *ei
    }
}
<|endoftext|>


<commit_before>//! Asynchronous notifications.

use std::fmt;
use std::time::Duration;

use {desynchronized, Result, Connection, NotificationsNew};
use message::BackendMessage::NotificationResponse;
use error::Error;

/// An asynchronous notification.
#[derive(Clone, Debug)]
pub struct Notification {
    /// The process ID of the notifying backend process.
    pub pid: u32,
    /// The name of the channel that the notify has been raised on.
    pub channel: String,
    /// The "payload" string passed from the notifying process.
    pub payload: String,
}

/// Notifications from the Postgres backend.
pub struct Notifications<'conn> {
    conn: &'conn Connection,
}

impl<'a> fmt::Debug for Notifications<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Notifications")
           .field("pending", &self.len())
           .finish()
    }
}

impl<'conn> Notifications<'conn> {
    /// Returns the number of pending notifications.
    pub fn len(&self) -> usize {
        self.conn.conn.borrow().notifications.len()
    }

    /// Returns an iterator over pending notifications.
    ///
    /// # Note
    ///
    /// This iterator may start returning `Some` after previously returning
    /// `None` if more notifications are received.
    pub fn iter<'a>(&'a self) -> Iter<'a> {
        Iter { conn: self.conn }
    }

    /// Returns an iterator over notifications that blocks until one is
    /// received if none are pending.
    ///
    /// The iterator will never return `None`.
    pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> {
        BlockingIter { conn: self.conn }
    }

    /// Returns an iterator over notifications that blocks for a limited time
    /// waiting to receive one if none are pending.
    ///
    /// # Note
    ///
    /// This iterator may start returning `Some` after previously returning
    /// `None` if more notifications are received.
    pub fn timeout_iter<'a>(&'a self, timeout: Duration) -> TimeoutIter<'a> {
        TimeoutIter {
            conn: self.conn,
            timeout: timeout,
        }
    }
}

impl<'a, 'conn> IntoIterator for &'a Notifications<'conn> {
    type Item = Result<Notification>;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

impl<'conn> NotificationsNew<'conn> for Notifications<'conn> {
    fn new(conn: &'conn Connection) -> Notifications<'conn> {
        Notifications { conn: conn }
    }
}

/// An iterator over pending notifications.
pub struct Iter<'a> {
    conn: &'a Connection,
}

impl<'a> Iterator for Iter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification_nonblocking() {
            Ok(Some(NotificationResponse { pid, channel, payload })) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Ok(None) => None,
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }
}

/// An iterator over notifications which will block if none are pending.
pub struct BlockingIter<'a> {
    conn: &'a Connection,
}

impl<'a> Iterator for BlockingIter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification() {
            Ok(NotificationResponse { pid, channel, payload }) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (usize::max_value(), None)
    }
}

/// An iterator over notifications which will block for a period of time if
/// none are pending.
pub struct TimeoutIter<'a> {
    conn: &'a Connection,
    timeout: Duration,
}

impl<'a> Iterator for TimeoutIter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification_timeout(self.timeout) {
            Ok(Some(NotificationResponse { pid, channel, payload })) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Ok(None) => None,
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }
}
<commit_msg>Add better size hints for notification iterators<commit_after>//! Asynchronous notifications.

use std::fmt;
use std::time::Duration;

use {desynchronized, Result, Connection, NotificationsNew};
use message::BackendMessage::NotificationResponse;
use error::Error;

/// An asynchronous notification.
#[derive(Clone, Debug)]
pub struct Notification {
    /// The process ID of the notifying backend process.
    pub pid: u32,
    /// The name of the channel that the notify has been raised on.
    pub channel: String,
    /// The "payload" string passed from the notifying process.
    pub payload: String,
}

/// Notifications from the Postgres backend.
pub struct Notifications<'conn> {
    conn: &'conn Connection,
}

impl<'a> fmt::Debug for Notifications<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Notifications")
           .field("pending", &self.len())
           .finish()
    }
}

impl<'conn> Notifications<'conn> {
    /// Returns the number of pending notifications.
    pub fn len(&self) -> usize {
        self.conn.conn.borrow().notifications.len()
    }

    /// Returns an iterator over pending notifications.
    ///
    /// # Note
    ///
    /// This iterator may start returning `Some` after previously returning
    /// `None` if more notifications are received.
    pub fn iter<'a>(&'a self) -> Iter<'a> {
        Iter { conn: self.conn }
    }

    /// Returns an iterator over notifications that blocks until one is
    /// received if none are pending.
    ///
    /// The iterator will never return `None`.
    pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> {
        BlockingIter { conn: self.conn }
    }

    /// Returns an iterator over notifications that blocks for a limited time
    /// waiting to receive one if none are pending.
    ///
    /// # Note
    ///
    /// This iterator may start returning `Some` after previously returning
    /// `None` if more notifications are received.
    pub fn timeout_iter<'a>(&'a self, timeout: Duration) -> TimeoutIter<'a> {
        TimeoutIter {
            conn: self.conn,
            timeout: timeout,
        }
    }
}

impl<'a, 'conn> IntoIterator for &'a Notifications<'conn> {
    type Item = Result<Notification>;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

impl<'conn> NotificationsNew<'conn> for Notifications<'conn> {
    fn new(conn: &'conn Connection) -> Notifications<'conn> {
        Notifications { conn: conn }
    }
}

/// An iterator over pending notifications.
pub struct Iter<'a> {
    conn: &'a Connection,
}

impl<'a> Iterator for Iter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification_nonblocking() {
            Ok(Some(NotificationResponse { pid, channel, payload })) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Ok(None) => None,
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.conn.conn.borrow().notifications.len(), None)
    }
}

/// An iterator over notifications which will block if none are pending.
pub struct BlockingIter<'a> {
    conn: &'a Connection,
}

impl<'a> Iterator for BlockingIter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification() {
            Ok(NotificationResponse { pid, channel, payload }) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (usize::max_value(), None)
    }
}

/// An iterator over notifications which will block for a period of time if
/// none are pending.
pub struct TimeoutIter<'a> {
    conn: &'a Connection,
    timeout: Duration,
}

impl<'a> Iterator for TimeoutIter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification_timeout(self.timeout) {
            Ok(Some(NotificationResponse { pid, channel, payload })) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Ok(None) => None,
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.conn.conn.borrow().notifications.len(), None)
    }
}
<|endoftext|>


<commit_before>use std::thread::spawn ;
use std::thread ;
use std::sync::mpsc ;
use std::sync::{Arc, Mutex} ;
use std::time::Duration ;
use std::path::Path ;
use std::collections::HashMap ;
use std::str::FromStr ;
use std::path::PathBuf ;

use log ;

const DEBUG_LOGGER:bool = false & cfg!(test) ;

#[cfg(test)]
use env_logger ;
#[cfg(test)]
use std::sync::{Once, ONCE_INIT} ;

#[cfg(test)]
static START: Once = ONCE_INIT ;

mod network_manager ;
mod log_message ;
mod message_handler ;
mod logger_state ;
mod message_worker ;

// Exports Level & Domain as part of the public interface
pub use self::log_message::{Level,Domain} ;

use self::log_message::{LogMessage,LogMessageType,MessagePartKey} ;
use self::message_worker::MessageWorker ;
use self::logger_state::{LoggerState,HandlerMessageType} ;

bitflags! {
    pub struct LoggerOptions: u16 {
        const FLUSH_EACH_MESSAGE   = 0b00000001 ;
        // If set, NSLogger waits for each message to be sent to the desktop viewer (this includes connecting to the viewer)

        const BROWSE_BONJOUR       = 0b00000010 ;
        const USE_SSL              = 0b00000100 ;
        const ROUTE_TO_LOGCAT      = 0b00001000 ;
    }
}

pub struct Logger {
    shared_state: Arc<Mutex<LoggerState>>,
    message_sender:mpsc::Sender<HandlerMessageType>,
}

impl Logger {

    pub fn new() -> Logger {
        if DEBUG_LOGGER {
            cfg_if! {
                if #[cfg(test)] {
                    fn init_test_logger() {
                        START.call_once(|| {

                            env_logger::init().unwrap() ;
                        }) ;
                        info!(target:"NSLogger", "NSLogger client started") ;
                    }
                }
                else {
                    fn init_test_logger() {}
                }
            }

            init_test_logger() ;
        }
        let (message_sender, message_receiver) = mpsc::channel() ;
        let sender_clone = message_sender.clone() ;

        return Logger{ message_sender: message_sender,
                       shared_state: Arc::new(Mutex::new(LoggerState::new(sender_clone, message_receiver))),
                      } ;
    }

    pub fn set_bonjour_service(&mut self, service_type:Option<&str>, service_name:Option<&str>, use_ssl:bool) {
        if self.shared_state.lock().unwrap().ready {
            let mut properties = HashMap::new() ;

            if let Some(name_value) = service_name {
                properties.insert("bonjour_service".to_string(), String::from(name_value)) ;
            }

            if let Some(type_value) = service_type {
                properties.insert("bonjour_type".to_string(), String::from(type_value)) ;
            }

            properties.insert("use_ssl".to_string(), String::from(if use_ssl { "1" } else { "0" })) ;

            self.message_sender.send(HandlerMessageType::OptionChange(properties)) ;
        }
        else {
            // Worker thread isn't yet setup
            let mut local_shared_state = self.shared_state.lock().unwrap() ;
            (*local_shared_state).bonjour_service_name = service_name.and_then( |v| Some(v.to_string()) ) ;
            (*local_shared_state).bonjour_service_type = service_type.and_then( |v| Some(v.to_string()) ) ;
            (*local_shared_state).options |= BROWSE_BONJOUR ;

            if use_ssl {
                (*local_shared_state).options = local_shared_state.options | USE_SSL ;
            } else {
                (*local_shared_state).options = local_shared_state.options - USE_SSL ;
            }
        }
    }

    pub fn set_remote_host(&mut self, host_name:&str, host_port:u16, use_ssl:bool) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "set_remote_host host={} port={} use_ssl={}", host_name, host_port, use_ssl) ;
        }

        if self.shared_state.lock().unwrap().ready {
            let mut properties = HashMap::new() ;
            properties.insert("remote_host".to_string(), String::from(host_name)) ;
            properties.insert("remote_port".to_string(), String::from(format!("{}", host_port))) ;
            properties.insert("use_ssl".to_string(), String::from(if use_ssl { "1" } else { "0" })) ;

            self.message_sender.send(HandlerMessageType::OptionChange(properties)) ;
        }
        else {
            // Worker thread isn't yet setup
            let mut local_shared_state = self.shared_state.lock().unwrap() ;
            (*local_shared_state).remote_host = Some(String::from(host_name)) ;
            (*local_shared_state).remote_port = Some(host_port) ;

            if use_ssl {
                (*local_shared_state).options = local_shared_state.options | USE_SSL ;
            } else {
                (*local_shared_state).options = local_shared_state.options - USE_SSL ;
            }
        }
    }

    pub fn set_log_file_path(&mut self, file_path:&str) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "set_log_file_path path={:?}", file_path) ;
        }

        if self.shared_state.lock().unwrap().ready {
            let mut properties = HashMap::new() ;
            properties.insert("filename".to_string(), String::from(file_path)) ;

            self.message_sender.send(HandlerMessageType::OptionChange(properties)) ;
        }
        else {
            self.shared_state.lock().unwrap().log_file_path = Some(PathBuf::from(file_path.to_string())) ;
        }
    }

    pub fn set_message_flushing(&mut self, flush_each_message:bool) {
        let mut local_state = self.shared_state.lock().unwrap() ;
        if flush_each_message {
            local_state.options |= FLUSH_EACH_MESSAGE ;
        } else {
            local_state.options -= FLUSH_EACH_MESSAGE ;
        }
    }

    pub fn logl(&self, filename:Option<&Path>, line_number:Option<usize>, method:Option<&str>, domain:Option<Domain>, level:Level, message:&str) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "entering log") ;
        }
        self.start_logging_thread_if_needed() ;

        if !self.shared_state.lock().unwrap().is_handler_running {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "Early return") ;
            }
            return ;
        }

        let mut log_message = LogMessage::with_header(LogMessageType::Log,
                                                      self.shared_state.lock().unwrap().get_and_increment_sequence_number(),
                                                      filename,
                                                      line_number,
                                                      method,
                                                      domain,
                                                      level) ;

        log_message.add_string(MessagePartKey::Message, message) ;

        self.send_and_flush_if_required(log_message) ;
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "Exiting log") ;
        }
    }

    pub fn logm(&self, domain: Option<Domain>, level: Level, message:&str) {
        self.logl(None, None, None, domain, level, message) ;
    }

    pub fn log(&self, message:&str) {
        self.logm(None, Level::Error, message) ;
    }

	/// Log a mark to the desktop viewer.
    ///
    /// Marks are important points that you can jump to directly in the desktop viewer. Message is
    /// optional, if null or empty it will be replaced with the current date / time
    ///
	/// * `message`	optional message
	///
    pub fn log_mark(&self, message:Option<&str>) {
        use chrono ;
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "entering log_mark") ;
        }
        self.start_logging_thread_if_needed() ;
        if !self.shared_state.lock().unwrap().is_handler_running {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "Early return") ;
            }
            return ;
        }

        let mut log_message = LogMessage::with_header(LogMessageType::Mark,
                                                      self.shared_state.lock().unwrap().get_and_increment_sequence_number(),
                                                      None,
                                                      None,
                                                      None,
                                                      None,
                                                      Level::Error) ;

        let mark_message = match message {
            Some(inner) => inner.to_string(),
            None => {
                let time_now = chrono::Utc::now() ;

                time_now.format("%b %-d, %-I:%M:%S").to_string()
            }
        } ;

        log_message.add_string(MessagePartKey::Message, &mark_message) ;

        self.send_and_flush_if_required(log_message) ;

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "leaving log_mark") ;
        }
    }

    pub fn log_data(&self, filename:Option<&Path>, line_number:Option<usize>, method:Option<&str>,
                     domain:Option<Domain>, level:Level, data:&[u8]) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "entering log_data") ;
        }

        self.start_logging_thread_if_needed() ;
        if !self.shared_state.lock().unwrap().is_handler_running {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "Early return") ;
            }
            return ;
        }

        let mut log_message = LogMessage::with_header(LogMessageType::Log,
                                                      self.shared_state.lock().unwrap().get_and_increment_sequence_number(),
                                                      filename,
                                                      line_number,
                                                      method,
                                                      domain,
                                                      level) ;

        log_message.add_binary_data(MessagePartKey::Message, data) ;

        self.send_and_flush_if_required(log_message) ;

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "leaving log_data") ;
        }
    }

    pub fn log_image(&mut self, filename:Option<&Path>, line_number:Option<usize>, method:Option<&str>,
                     domain:Option<Domain>, level:Level, data:&[u8]) {

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "entering log_image") ;
        }
        self.start_logging_thread_if_needed() ;
        if !self.shared_state.lock().unwrap().is_handler_running {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "Early return") ;
            }
            return ;
        }

        let mut log_message = LogMessage::with_header(LogMessageType::Log,
                                                      self.shared_state.lock().unwrap().get_and_increment_sequence_number(),
                                                      filename,
                                                      line_number,
                                                      method,
                                                      domain,
                                                      level) ;

        log_message.add_image_data(MessagePartKey::Message, data) ;

        self.send_and_flush_if_required(log_message) ;

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "leaving log_image") ;
        }
    }

    fn start_logging_thread_if_needed(&self) {
        let mut waiting = false ;

        {
            let mut local_shared_state = self.shared_state.lock().unwrap() ;
            if local_shared_state.message_receiver.is_some() {
                local_shared_state.ready_waiters.push(thread::current()) ;
                waiting = true ;

                let cloned_state = Arc::clone(&self.shared_state) ;
                // NOTE: only clones the pointer reference, not the state itself

                let receiver = local_shared_state.message_receiver.take().unwrap() ;
                let sender = self.message_sender.clone() ;
                spawn( move || {
                    MessageWorker::new(cloned_state, sender, receiver).run() ;
                }) ;
            }
        }

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "Waiting for worker to be ready") ;
        }

        // FIXME There seems to be cases where the system deadlocks here unless we forcibly make
        // one of the blocked threads do a few execution steps.
        // More specifically, the message worker is already running the message handler loop
        // (therefore it should already have dealt with the ready state...) while the logging
        // threads are waiting for the worker's ack.
        // NOTE: Replacing park_timeout with park seems to solve the problem. For some reason,
        // there seems to be some kind of locking (or deadlock, rather) going on while the parked thread tries to figure
        // out the unix time since for unparking. Double parking of the thread via ONCE.init??
        // NOTE: Is it possible to deadlock because the 'main' worker thread is in the message
        // handler loop and another thread tries to access the shared state?
        while !self.shared_state.lock().unwrap().ready {
            if !waiting {
                self.shared_state.lock().unwrap().ready_waiters.push(thread::current()) ;
                waiting = true ;
            }

            println!("Parking thread") ;

            thread::park_timeout(Duration::from_millis(100)) ;
            //if (Thread.interrupted())
            //   Thread.currentThread().interrupt();

            println!("Thread left park") ;
        }

        println!("Passed the ready condition") ;

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "Worker is ready and running") ;
        }
    }

    fn send_and_flush_if_required(& self, mut log_message:LogMessage) {
        let needs_flush = !(self.shared_state.lock().unwrap().options & FLUSH_EACH_MESSAGE).is_empty() ;
        if DEBUG_LOGGER && !needs_flush {
            warn!(target:"NSLogger", "no need to flush!!") ;
        }
        let mut flush_rx:Option<mpsc::Receiver<bool>> = None ;
        if needs_flush {
            flush_rx = log_message.flush_rx.take() ;
        }

        self.message_sender.send(HandlerMessageType::AddLog(log_message)) ;

        if needs_flush {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "waiting for message flush") ;
            }
            flush_rx.unwrap().recv() ;
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "message flush ack received") ;
            }
        }
    }
}

impl Drop for Logger {
    fn drop(&mut self) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "calling drop for logger instance") ;
        }

        self.message_sender.send(HandlerMessageType::Quit) ;

    }
}

impl log::Log for Logger {
    fn enabled(&self, metadata:&log::LogMetadata) -> bool {
        true
    }

    fn log(&self, record:&log::LogRecord) {
        if !self.enabled(record.metadata()) {
            return ;
        }

        self.logl(Some(Path::new(record.location().file())),
                   Some(record.location().line() as usize),
                   None,
                   Some(Domain::from_str(record.target()).unwrap()),
                   Level::from_log_level(record.level()),
                   &format!("{}", record.args())) ;
    }
}

unsafe impl Sync for Logger {}

unsafe impl Send for Logger {}

<commit_msg>- Removes println calls<commit_after>use std::thread::spawn ;
use std::thread ;
use std::sync::mpsc ;
use std::sync::{Arc, Mutex} ;
use std::time::Duration ;
use std::path::Path ;
use std::collections::HashMap ;
use std::str::FromStr ;
use std::path::PathBuf ;

use log ;

const DEBUG_LOGGER:bool = false & cfg!(test) ;

#[cfg(test)]
use env_logger ;
#[cfg(test)]
use std::sync::{Once, ONCE_INIT} ;

#[cfg(test)]
static START: Once = ONCE_INIT ;

mod network_manager ;
mod log_message ;
mod message_handler ;
mod logger_state ;
mod message_worker ;

// Exports Level & Domain as part of the public interface
pub use self::log_message::{Level,Domain} ;

use self::log_message::{LogMessage,LogMessageType,MessagePartKey} ;
use self::message_worker::MessageWorker ;
use self::logger_state::{LoggerState,HandlerMessageType} ;

bitflags! {
    pub struct LoggerOptions: u16 {
        const FLUSH_EACH_MESSAGE   = 0b00000001 ;
        // If set, NSLogger waits for each message to be sent to the desktop viewer (this includes connecting to the viewer)

        const BROWSE_BONJOUR       = 0b00000010 ;
        const USE_SSL              = 0b00000100 ;
        const ROUTE_TO_LOGCAT      = 0b00001000 ;
    }
}

pub struct Logger {
    shared_state: Arc<Mutex<LoggerState>>,
    message_sender:mpsc::Sender<HandlerMessageType>,
}

impl Logger {

    pub fn new() -> Logger {
        if DEBUG_LOGGER {
            cfg_if! {
                if #[cfg(test)] {
                    fn init_test_logger() {
                        START.call_once(|| {

                            env_logger::init().unwrap() ;
                        }) ;
                        info!(target:"NSLogger", "NSLogger client started") ;
                    }
                }
                else {
                    fn init_test_logger() {}
                }
            }

            init_test_logger() ;
        }
        let (message_sender, message_receiver) = mpsc::channel() ;
        let sender_clone = message_sender.clone() ;

        return Logger{ message_sender: message_sender,
                       shared_state: Arc::new(Mutex::new(LoggerState::new(sender_clone, message_receiver))),
                      } ;
    }

    pub fn set_bonjour_service(&mut self, service_type:Option<&str>, service_name:Option<&str>, use_ssl:bool) {
        if self.shared_state.lock().unwrap().ready {
            let mut properties = HashMap::new() ;

            if let Some(name_value) = service_name {
                properties.insert("bonjour_service".to_string(), String::from(name_value)) ;
            }

            if let Some(type_value) = service_type {
                properties.insert("bonjour_type".to_string(), String::from(type_value)) ;
            }

            properties.insert("use_ssl".to_string(), String::from(if use_ssl { "1" } else { "0" })) ;

            self.message_sender.send(HandlerMessageType::OptionChange(properties)) ;
        }
        else {
            // Worker thread isn't yet setup
            let mut local_shared_state = self.shared_state.lock().unwrap() ;
            (*local_shared_state).bonjour_service_name = service_name.and_then( |v| Some(v.to_string()) ) ;
            (*local_shared_state).bonjour_service_type = service_type.and_then( |v| Some(v.to_string()) ) ;
            (*local_shared_state).options |= BROWSE_BONJOUR ;

            if use_ssl {
                (*local_shared_state).options = local_shared_state.options | USE_SSL ;
            } else {
                (*local_shared_state).options = local_shared_state.options - USE_SSL ;
            }
        }
    }

    pub fn set_remote_host(&mut self, host_name:&str, host_port:u16, use_ssl:bool) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "set_remote_host host={} port={} use_ssl={}", host_name, host_port, use_ssl) ;
        }

        if self.shared_state.lock().unwrap().ready {
            let mut properties = HashMap::new() ;
            properties.insert("remote_host".to_string(), String::from(host_name)) ;
            properties.insert("remote_port".to_string(), String::from(format!("{}", host_port))) ;
            properties.insert("use_ssl".to_string(), String::from(if use_ssl { "1" } else { "0" })) ;

            self.message_sender.send(HandlerMessageType::OptionChange(properties)) ;
        }
        else {
            // Worker thread isn't yet setup
            let mut local_shared_state = self.shared_state.lock().unwrap() ;
            (*local_shared_state).remote_host = Some(String::from(host_name)) ;
            (*local_shared_state).remote_port = Some(host_port) ;

            if use_ssl {
                (*local_shared_state).options = local_shared_state.options | USE_SSL ;
            } else {
                (*local_shared_state).options = local_shared_state.options - USE_SSL ;
            }
        }
    }

    pub fn set_log_file_path(&mut self, file_path:&str) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "set_log_file_path path={:?}", file_path) ;
        }

        if self.shared_state.lock().unwrap().ready {
            let mut properties = HashMap::new() ;
            properties.insert("filename".to_string(), String::from(file_path)) ;

            self.message_sender.send(HandlerMessageType::OptionChange(properties)) ;
        }
        else {
            self.shared_state.lock().unwrap().log_file_path = Some(PathBuf::from(file_path.to_string())) ;
        }
    }

    pub fn set_message_flushing(&mut self, flush_each_message:bool) {
        let mut local_state = self.shared_state.lock().unwrap() ;
        if flush_each_message {
            local_state.options |= FLUSH_EACH_MESSAGE ;
        } else {
            local_state.options -= FLUSH_EACH_MESSAGE ;
        }
    }

    pub fn logl(&self, filename:Option<&Path>, line_number:Option<usize>, method:Option<&str>, domain:Option<Domain>, level:Level, message:&str) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "entering log") ;
        }
        self.start_logging_thread_if_needed() ;

        if !self.shared_state.lock().unwrap().is_handler_running {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "Early return") ;
            }
            return ;
        }

        let mut log_message = LogMessage::with_header(LogMessageType::Log,
                                                      self.shared_state.lock().unwrap().get_and_increment_sequence_number(),
                                                      filename,
                                                      line_number,
                                                      method,
                                                      domain,
                                                      level) ;

        log_message.add_string(MessagePartKey::Message, message) ;

        self.send_and_flush_if_required(log_message) ;
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "Exiting log") ;
        }
    }

    pub fn logm(&self, domain: Option<Domain>, level: Level, message:&str) {
        self.logl(None, None, None, domain, level, message) ;
    }

    pub fn log(&self, message:&str) {
        self.logm(None, Level::Error, message) ;
    }

	/// Log a mark to the desktop viewer.
    ///
    /// Marks are important points that you can jump to directly in the desktop viewer. Message is
    /// optional, if null or empty it will be replaced with the current date / time
    ///
	/// * `message`	optional message
	///
    pub fn log_mark(&self, message:Option<&str>) {
        use chrono ;
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "entering log_mark") ;
        }
        self.start_logging_thread_if_needed() ;
        if !self.shared_state.lock().unwrap().is_handler_running {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "Early return") ;
            }
            return ;
        }

        let mut log_message = LogMessage::with_header(LogMessageType::Mark,
                                                      self.shared_state.lock().unwrap().get_and_increment_sequence_number(),
                                                      None,
                                                      None,
                                                      None,
                                                      None,
                                                      Level::Error) ;

        let mark_message = match message {
            Some(inner) => inner.to_string(),
            None => {
                let time_now = chrono::Utc::now() ;

                time_now.format("%b %-d, %-I:%M:%S").to_string()
            }
        } ;

        log_message.add_string(MessagePartKey::Message, &mark_message) ;

        self.send_and_flush_if_required(log_message) ;

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "leaving log_mark") ;
        }
    }

    pub fn log_data(&self, filename:Option<&Path>, line_number:Option<usize>, method:Option<&str>,
                     domain:Option<Domain>, level:Level, data:&[u8]) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "entering log_data") ;
        }

        self.start_logging_thread_if_needed() ;
        if !self.shared_state.lock().unwrap().is_handler_running {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "Early return") ;
            }
            return ;
        }

        let mut log_message = LogMessage::with_header(LogMessageType::Log,
                                                      self.shared_state.lock().unwrap().get_and_increment_sequence_number(),
                                                      filename,
                                                      line_number,
                                                      method,
                                                      domain,
                                                      level) ;

        log_message.add_binary_data(MessagePartKey::Message, data) ;

        self.send_and_flush_if_required(log_message) ;

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "leaving log_data") ;
        }
    }

    pub fn log_image(&mut self, filename:Option<&Path>, line_number:Option<usize>, method:Option<&str>,
                     domain:Option<Domain>, level:Level, data:&[u8]) {

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "entering log_image") ;
        }
        self.start_logging_thread_if_needed() ;
        if !self.shared_state.lock().unwrap().is_handler_running {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "Early return") ;
            }
            return ;
        }

        let mut log_message = LogMessage::with_header(LogMessageType::Log,
                                                      self.shared_state.lock().unwrap().get_and_increment_sequence_number(),
                                                      filename,
                                                      line_number,
                                                      method,
                                                      domain,
                                                      level) ;

        log_message.add_image_data(MessagePartKey::Message, data) ;

        self.send_and_flush_if_required(log_message) ;

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "leaving log_image") ;
        }
    }

    fn start_logging_thread_if_needed(&self) {
        let mut waiting = false ;

        {
            let mut local_shared_state = self.shared_state.lock().unwrap() ;
            if local_shared_state.message_receiver.is_some() {
                local_shared_state.ready_waiters.push(thread::current()) ;
                waiting = true ;

                let cloned_state = Arc::clone(&self.shared_state) ;
                // NOTE: only clones the pointer reference, not the state itself

                let receiver = local_shared_state.message_receiver.take().unwrap() ;
                let sender = self.message_sender.clone() ;
                spawn( move || {
                    MessageWorker::new(cloned_state, sender, receiver).run() ;
                }) ;
            }
        }

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "Waiting for worker to be ready") ;
        }

        while !self.shared_state.lock().unwrap().ready {
            if !waiting {
                self.shared_state.lock().unwrap().ready_waiters.push(thread::current()) ;
                waiting = true ;
            }

            thread::park_timeout(Duration::from_millis(100)) ;
        }

        if DEBUG_LOGGER {
            info!(target:"NSLogger", "Worker is ready and running") ;
        }
    }

    fn send_and_flush_if_required(& self, mut log_message:LogMessage) {
        let needs_flush = !(self.shared_state.lock().unwrap().options & FLUSH_EACH_MESSAGE).is_empty() ;
        if DEBUG_LOGGER && !needs_flush {
            warn!(target:"NSLogger", "no need to flush!!") ;
        }
        let mut flush_rx:Option<mpsc::Receiver<bool>> = None ;
        if needs_flush {
            flush_rx = log_message.flush_rx.take() ;
        }

        self.message_sender.send(HandlerMessageType::AddLog(log_message)) ;

        if needs_flush {
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "waiting for message flush") ;
            }
            flush_rx.unwrap().recv() ;
            if DEBUG_LOGGER {
                info!(target:"NSLogger", "message flush ack received") ;
            }
        }
    }
}

impl Drop for Logger {
    fn drop(&mut self) {
        if DEBUG_LOGGER {
            info!(target:"NSLogger", "calling drop for logger instance") ;
        }

        self.message_sender.send(HandlerMessageType::Quit) ;

    }
}

impl log::Log for Logger {
    fn enabled(&self, metadata:&log::LogMetadata) -> bool {
        true
    }

    fn log(&self, record:&log::LogRecord) {
        if !self.enabled(record.metadata()) {
            return ;
        }

        self.logl(Some(Path::new(record.location().file())),
                   Some(record.location().line() as usize),
                   None,
                   Some(Domain::from_str(record.target()).unwrap()),
                   Level::from_log_level(record.level()),
                   &format!("{}", record.args())) ;
    }
}

unsafe impl Sync for Logger {}

unsafe impl Send for Logger {}

<|endoftext|>


<commit_before>use dom::bindings::element;
use dom::bindings::utils;
use dom::bindings::utils::{DOM_OBJECT_SLOT, CacheableWrapper};
use dom::node::{AbstractNode, Text, Comment, Doctype, TextNodeTypeId, CommentNodeTypeId};
use dom::node::{DoctypeNodeTypeId};

use js::jsapi::{JSFreeOp, JSObject, JSContext};
use js::jsapi::bindgen::{JS_GetReservedSlot, JS_SetReservedSlot};
use js::glue::bindgen::{RUST_JSVAL_TO_PRIVATE, RUST_PRIVATE_TO_JSVAL};
use js::rust::{Compartment, jsobj};

extern fn finalize_text(_fop: *JSFreeOp, obj: *JSObject) {
    debug!("text finalize: %?!", obj as uint);
    unsafe {
        let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32);
        let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val));
        let _elem: ~Text = cast::transmute(node.raw_object());
    }
}

extern fn finalize_comment(_fop: *JSFreeOp, obj: *JSObject) {
    debug!("comment finalize: %?!", obj as uint);
    unsafe {
        let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32);
        let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val));
        let _elem: ~Comment = cast::transmute(node.raw_object());
    }
}

extern fn finalize_doctype(_fop: *JSFreeOp, obj: *JSObject) {
    debug!("doctype finalize: %?!", obj as uint);
    unsafe {
        let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32);
        let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val));
        let _elem: ~Doctype = cast::transmute(node.raw_object());
    }
}

pub fn init(compartment: @mut Compartment) {
    let _ = utils::define_empty_prototype(~"CharacterData", Some(~"Node"), compartment);

    let _ = utils::define_empty_prototype(~"TextPrototype",
                                          Some(~"CharacterData"),
                                          compartment);
    let _ = utils::define_empty_prototype(~"CommentPrototype",
                                          Some(~"CharacterData"),
                                          compartment);
    let _ = utils::define_empty_prototype(~"DocumentTypePrototype",
                                          Some(~"Node"),
                                          compartment);

    compartment.register_class(utils::instance_jsclass(~"Text",
                                                       finalize_text,
                                                       element::trace));
    compartment.register_class(utils::instance_jsclass(~"Comment",
                                                       finalize_comment,
                                                       element::trace));
    compartment.register_class(utils::instance_jsclass(~"DocumentType",
                                                       finalize_doctype,
                                                       element::trace));

}

pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj {
    let (proto, instance) = match node.type_id() {
      TextNodeTypeId => (~"TextPrototype", ~"Text"),
      CommentNodeTypeId => (~"CommentPrototype", ~"Comment"),
      DoctypeNodeTypeId => (~"DocumentTypePrototype", ~"DocumentType"),
      _ => fail!(~"text::create only handles textual nodes")
    };

    //XXXjdm the parent should probably be the node parent instead of the global
    //TODO error checking
    let compartment = utils::get_compartment(cx);
    let obj = result::unwrap(compartment.new_object_with_proto(instance,
                                                               proto,
                                                               compartment.global_obj.ptr));

    let cache = node.get_wrappercache();
    assert!(cache.get_wrapper().is_null());
    cache.set_wrapper(obj.ptr);

    unsafe {
        let raw_ptr = ptr::to_unsafe_ptr(node) as *libc::c_void;
        JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT as u32, RUST_PRIVATE_TO_JSVAL(raw_ptr));
    }

    return obj;
}<commit_msg>Make text nodes use same private slot representation as DOM elements.<commit_after>use dom::bindings::element;
use dom::bindings::node::unwrap;
use dom::bindings::utils;
use dom::bindings::utils::{DOM_OBJECT_SLOT, CacheableWrapper};
use dom::node::{AbstractNode, Text, Comment, Doctype, TextNodeTypeId, CommentNodeTypeId};
use dom::node::{DoctypeNodeTypeId};

use js::jsapi::{JSFreeOp, JSObject, JSContext};
use js::jsapi::bindgen::{JS_SetReservedSlot};
use js::glue::bindgen::{RUST_PRIVATE_TO_JSVAL};
use js::rust::{Compartment, jsobj};

extern fn finalize_text(_fop: *JSFreeOp, obj: *JSObject) {
    debug!("text finalize: %?!", obj as uint);
    unsafe {
        let node: AbstractNode = unwrap(obj);
        let _elem: ~Text = cast::transmute(node.raw_object());
    }
}

extern fn finalize_comment(_fop: *JSFreeOp, obj: *JSObject) {
    debug!("comment finalize: %?!", obj as uint);
    unsafe {
        let node: AbstractNode = unwrap(obj);
        let _elem: ~Comment = cast::transmute(node.raw_object());
    }
}

extern fn finalize_doctype(_fop: *JSFreeOp, obj: *JSObject) {
    debug!("doctype finalize: %?!", obj as uint);
    unsafe {
        let node: AbstractNode = unwrap(obj);
        let _elem: ~Doctype = cast::transmute(node.raw_object());
    }
}

pub fn init(compartment: @mut Compartment) {
    let _ = utils::define_empty_prototype(~"CharacterData", Some(~"Node"), compartment);

    let _ = utils::define_empty_prototype(~"TextPrototype",
                                          Some(~"CharacterData"),
                                          compartment);
    let _ = utils::define_empty_prototype(~"CommentPrototype",
                                          Some(~"CharacterData"),
                                          compartment);
    let _ = utils::define_empty_prototype(~"DocumentTypePrototype",
                                          Some(~"Node"),
                                          compartment);

    compartment.register_class(utils::instance_jsclass(~"Text",
                                                       finalize_text,
                                                       element::trace));
    compartment.register_class(utils::instance_jsclass(~"Comment",
                                                       finalize_comment,
                                                       element::trace));
    compartment.register_class(utils::instance_jsclass(~"DocumentType",
                                                       finalize_doctype,
                                                       element::trace));

}

pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj {
    let (proto, instance) = match node.type_id() {
      TextNodeTypeId => (~"TextPrototype", ~"Text"),
      CommentNodeTypeId => (~"CommentPrototype", ~"Comment"),
      DoctypeNodeTypeId => (~"DocumentTypePrototype", ~"DocumentType"),
      _ => fail!(~"text::create only handles textual nodes")
    };

    //XXXjdm the parent should probably be the node parent instead of the global
    //TODO error checking
    let compartment = utils::get_compartment(cx);
    let obj = result::unwrap(compartment.new_object_with_proto(instance,
                                                               proto,
                                                               compartment.global_obj.ptr));

    let cache = node.get_wrappercache();
    assert!(cache.get_wrapper().is_null());
    cache.set_wrapper(obj.ptr);

    unsafe {
        let raw_ptr = node.raw_object() as *libc::c_void;
        JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT as u32, RUST_PRIVATE_TO_JSVAL(raw_ptr));
    }

    return obj;
}<|endoftext|>


<commit_before>use ansi_term::Style;
use fs::File;
use info::filetype::FileExtensions;
use output::file_name::FileStyle;

pub trait FileIcon {
    fn icon_file(&self, file: &File) -> Option<char>;
}

pub enum Icons {
    Audio,
    Image,
    Video,
}

impl Icons {
    pub fn value(&self) -> char {
        match *self {
            Icons::Audio => '\u{f001}',
            Icons::Image => '\u{f1c5}',
            Icons::Video => '\u{f03d}',
        }
    }
}

pub fn painted_icon(file: &File, style: &FileStyle) -> String {
    let file_icon = icon(&file).to_string();
    let painted = style.exts
            .colour_file(&file)
            .map_or(file_icon.to_string(), |c| {
                // Remove underline from icon
                if c.is_underline {
                    match c.foreground {
                        Some(color) => Style::from(color).paint(file_icon).to_string(),
                        None => Style::default().paint(file_icon).to_string(),
                    }
                } else {
                    c.paint(file_icon).to_string()
                }
            });
    format!("{} ", painted)
}

fn icon(file: &File) -> char {
    let extensions = Box::new(FileExtensions);
    if file.is_directory() { '\u{f115}' }
    else if let Some(icon) = extensions.icon_file(file) { icon }
    else {
        if let Some(ext) = file.ext.as_ref() {
            match ext.as_str() {
                "ai" => '\u{e7b4}',
                "android" => '\u{e70e}',
                "apple" => '\u{f179}',
                "avro" => '\u{e60b}',
                "c" => '\u{e61e}',
                "clj" => '\u{e768}',
                "coffee" => '\u{f0f4}',
                "conf" => '\u{e615}',
                "cpp" => '\u{e61d}',
                "css" => '\u{e749}',
                "d" => '\u{e7af}',
                "dart" => '\u{e798}',
                "db" => '\u{f1c0}',
                "diff" => '\u{f440}',
                "doc" => '\u{f1c2}',
                "ebook" => '\u{e28b}',
                "env" => '\u{f462}',
                "epub" => '\u{e28a}',
                "erl" => '\u{e7b1}',
                "font" => '\u{f031}',
                "gform" => '\u{f298}',
                "git" => '\u{f1d3}',
                "go" => '\u{e626}',
                "hs" => '\u{e777}',
                "html" => '\u{f13b}',
                "iml" => '\u{e7b5}',
                "java" => '\u{e204}',
                "js" => '\u{e74e}',
                "json" => '\u{e60b}',
                "jsx" => '\u{e7ba}',
                "less" => '\u{e758}',
                "log" => '\u{f18d}',
                "lua" => '\u{e620}',
                "md" => '\u{f48a}',
                "mustache" => '\u{e60f}',
                "npmignore" => '\u{e71e}',
                "pdf" => '\u{f1c1}',
                "php" => '\u{e73d}',
                "pl" => '\u{e769}',
                "ppt" => '\u{f1c4}',
                "psd" => '\u{e7b8}',
                "py" => '\u{e606}',
                "r" => '\u{f25d}',
                "rb" => '\u{e21e}',
                "rdb" => '\u{e76d}',
                "rs" => '\u{e7a8}',
                "rss" => '\u{f09e}',
                "rubydoc" => '\u{e73b}',
                "sass" => '\u{e603}',
                "scala" => '\u{e737}',
                "shell" => '\u{f489}',
                "sqlite3" => '\u{e7c4}',
                "styl" => '\u{e600}',
                "tex" => '\u{e600}',
                "ts" => '\u{e628}',
                "twig" => '\u{e61c}',
                "txt" => '\u{f15c}',
                "video" => '\u{f03d}',
                "vim" => '\u{e62b}',
                "xls" => '\u{f1c3}',
                "xml" => '\u{e619}',
                "yml" => '\u{f481}',
                "zip" => '\u{f410}',
                _ => '\u{f15b}'
            }
        } else {
            '\u{f15b}'
        }
    }
}
<commit_msg>Add better icons spacing<commit_after>use ansi_term::Style;
use fs::File;
use info::filetype::FileExtensions;
use output::file_name::FileStyle;

pub trait FileIcon {
    fn icon_file(&self, file: &File) -> Option<char>;
}

pub enum Icons {
    Audio,
    Image,
    Video,
}

impl Icons {
    pub fn value(&self) -> char {
        match *self {
            Icons::Audio => '\u{f001}',
            Icons::Image => '\u{f1c5}',
            Icons::Video => '\u{f03d}',
        }
    }
}

pub fn painted_icon(file: &File, style: &FileStyle) -> String {
    let file_icon = icon(&file).to_string();
    let painted = style.exts
            .colour_file(&file)
            .map_or(file_icon.to_string(), |c| {
                // Remove underline from icon
                if c.is_underline {
                    match c.foreground {
                        Some(color) => Style::from(color).paint(file_icon).to_string(),
                        None => Style::default().paint(file_icon).to_string(),
                    }
                } else {
                    c.paint(file_icon).to_string()
                }
            });
    format!("{}  ", painted)
}

fn icon(file: &File) -> char {
    let extensions = Box::new(FileExtensions);
    if file.is_directory() { '\u{f115}' }
    else if let Some(icon) = extensions.icon_file(file) { icon }
    else {
        if let Some(ext) = file.ext.as_ref() {
            match ext.as_str() {
                "ai" => '\u{e7b4}',
                "android" => '\u{e70e}',
                "apple" => '\u{f179}',
                "avro" => '\u{e60b}',
                "c" => '\u{e61e}',
                "clj" => '\u{e768}',
                "coffee" => '\u{f0f4}',
                "conf" => '\u{e615}',
                "cpp" => '\u{e61d}',
                "css" => '\u{e749}',
                "d" => '\u{e7af}',
                "dart" => '\u{e798}',
                "db" => '\u{f1c0}',
                "diff" => '\u{f440}',
                "doc" => '\u{f1c2}',
                "ebook" => '\u{e28b}',
                "env" => '\u{f462}',
                "epub" => '\u{e28a}',
                "erl" => '\u{e7b1}',
                "font" => '\u{f031}',
                "gform" => '\u{f298}',
                "git" => '\u{f1d3}',
                "go" => '\u{e626}',
                "hs" => '\u{e777}',
                "html" => '\u{f13b}',
                "iml" => '\u{e7b5}',
                "java" => '\u{e204}',
                "js" => '\u{e74e}',
                "json" => '\u{e60b}',
                "jsx" => '\u{e7ba}',
                "less" => '\u{e758}',
                "log" => '\u{f18d}',
                "lua" => '\u{e620}',
                "md" => '\u{f48a}',
                "mustache" => '\u{e60f}',
                "npmignore" => '\u{e71e}',
                "pdf" => '\u{f1c1}',
                "php" => '\u{e73d}',
                "pl" => '\u{e769}',
                "ppt" => '\u{f1c4}',
                "psd" => '\u{e7b8}',
                "py" => '\u{e606}',
                "r" => '\u{f25d}',
                "rb" => '\u{e21e}',
                "rdb" => '\u{e76d}',
                "rs" => '\u{e7a8}',
                "rss" => '\u{f09e}',
                "rubydoc" => '\u{e73b}',
                "sass" => '\u{e603}',
                "scala" => '\u{e737}',
                "shell" => '\u{f489}',
                "sqlite3" => '\u{e7c4}',
                "styl" => '\u{e600}',
                "tex" => '\u{e600}',
                "ts" => '\u{e628}',
                "twig" => '\u{e61c}',
                "txt" => '\u{f15c}',
                "video" => '\u{f03d}',
                "vim" => '\u{e62b}',
                "xls" => '\u{f1c3}',
                "xml" => '\u{e619}',
                "yml" => '\u{f481}',
                "zip" => '\u{f410}',
                _ => '\u{f15b}'
            }
        } else {
            '\u{f15b}'
        }
    }
}
<|endoftext|>