<commit_before>//! This crate provides functionality to execute a daily schedule for home
//! automation purposes. It provided a variety to schedule tasks in a day.
//!
//! The purpose of this crate is only calculation of execution times, but
//! doesn't perform the actual execution of the schedule loop.
//!
//! It doesn't rely on system time on purpose to allow easier testing and
//! qualification, without considering the real-time aspects. All
//! calculated timestamps are UTC based and any local-time conversion are
//! based on the zoneinfo crate.
extern crate rand;
extern crate time;
extern crate zoneinfo;

use time::{Timespec, Duration, at_utc};
use std::collections::BTreeMap;
use std::rc::Rc;
use rand::{Rng, thread_rng};
use zoneinfo::{ZoneInfo, ZoneInfoElement};
use std::io::Result;
use std::cell::RefCell;

/// Represents abstract action identifier
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Context(pub usize);

/// Represents a fixed moment in a day
#[derive(Debug)]
pub enum Moment {
    /// Duration is offset in time based on local midnight
    LocalTime(Duration),
    /// Duration is offset in time based on UTC midnight
    UtcTime(Duration)
}

/// Local time definition
enum LocalTimeState {
    /// Zone-info state is not loaded yet
    Unknown,
    /// Current zone-info specifies no future daylight saving time change is expected
    NoChangePending(ZoneInfoElement),
    /// Daylight saving time change is pending
    ChangePending(Timespec, // transition time
                  ZoneInfoElement, // zone information before transition time
                  ZoneInfoElement) // zone information at and after transition time
}

impl Moment {
    /// Create a moment in a day
    pub fn new(h:u8, m:u8, s:u8) -> Moment {
        Moment::LocalTime(
            Duration::hours(h as i64) +
            Duration::minutes(m as i64) +
            Duration::seconds(s as i64))
    }

    /// Create a moment in a day based on Timespec
    pub fn new_from_timespec(ts: Timespec) -> Moment {
        let mut tm_utc = at_utc(ts);

        tm_utc.tm_hour = 0;
        tm_utc.tm_min = 0;
        tm_utc.tm_sec = 0;
        tm_utc.tm_nsec = 0;

        Moment::UtcTime(ts - tm_utc.to_timespec())
    }

    /// Convert schedule time to actual time stamp
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Timespec {
        match self {
            // timestamp is simply a reference to UTC so just add the offset
            &Moment::UtcTime(offset) => ut_midnight_reference + offset,
            // timestamp is a reference to the moment in a day
            &Moment::LocalTime(offset) => {
                let ut_offset = match *localtime {
                    LocalTimeState::NoChangePending(ref info) => info.ut_offset,
                    LocalTimeState::ChangePending(transition_time, ref before, ref after) => {
                        if ut_midnight_reference + offset - before.ut_offset < transition_time {
                            before.ut_offset
                        }
                        else {
                            after.ut_offset
                        }
                    }
                    _ => unreachable!()
                };

                ut_midnight_reference + offset - ut_offset
            }
        }
    }
}

// FIXME: remove this
impl std::fmt::Display for Moment {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        let duration = match self {
            &Moment::UtcTime(d) => d,
            &Moment::LocalTime(d) => d,
        };
        try!(write!(fmt, "{:02}:{:02}:{:02}", duration.num_hours(), duration.num_minutes() % 60, duration.num_seconds() % 60));
        if let &Moment::UtcTime(_) = self {
            try!(write!(fmt, " (UTC)"));
        }
        Ok(())
    }
}

/// Weekday filter specifier
pub enum Filter {
    /// Always execute  event
    Always,
    /// Only execute Monday till Friday
    MonToFri,
    /// Only execute Saturday and Sunday
    Weekend // FIXME: more abstractions?
}

impl Filter {
    /// Indicate whether given time is valid to be scheduled based on weekday
    fn filter_days(&self, time: Timespec, zoneinfo: &ZoneInfoElement) -> bool {
        // make sure reference time is in the same weekday in UTC as it would be
        // in local time.
        let ref_time = time + zoneinfo.ut_offset;
        let wday = at_utc(ref_time).tm_wday;
        let weekend = wday == 0 || wday == 6; // 0 = Sunday, 6 = Saturday

        match self {
            &Filter::Always => true,
            &Filter::MonToFri => !weekend,
            &Filter::Weekend => weekend
        }
    }

    /// Indicate whether given time is valid to be scheduled based on weekday
    fn day_scheduled(&self, time: Timespec, localtime: &LocalTimeState) -> bool {
        match self {
            &Filter::Always => true,
            &Filter::MonToFri|&Filter::Weekend => {
                let zoneinfo = match localtime {
                    &LocalTimeState::NoChangePending(ref zoneinfo) => zoneinfo,
                    &LocalTimeState::ChangePending(ref transition, ref z1, ref z2) => {
                        if time < *transition {
                            z1
                        }
                        else {
                            z2
                        }
                    }
                    _ => unreachable!()
                };

                self.filter_days(time, zoneinfo)
            },
        }
    }
}

/// Represent a (abstract) moment in a day
pub enum DailyEvent<'a> {
    /// A fixed moment in a day
    Fixed(Filter, Moment),
    /// A random moment between two given fixed moments
    Fuzzy(Filter, Moment, Moment),
    /// A externally provided moment in time + variance
    ByClosure(Filter, &'a Fn(Timespec) -> Moment, Duration)
}

// FIXME: remove this in time
impl<'a> std::fmt::Display for DailyEvent<'a> {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            &DailyEvent::Fixed(_, ref t) => write!(fmt, "{}", t),
            &DailyEvent::Fuzzy(_, ref b, ref a) => write!(fmt, "{} ~ {}", b, a),
            &DailyEvent::ByClosure(_, _, ref variance) =>
                write!(fmt, "dynamic ~{}s", variance.num_seconds()),
        }
    }
}

/// Represents a moment and an specific action in a day
struct Event<'a, H: Handler + 'a> {
    /// A moment in a day
    moment: DailyEvent<'a>,
    /// Reference to a action handler
    action: &'a RefCell<H>,
    /// Externally provided reference for the implementor
    context: Context
}

impl<'a, H: Handler + 'a> Event<'a, H> {
    /// Determine time-stamp for event
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Option<Timespec> {
        let ts = match self.moment {
            DailyEvent::Fixed(_, ref moment) =>
                moment.create_timestamp(ut_midnight_reference, localtime),
            DailyEvent::Fuzzy(_, ref m1, ref m2) => {
                // pick a time between both given moment
                let mut rng = rand::thread_rng();
                let t1 = m1.create_timestamp(ut_midnight_reference, localtime);
                let t2 = m2.create_timestamp(ut_midnight_reference, localtime);
                let t_start = if t1 >= t2 {t2} else {t1};
                let t_end = if t1 >= t2 {t1} else {t2};
                let duration = t_end - t_start;
                if duration > Duration::seconds(0) {
                    t_start + Duration::seconds(rng.gen_range(0, duration.num_seconds()))
                }
                else {
                    t_start
                }
            }
            DailyEvent::ByClosure(_, ref func, ref variance) => {
                let moment = func(ut_midnight_reference);
                // generate a offset based on variance compared to the generated moment
                let mut rng = rand::thread_rng();
                let offset = if *variance > Duration::seconds(0) {
                    rng.gen_range(0, variance.num_seconds())
                }
                else {
                    0
                };
                let offset = Duration::seconds(variance.num_seconds() / 2 - offset);
                moment.create_timestamp(ut_midnight_reference, localtime) + offset
            }
        };
        let do_schedule = match self.moment {
            DailyEvent::Fixed(ref w, _) |
            DailyEvent::Fuzzy(ref w, _, _) |
            DailyEvent::ByClosure(ref w, _, _) => w.day_scheduled(ts, localtime)
        };

        if do_schedule {
            Some(ts)
        }
        else {
            None
        }
    }
}

/// Trait to be implemented by the event handler
pub trait Handler {
    /// Perform a action (in a day)
    fn kick(&mut self, timestamp: &Timespec, event: &DailyEvent, kick: &Context);
}

/// Calculates and executes scheduled event every day
pub struct Schedule<'a, H: Handler + 'a> {
    // List of (abstract) moments in a day
    events: Vec<Rc<Event<'a, H>>>,

    // Time zone related information
    zoneinfo: ZoneInfo,

    // Next zone change
    localtime: LocalTimeState,

    // Tree of actual scheduled moments and reference to the abstract moment in a day
    schedule: BTreeMap<Timespec, Rc<Event<'a, H>>>
}

impl<'a, H: Handler + 'a> Schedule<'a, H> {
    /// Create a (empty) list of scheduled daily events
    pub fn new() -> Result<Schedule<'a, H>> {
        Ok(Schedule {
            events: vec![],
            zoneinfo: try!(ZoneInfo::get_local_zoneinfo()),
            localtime: LocalTimeState::Unknown,
            schedule: BTreeMap::new()
        })
    }

    /// Add a (abstract) moment and action in a day
    pub fn add_event(&mut self,
                     moment: DailyEvent<'a>,
                     action: &'a RefCell<H>,
                     context: Context) {
        self.events.push(Rc::new(Event {
            moment: moment,
            action: action,
            context: context
        }));
    }

    /// Determine next zone info state
    fn new_change_state(&self, timestamp: Timespec) -> LocalTimeState {
        // yes, a unwrap, since a serious problem be present when no zone-info could be retrieved
        let actual = self.zoneinfo.get_actual_zoneinfo(timestamp).unwrap();
        match self.zoneinfo.get_next_transition_time(timestamp) {
            Some((next_change, next)) =>
                LocalTimeState::ChangePending(next_change, actual, next),
            None => LocalTimeState::NoChangePending(actual)
        }
    }

    /// Update the schedule for 24 hours (only use with 24 hour incrementing timestamps,
    /// preferably every day)
    pub fn update_schedule(&mut self, ut_midnight_reference: Timespec) {
        match self.localtime {
            LocalTimeState::Unknown =>
                self.localtime = self.new_change_state(ut_midnight_reference),
            LocalTimeState::ChangePending(time, _, _) => {
                if time <= ut_midnight_reference {
                    self.localtime = self.new_change_state(ut_midnight_reference);
                }
            },
            _ => {}
        }

        for event in &self.events {
            let timestamp = event.create_timestamp(ut_midnight_reference, &self.localtime);
            if let Some(timestamp) = timestamp {
                self.schedule.insert(timestamp, event.clone());
            }
        }
    }

    /// Consume schedule until provided moment `now` and kick last or current event and returns next event time
    pub fn kick_event(&mut self, now: Timespec) -> Option<Timespec> {
        let past_events: Vec<Timespec> = self.schedule.keys().filter(|&k| *k <= now).cloned().collect();

        // kick the current event...
        if let Some(timestamp) = past_events.last() {
            if let Some(schedule_event) = self.schedule.get(timestamp) {
                schedule_event.action.borrow_mut().kick(&timestamp, &schedule_event.moment, &schedule_event.context);
            }
        }

        // ...and consume that and prior events
        for past_event in past_events {
            self.schedule.remove(&past_event);
        }

        self.schedule.keys().cloned().nth(0)
    }

    /// Peek when next event will happen
    pub fn peek_event(&self) -> Option<Timespec> {
        self.schedule.keys().cloned().nth(0)
    }

    // TODO remove
    pub fn print_keys(&self) {
        for k in self.schedule.keys() {
            println!("{} {}", at_utc(*k).rfc822(), self.schedule.get(k).unwrap().moment);
        }
    }
}
<commit_msg>Fix zoneinfo API change<commit_after>//! This crate provides functionality to execute a daily schedule for home
//! automation purposes. It provided a variety to schedule tasks in a day.
//!
//! The purpose of this crate is only calculation of execution times, but
//! doesn't perform the actual execution of the schedule loop.
//!
//! It doesn't rely on system time on purpose to allow easier testing and
//! qualification, without considering the real-time aspects. All
//! calculated timestamps are UTC based and any local-time conversion are
//! based on the zoneinfo crate.
extern crate rand;
extern crate time;
extern crate zoneinfo;

use time::{Timespec, Duration, at_utc};
use std::collections::BTreeMap;
use std::rc::Rc;
use rand::{Rng, thread_rng};
use zoneinfo::{ZoneInfo, ZoneInfoElement};
use std::io::Result;
use std::cell::RefCell;

/// Represents abstract action identifier
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Context(pub usize);

/// Represents a fixed moment in a day
#[derive(Debug)]
pub enum Moment {
    /// Duration is offset in time based on local midnight
    LocalTime(Duration),
    /// Duration is offset in time based on UTC midnight
    UtcTime(Duration)
}

/// Local time definition
enum LocalTimeState {
    /// Zone-info state is not loaded yet
    Unknown,
    /// Current zone-info specifies no future daylight saving time change is expected
    NoChangePending(ZoneInfoElement),
    /// Daylight saving time change is pending
    ChangePending(Timespec, // transition time
                  ZoneInfoElement, // zone information before transition time
                  ZoneInfoElement) // zone information at and after transition time
}

impl Moment {
    /// Create a moment in a day
    pub fn new(h:u8, m:u8, s:u8) -> Moment {
        Moment::LocalTime(
            Duration::hours(h as i64) +
            Duration::minutes(m as i64) +
            Duration::seconds(s as i64))
    }

    /// Create a moment in a day based on Timespec
    pub fn new_from_timespec(ts: Timespec) -> Moment {
        let mut tm_utc = at_utc(ts);

        tm_utc.tm_hour = 0;
        tm_utc.tm_min = 0;
        tm_utc.tm_sec = 0;
        tm_utc.tm_nsec = 0;

        Moment::UtcTime(ts - tm_utc.to_timespec())
    }

    /// Convert schedule time to actual time stamp
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Timespec {
        match self {
            // timestamp is simply a reference to UTC so just add the offset
            &Moment::UtcTime(offset) => ut_midnight_reference + offset,
            // timestamp is a reference to the moment in a day
            &Moment::LocalTime(offset) => {
                let pre_localtime_cor = ut_midnight_reference + offset;

                let ut_offset = match *localtime {
                    LocalTimeState::NoChangePending(ref info) => info.ut_offset,
                    LocalTimeState::ChangePending(transition_time, ref before, ref after) => {
                        let reftime = Timespec::new(pre_localtime_cor.sec - before.ut_offset as i64,
                                                    pre_localtime_cor.nsec);
                        if reftime < transition_time {
                            before.ut_offset
                        } else {
                            after.ut_offset
                        }
                    }
                    _ => unreachable!()
                };

                Timespec::new(pre_localtime_cor.sec - ut_offset as i64, pre_localtime_cor.nsec)
            }
        }
    }
}

// FIXME: remove this
impl std::fmt::Display for Moment {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        let duration = match self {
            &Moment::UtcTime(d) => d,
            &Moment::LocalTime(d) => d,
        };
        try!(write!(fmt, "{:02}:{:02}:{:02}", duration.num_hours(), duration.num_minutes() % 60, duration.num_seconds() % 60));
        if let &Moment::UtcTime(_) = self {
            try!(write!(fmt, " (UTC)"));
        }
        Ok(())
    }
}

/// Weekday filter specifier
pub enum Filter {
    /// Always execute  event
    Always,
    /// Only execute Monday till Friday
    MonToFri,
    /// Only execute Saturday and Sunday
    Weekend // FIXME: more abstractions?
}

impl Filter {
    /// Indicate whether given time is valid to be scheduled based on weekday
    fn filter_days(&self, time: Timespec, zoneinfo: &ZoneInfoElement) -> bool {
        // make sure reference time is in the same weekday in UTC as it would be
        // in local time.
        let ref_time = Timespec::new(time.sec + zoneinfo.ut_offset as i64, time.nsec);
        let wday = at_utc(ref_time).tm_wday;
        let weekend = wday == 0 || wday == 6; // 0 = Sunday, 6 = Saturday

        match self {
            &Filter::Always => true,
            &Filter::MonToFri => !weekend,
            &Filter::Weekend => weekend
        }
    }

    /// Indicate whether given time is valid to be scheduled based on weekday
    fn day_scheduled(&self, time: Timespec, localtime: &LocalTimeState) -> bool {
        match self {
            &Filter::Always => true,
            &Filter::MonToFri|&Filter::Weekend => {
                let zoneinfo = match localtime {
                    &LocalTimeState::NoChangePending(ref zoneinfo) => zoneinfo,
                    &LocalTimeState::ChangePending(ref transition, ref z1, ref z2) => {
                        if time < *transition {
                            z1
                        } else {
                            z2
                        }
                    }
                    _ => unreachable!()
                };

                self.filter_days(time, zoneinfo)
            },
        }
    }
}

/// Represent a (abstract) moment in a day
pub enum DailyEvent<'a> {
    /// A fixed moment in a day
    Fixed(Filter, Moment),
    /// A random moment between two given fixed moments
    Fuzzy(Filter, Moment, Moment),
    /// A externally provided moment in time + variance
    ByClosure(Filter, &'a Fn(Timespec) -> Moment, Duration)
}

// FIXME: remove this in time
impl<'a> std::fmt::Display for DailyEvent<'a> {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            &DailyEvent::Fixed(_, ref t) => write!(fmt, "{}", t),
            &DailyEvent::Fuzzy(_, ref b, ref a) => write!(fmt, "{} ~ {}", b, a),
            &DailyEvent::ByClosure(_, _, ref variance) =>
                write!(fmt, "dynamic ~{}s", variance.num_seconds()),
        }
    }
}

/// Represents a moment and an specific action in a day
struct Event<'a, H: Handler + 'a> {
    /// A moment in a day
    moment: DailyEvent<'a>,
    /// Reference to a action handler
    action: &'a RefCell<H>,
    /// Externally provided reference for the implementor
    context: Context
}

impl<'a, H: Handler + 'a> Event<'a, H> {
    /// Determine time-stamp for event
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Option<Timespec> {
        let ts = match self.moment {
            DailyEvent::Fixed(_, ref moment) =>
                moment.create_timestamp(ut_midnight_reference, localtime),
            DailyEvent::Fuzzy(_, ref m1, ref m2) => {
                // pick a time between both given moment
                let mut rng = rand::thread_rng();
                let t1 = m1.create_timestamp(ut_midnight_reference, localtime);
                let t2 = m2.create_timestamp(ut_midnight_reference, localtime);
                let t_start = if t1 >= t2 {t2} else {t1};
                let t_end = if t1 >= t2 {t1} else {t2};
                let duration = t_end - t_start;
                if duration > Duration::seconds(0) {
                    t_start + Duration::seconds(rng.gen_range(0, duration.num_seconds()))
                } else {
                    t_start
                }
            }
            DailyEvent::ByClosure(_, ref func, ref variance) => {
                let moment = func(ut_midnight_reference);
                // generate a offset based on variance compared to the generated moment
                let mut rng = rand::thread_rng();
                let offset = if *variance > Duration::seconds(0) {
                    rng.gen_range(0, variance.num_seconds())
                } else {
                    0
                };
                let offset = Duration::seconds(variance.num_seconds() / 2 - offset);
                moment.create_timestamp(ut_midnight_reference, localtime) + offset
            }
        };
        let do_schedule = match self.moment {
            DailyEvent::Fixed(ref w, _) |
            DailyEvent::Fuzzy(ref w, _, _) |
            DailyEvent::ByClosure(ref w, _, _) => w.day_scheduled(ts, localtime)
        };

        if do_schedule {
            Some(ts)
        } else {
            None
        }
    }
}

/// Trait to be implemented by the event handler
pub trait Handler {
    /// Perform a action (in a day)
    fn kick(&mut self, timestamp: &Timespec, event: &DailyEvent, kick: &Context);
}

/// Calculates and executes scheduled events every day
pub struct Schedule<'a, H: Handler + 'a> {
    // List of (abstract) moments in a day
    events: Vec<Rc<Event<'a, H>>>,

    // Time zone related information
    zoneinfo: ZoneInfo,

    // Next zone change
    localtime: LocalTimeState,

    // Tree of actual scheduled moments and reference to the abstract moment in a day
    schedule: BTreeMap<Timespec, Rc<Event<'a, H>>>
}

impl<'a, H: Handler + 'a> Schedule<'a, H> {
    /// Create a (empty) list of scheduled daily events
    pub fn new() -> Result<Schedule<'a, H>> {
        Ok(Schedule {
            events: vec![],
            zoneinfo: try!(ZoneInfo::get_local_zoneinfo()),
            localtime: LocalTimeState::Unknown,
            schedule: BTreeMap::new()
        })
    }

    /// Add a (abstract) moment and action in a day
    pub fn add_event(&mut self,
                     moment: DailyEvent<'a>,
                     action: &'a RefCell<H>,
                     context: Context) {
        self.events.push(Rc::new(Event {
            moment: moment,
            action: action,
            context: context
        }));
    }

    /// Determine next zone info state
    fn new_change_state(&self, timestamp: Timespec) -> LocalTimeState {
        // yes, a unwrap, since a serious problem be present when no zone-info could be retrieved
        let actual = self.zoneinfo.get_actual_zoneinfo(timestamp).unwrap();
        match self.zoneinfo.get_next_transition_time(timestamp) {
            Some((next_change, next)) =>
                LocalTimeState::ChangePending(next_change, actual, next),
            None => LocalTimeState::NoChangePending(actual)
        }
    }

    /// Update the schedule for 24 hours (only use with 24 hour incrementing timestamps,
    /// preferably every day)
    pub fn update_schedule(&mut self, ut_midnight_reference: Timespec) {
        match self.localtime {
            LocalTimeState::Unknown =>
                self.localtime = self.new_change_state(ut_midnight_reference),
            LocalTimeState::ChangePending(time, _, _) => {
                if time <= ut_midnight_reference {
                    self.localtime = self.new_change_state(ut_midnight_reference);
                }
            },
            _ => {}
        }

        for event in &self.events {
            let timestamp = event.create_timestamp(ut_midnight_reference, &self.localtime);
            if let Some(timestamp) = timestamp {
                self.schedule.insert(timestamp, event.clone());
            }
        }
    }

    /// Consume schedule until provided moment `now` and kick last or current event and returns next event time
    pub fn kick_event(&mut self, now: Timespec) -> Option<Timespec> {
        let past_events: Vec<Timespec> = self.schedule.keys().filter(|&k| *k <= now).cloned().collect();

        // kick the current event...
        if let Some(timestamp) = past_events.last() {
            if let Some(schedule_event) = self.schedule.get(timestamp) {
                schedule_event.action.borrow_mut().kick(&timestamp, &schedule_event.moment, &schedule_event.context);
            }
        }

        // ...and consume that and prior events
        for past_event in past_events {
            self.schedule.remove(&past_event);
        }

        self.schedule.keys().cloned().nth(0)
    }

    /// Peek when next event will happen
    pub fn peek_event(&self) -> Option<Timespec> {
        self.schedule.keys().cloned().nth(0)
    }

    // TODO remove
    pub fn print_keys(&self) {
        for k in self.schedule.keys() {
            println!("{} {}", at_utc(*k).rfc822(), self.schedule.get(k).unwrap().moment);
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::{Error, ErrorKind, Read, Write};
use std::thread;
use std::sync::Arc;
use std::net::{TcpListener, TcpStream};
use unix_socket::{UnixListener, UnixStream};

enum VarlinkListener {
    TCP(TcpListener),
    UNIX(UnixListener),
}

enum VarlinkStream {
    TCP(TcpStream),
    UNIX(UnixStream),
}

impl<'a> VarlinkStream {
    pub fn split(&mut self) -> io::Result<(Box<Read + Send + Sync>, Box<Write + Send + Sync>)> {
        match *self {
            VarlinkStream::TCP(ref mut s) => {
                Ok((Box::new(s.try_clone()?), Box::new(s.try_clone()?)))
            }
            VarlinkStream::UNIX(ref mut s) => {
                Ok((Box::new(s.try_clone()?), Box::new(s.try_clone()?)))
            }
        }
    }
}

impl VarlinkListener {
    pub fn new(address: &str) -> io::Result<Self> {
        if address.starts_with("tcp:") {
            Ok(VarlinkListener::TCP(TcpListener::bind(&address[4..])?))
        } else if address.starts_with("unix:") {
            let mut addr = String::from(&address[5..]);
            if addr.starts_with("@") {
                addr = addr.replacen("@", "\0", 1);
            }
            Ok(VarlinkListener::UNIX(UnixListener::bind(addr)?))
        } else {
            Err(Error::new(ErrorKind::Other, "unknown varlink address"))
        }
    }
    pub fn accept(&self) -> io::Result<VarlinkStream> {
        match self {
            &VarlinkListener::TCP(ref l) => {
                let (mut s, _addr) = l.accept()?;
                Ok(VarlinkStream::TCP(s))
            }
            &VarlinkListener::UNIX(ref l) => {
                let (mut s, _addr) = l.accept()?;
                Ok(VarlinkStream::UNIX(s))
            }
        }
    }
}

pub fn listen(addr: &str, service: Arc<::VarlinkService>) -> io::Result<()> {
    println!("Listening on {}", addr);
    let listener = VarlinkListener::new(addr)?;

    loop {
        let mut stream = listener.accept()?;
        let service = service.clone();
        let _join = thread::spawn(move || -> io::Result<()> {
            let (mut r, mut w) = stream.split()?;
            if let Err(e) = service.handle(&mut r, &mut w) {
                println!("Handle Error: {}", e);
            }
            Ok(())
        });
    }
}
<commit_msg>add exec: activation<commit_after>use std::io;
use std::io::{Error, ErrorKind, Read, Write};
use std::thread;
use std::sync::Arc;
use std::net::{TcpListener, TcpStream};
// FIXME: abstract unix domains sockets still not in std
// FIXME: https://github.com/rust-lang/rust/issues/14194
use unix_socket::{UnixListener, UnixStream};
use std::fs;
use std::env;
use std::os::unix::io::FromRawFd;

enum VarlinkListener {
    TCP(TcpListener),
    UNIX(UnixListener),
}

enum VarlinkStream {
    TCP(TcpStream),
    UNIX(UnixStream),
}

impl<'a> VarlinkStream {
    pub fn split(&mut self) -> io::Result<(Box<Read + Send + Sync>, Box<Write + Send + Sync>)> {
        match *self {
            VarlinkStream::TCP(ref mut s) => {
                Ok((Box::new(s.try_clone()?), Box::new(s.try_clone()?)))
            }
            VarlinkStream::UNIX(ref mut s) => {
                Ok((Box::new(s.try_clone()?), Box::new(s.try_clone()?)))
            }
        }
    }
}

fn activation_listener() -> io::Result<Option<UnixListener>> {
    /*
	FIXME: only working on nightly https://github.com/rust-lang/rust/pull/45059

    let spid = env::var("LISTEN_PID");
    if let Ok(pid) = spid {
        let mypid = std::process::id();
        match pid.parse::<u32>() {
            mypid => {}
            _ => return Ok(None),
        }
    } else {
        return Ok(None);
    }
*/
    let snfds = env::var("LISTEN_FDS");
    if let Ok(nfds) = snfds {
        match nfds.parse::<u32>() {
            Ok(1) => {}
            _ => return Ok(None),
        }
    } else {
        return Ok(None);
    }
    unsafe {
        //syscall.CloseOnExec(3)
        let listener = UnixListener::from_raw_fd(3);
        env::remove_var("LISTEN_PID");
        env::remove_var("LISTEN_FDS");

        Ok(Some(listener))
    }
}

//FIXME: add Drop with shutdown() and unix file removal
impl VarlinkListener {
    pub fn new(address: &str) -> io::Result<Self> {
        if let Some(l) = activation_listener()? {
            return Ok(VarlinkListener::UNIX(l));
        }

        if address.starts_with("tcp:") {
            Ok(VarlinkListener::TCP(TcpListener::bind(&address[4..])?))
        } else if address.starts_with("unix:") {
            let mut addr = String::from(address[5..].split(";").next().unwrap());
            if addr.starts_with("@") {
                addr = addr.replacen("@", "\0", 1);
            } else {
                // ignore error on non-existant file
                let _ = fs::remove_file(addr.clone());
            }
            Ok(VarlinkListener::UNIX(UnixListener::bind(addr)?))
        } else {
            Err(Error::new(ErrorKind::Other, "unknown varlink address"))
        }
    }

    pub fn accept(&self) -> io::Result<VarlinkStream> {
        match self {
            &VarlinkListener::TCP(ref l) => {
                let (mut s, _addr) = l.accept()?;
                Ok(VarlinkStream::TCP(s))
            }
            &VarlinkListener::UNIX(ref l) => {
                let (mut s, _addr) = l.accept()?;
                Ok(VarlinkStream::UNIX(s))
            }
        }
    }
}

pub fn listen(addr: &str, service: Arc<::VarlinkService>) -> io::Result<()> {
    println!("Listening on {}", addr);
    let listener = VarlinkListener::new(addr)?;

    loop {
        let mut stream;
        match listener.accept() {
            Err(e) => match Error::last_os_error().raw_os_error() {
                Some(11) => continue,
                _ => return Err(e),
            },
            Ok(s) => stream = s,
        }
        let service = service.clone();
        let _join = thread::spawn(move || -> io::Result<()> {
            let (mut r, mut w) = stream.split()?;
            if let Err(e) = service.handle(&mut r, &mut w) {
                println!("Handle Error: {}", e);
            }
            Ok(())
        });
    }
}
<|endoftext|>


<commit_before>/// A graph similarity score using neighbor matching according to [this paper][1].
///
/// [1]: http://arxiv.org/abs/1009.5290 "2010, Mladen Nikolic, Measuring Similarity
///      of Graph Nodes by Neighbor Matching"

extern crate nalgebra;
extern crate munkres;

use nalgebra::{DMat, Shape, ApproxEq};
use munkres::{WeightMatrix, solve_assignment};
use std::cmp;
use std::mem;

pub type Idx = u32;

trait Edges {
    /// The number of edges
    fn len(&self) -> usize;

    /// Returns the target node of the nth-edge
    fn nth_edge(&self, n: usize) -> usize;

    /// Returns the nth edge weight
    fn nth_edge_weight(&self, _n: usize) -> f32 {
        panic!();
    }
}

impl<'a> Edges for &'a [Idx] {
    #[inline]
    fn len(&self) -> usize {
        let x: &[Idx] = self;
        x.len()
    }
    #[inline]
    fn nth_edge(&self, n: usize) -> usize {
        self[n] as usize
    }
}

#[inline]
/// Calculates the similarity of two nodes `i` and `j`.
///
/// `n_i` contains the neighborhood of i (either in or out neighbors, not both)
/// `n_j` contains the neighborhood of j (either in or out neighbors, not both)
/// `x`   the similarity matrix.
fn s_next<T: Edges>(n_i: T, n_j: T, x: &DMat<f32>) -> f32 {
    let max_deg = cmp::max(n_i.len(), n_j.len());
    let min_deg = cmp::min(n_i.len(), n_j.len());

    if min_deg == 0 {
        // in the paper, 0/0 is defined as 1.0
        return 1.0;
    }

    assert!(min_deg > 0 && max_deg > 0);

    // map indicies from 0..min(degree) to the node indices
    let mapidx = |(a, b)| (n_i.nth_edge(a), n_j.nth_edge(b));

    let mut w = WeightMatrix::from_fn(min_deg, |ab| x[mapidx(ab)]);

    let assignment = solve_assignment(&mut w);
    assert!(assignment.len() == min_deg);

    let sum: f32 = assignment.iter().fold(0.0, |acc, &ab| acc + x[mapidx(ab)]);

    return sum / max_deg as f32;
}

#[inline]
/// Calculates x[k+1]
///
/// `node_color_scale((i,j))`: If two nodes `i` (of graph A) and `j` (of graph B)
/// are of different color, this can be set to return 0.0. Alternatively a
/// node-color distance (within 0...1) could be used to penalize.
fn next_x<F>(x: &DMat<f32>,
             new_x: &mut DMat<f32>,
             in_a: &[Vec<Idx>],
             in_b: &[Vec<Idx>],
             out_a: &[Vec<Idx>],
             out_b: &[Vec<Idx>],
             node_color_scale: F)
    where F: Fn((usize, usize)) -> f32
{
    let shape = x.shape();
    assert!(shape == new_x.shape());

    for i in 0..shape.0 {
        for j in 0..shape.1 {
            let in_i: &[Idx] = &in_a[i];
            let in_j: &[Idx] = &in_b[j];
            let out_i: &[Idx] = &out_a[i];
            let out_j: &[Idx] = &out_b[j];
            new_x[(i, j)] = node_color_scale((i, j)) *
                            (s_next(in_i, in_j, x) + s_next(out_i, out_j, x)) /
                            2.0;
        }
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Graph<'a> {
    in_edges: &'a [Vec<Idx>],
    out_edges: &'a [Vec<Idx>],
}

impl<'a> Graph<'a> {
    pub fn new(in_edges: &'a [Vec<Idx>], out_edges: &'a [Vec<Idx>]) -> Graph<'a> {
        assert!(in_edges.len() == out_edges.len());
        Graph {
            in_edges: in_edges,
            out_edges: out_edges,
        }
    }

    fn num_nodes(&self) -> usize {
        let n = self.in_edges.len();
        assert!(n == self.out_edges.len());
        n
    }

    fn node_degree(&self, node_idx: usize) -> usize {
        self.in_edges[node_idx].len() + self.out_edges[node_idx].len()
    }
}

#[derive(Debug)]
pub struct GraphSimilarityMatrix<'a, F: 'a> {
    graph_a: Graph<'a>,
    graph_b: Graph<'a>,
    node_color_scale: &'a F,
    // current version of similarity matrix
    current: DMat<f32>,
    // previous version of similarity matrix
    previous: DMat<f32>,
    // current number of iterations
    num_iterations: usize,
}

impl<'a, F> GraphSimilarityMatrix<'a, F> where F: Fn((usize, usize)) -> f32
{
    pub fn new(graph_a: Graph<'a>,
               graph_b: Graph<'a>,
               node_color_scale: &'a F)
               -> GraphSimilarityMatrix<'a, F> {
        // `x` is the node-similarity matrix.
        // we initialize `x`, so that x[i,j]=1 for all i in A.edges() and j in
        // B.edges().
        let x: DMat<f32> = DMat::from_fn(graph_a.num_nodes(), graph_b.num_nodes(), |i, j| {
            node_color_scale((i, j)) *
            if graph_a.node_degree(i) > 0 && graph_b.node_degree(j) > 0 {
                1.0
            } else {
                0.0
            }
        });

        let new_x: DMat<f32> = DMat::new_zeros(graph_a.num_nodes(), graph_b.num_nodes());

        GraphSimilarityMatrix {
            graph_a: graph_a,
            graph_b: graph_b,
            node_color_scale: node_color_scale,
            current: x,
            previous: new_x,
            num_iterations: 0,
        }
    }

    fn in_eps(&self, eps: f32) -> bool {
        self.previous.approx_eq_eps(&self.current, &eps)
    }

    /// Calculates the next iteration of the similarity matrix.
    pub fn next(&mut self) {
        next_x(&self.current,
               &mut self.previous,
               self.graph_a.in_edges,
               self.graph_b.in_edges,
               self.graph_a.out_edges,
               self.graph_b.out_edges,
               self.node_color_scale);
        mem::swap(&mut self.previous, &mut self.current);
        self.num_iterations += 1;
    }

    #[inline]
    /// Iteratively calculate the similarity matrix.
    ///
    /// `eps`:   When to stop the iteration
    /// `stop_after_iter`: Stop after iteration (Calculate x(stop_after_iter))
    pub fn iterate(&mut self, eps: f32, stop_after_iter: usize) {
        for _ in 0..stop_after_iter {
            if self.in_eps(eps) {
                break;
            }
            self.next();
        }
    }

    pub fn matrix(&self) -> &DMat<f32> {
        &self.current
    }

    pub fn num_iterations(&self) -> usize {
        self.num_iterations
    }

    fn optimal_node_assignment(&self, n: usize) -> Vec<(usize, usize)> {
        let x = &self.current;
        assert!(n > 0);
        let mut w = WeightMatrix::from_fn(n, |ij| x[ij]);
        let assignment = solve_assignment(&mut w);
        assert!(assignment.len() == n);
        assignment
    }

    fn score_optimal_sum(&self, n: usize) -> f32 {
        self.optimal_node_assignment(n).iter().fold(0.0, |acc, &ab| acc + self.current[ab])
    }

    /// Calculate a measure how good the edge weights match up.
    ///
    /// We start by calculating the optimal node assignment between nodes of graph A and
    /// graph B, then compare all outgoing edges of similar nodes by again using an
    /// assignment.
    pub fn score_outgoing_edge_weights(&self) -> f32 {
        // XXX
        0.0
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min degree of both graphs.
    /// Used as default in the paper.
    pub fn score_sum_norm_min_degree(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        if n > 0 {
            self.score_optimal_sum(n) / n as f32
        } else {
            0.0
        }
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min degree of both graphs.
    /// Penalizes the difference in size of graphs.
    pub fn score_sum_norm_max_degree(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        let m = cmp::max(x.nrows(), x.ncols());

        if n > 0 {
            assert!(m > 0);
            self.score_optimal_sum(n) / m as f32
        } else {
            0.0
        }
    }

    /// Calculates the average over the whole node similarity matrix. This is faster,
    /// as no assignment has to be found. "Graphs with greater number of automorphisms
    /// would be considered to be more self-similar than graphs without automorphisms."
    pub fn score_average(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        if n > 0 {
            let sum: f32 = x.as_vec().iter().fold(0.0, |acc, &v| acc + v);
            let len = x.as_vec().len();
            assert!(len > 0);
            sum / len as f32
        } else {
            0.0
        }
    }
}

#[test]
fn test_matrix() {
    // A: 0 --> 1
    let in_a = vec![vec![], vec![0]];
    let out_a = vec![vec![1], vec![]];

    // B: 0 <-- 1
    let in_b = vec![vec![1], vec![]];
    let out_b = vec![vec![], vec![0]];

    let node_color = |_| 1.0;
    let mut s = GraphSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                           Graph::new(&in_b, &out_b),
                                           &node_color);
    s.iterate(0.1, 100);

    // println!("{:?}", s);
    assert_eq!(1, s.num_iterations());
    let mat = s.matrix();
    assert_eq!(2, mat.nrows());
    assert_eq!(2, mat.ncols());

    // A and B are isomorphic
    assert_eq!(1.0, mat[(0, 0)]);
    assert_eq!(1.0, mat[(0, 1)]);
    assert_eq!(1.0, mat[(1, 0)]);
    assert_eq!(1.0, mat[(1, 1)]);
}

#[test]
fn test_matrix_iter1() {
    let in_a = vec![vec![0, 0, 0]];
    let out_a = vec![vec![0, 0, 0]];

    let in_b = vec![vec![0, 0, 0, 0, 0]];
    let out_b = vec![vec![0, 0, 0, 0, 0]];

    let node_color = |_| 1.0;
    let mut s = GraphSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                           Graph::new(&in_b, &out_b),
                                           &node_color);
    s.iterate(0.1, 1);

    assert_eq!(1, s.num_iterations());
    let mat = s.matrix();
    assert_eq!(3.0 / 5.0, mat[(0, 0)]);
}

#[test]
fn test_score() {
    // A: 0 --> 1
    let in_a = vec![vec![], vec![0]];
    let out_a = vec![vec![1], vec![]];

    // B: 0 <-- 1
    let in_b = vec![vec![1], vec![]];
    let out_b = vec![vec![], vec![0]];

    let node_color = |_| 1.0;
    let mut s = GraphSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                           Graph::new(&in_b, &out_b),
                                           &node_color);
    s.iterate(0.1, 100);

    assert_eq!(1, s.num_iterations());

    // The score is 1.0 <=> A and B are isomorphic
    assert_eq!(1.0, s.score_sum_norm_min_degree());

    // The score is 1.0 <=> A and B are isomorphic
    assert_eq!(1.0, s.score_sum_norm_max_degree());
}
<commit_msg>Introduce NodeColorMatching trait.<commit_after>/// A graph similarity score using neighbor matching according to [this paper][1].
///
/// [1]: http://arxiv.org/abs/1009.5290 "2010, Mladen Nikolic, Measuring Similarity
///      of Graph Nodes by Neighbor Matching"

extern crate nalgebra;
extern crate munkres;

use nalgebra::{DMat, Shape, ApproxEq};
use munkres::{WeightMatrix, solve_assignment};
use std::cmp;
use std::mem;

pub type Idx = u32;

trait Edges {
    /// The number of edges
    fn len(&self) -> usize;

    /// Returns the target node of the nth-edge
    fn nth_edge(&self, n: usize) -> usize;

    /// Returns the nth edge weight
    fn nth_edge_weight(&self, _n: usize) -> f32 {
        panic!();
    }
}

impl<'a> Edges for &'a [Idx] {
    #[inline]
    fn len(&self) -> usize {
        let x: &[Idx] = self;
        x.len()
    }
    #[inline]
    fn nth_edge(&self, n: usize) -> usize {
        self[n] as usize
    }
}

pub trait NodeColorMatching {
    /// Determines how close or distant two nodes `node_i` of graph A,
    /// and `node_j` of graph B are. If they have different colors,
    /// this method could return 0.0 to describe that they are completely
    /// different nodes and as such the neighbor matching will try to choose
    /// a different node.
    /// NOTE: The returned value MUST be in the range [0, 1].
    fn node_color_matching(&self, node_i: usize, node_j: usize) -> f32;
}

pub struct IgnoreNodeColors;

impl NodeColorMatching for IgnoreNodeColors {
    fn node_color_matching(&self, _node_i: usize, _node_j: usize) -> f32 {
        1.0
    }
}

#[inline]
/// Calculates the similarity of two nodes `i` and `j`.
///
/// `n_i` contains the neighborhood of i (either in or out neighbors, not both)
/// `n_j` contains the neighborhood of j (either in or out neighbors, not both)
/// `x`   the similarity matrix.
fn s_next<T: Edges>(n_i: T, n_j: T, x: &DMat<f32>) -> f32 {
    let max_deg = cmp::max(n_i.len(), n_j.len());
    let min_deg = cmp::min(n_i.len(), n_j.len());

    if min_deg == 0 {
        // in the paper, 0/0 is defined as 1.0
        return 1.0;
    }

    assert!(min_deg > 0 && max_deg > 0);

    // map indicies from 0..min(degree) to the node indices
    let mapidx = |(a, b)| (n_i.nth_edge(a), n_j.nth_edge(b));

    let mut w = WeightMatrix::from_fn(min_deg, |ab| x[mapidx(ab)]);

    let assignment = solve_assignment(&mut w);
    assert!(assignment.len() == min_deg);

    let sum: f32 = assignment.iter().fold(0.0, |acc, &ab| acc + x[mapidx(ab)]);

    return sum / max_deg as f32;
}

#[inline]
/// Calculates x[k+1]
///
/// `node_color_matching(i,j)`: If two nodes `i` (of graph A) and `j` (of graph B)
/// are of different color, this can be set to return 0.0. Alternatively a
/// node-color distance (within 0...1) could be used to penalize.
fn next_x<F: NodeColorMatching>(x: &DMat<f32>,
                                new_x: &mut DMat<f32>,
                                in_a: &[Vec<Idx>],
                                in_b: &[Vec<Idx>],
                                out_a: &[Vec<Idx>],
                                out_b: &[Vec<Idx>],
                                node_color_matching: &F) {
    let shape = x.shape();
    assert!(shape == new_x.shape());

    for i in 0..shape.0 {
        for j in 0..shape.1 {
            let in_i: &[Idx] = &in_a[i];
            let in_j: &[Idx] = &in_b[j];
            let out_i: &[Idx] = &out_a[i];
            let out_j: &[Idx] = &out_b[j];
            let scale = node_color_matching.node_color_matching(i, j);
            debug_assert!(scale >= 0.0 && scale <= 1.0);
            new_x[(i, j)] = scale * (s_next(in_i, in_j, x) + s_next(out_i, out_j, x)) / 2.0;
        }
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Graph<'a> {
    in_edges: &'a [Vec<Idx>],
    out_edges: &'a [Vec<Idx>],
}

impl<'a> Graph<'a> {
    pub fn new(in_edges: &'a [Vec<Idx>], out_edges: &'a [Vec<Idx>]) -> Graph<'a> {
        assert!(in_edges.len() == out_edges.len());
        Graph {
            in_edges: in_edges,
            out_edges: out_edges,
        }
    }

    fn num_nodes(&self) -> usize {
        let n = self.in_edges.len();
        assert!(n == self.out_edges.len());
        n
    }

    fn node_degree(&self, node_idx: usize) -> usize {
        self.in_edges[node_idx].len() + self.out_edges[node_idx].len()
    }
}

#[derive(Debug)]
pub struct GraphSimilarityMatrix<'a, F: NodeColorMatching + 'a> {
    graph_a: Graph<'a>,
    graph_b: Graph<'a>,
    node_color_matching: F,
    // current version of similarity matrix
    current: DMat<f32>,
    // previous version of similarity matrix
    previous: DMat<f32>,
    // current number of iterations
    num_iterations: usize,
}

impl<'a, F> GraphSimilarityMatrix<'a, F> where F: NodeColorMatching
{
    pub fn new(graph_a: Graph<'a>,
               graph_b: Graph<'a>,
               node_color_matching: F)
               -> GraphSimilarityMatrix<'a, F> {
        // `x` is the node-similarity matrix.
        // we initialize `x`, so that x[i,j]=1 for all i in A.edges() and j in
        // B.edges().
        let x: DMat<f32> = DMat::from_fn(graph_a.num_nodes(), graph_b.num_nodes(), |i, j| {
            if graph_a.node_degree(i) > 0 && graph_b.node_degree(j) > 0 {
                let scale = node_color_matching.node_color_matching(i, j);
                debug_assert!(scale >= 0.0 && scale <= 1.0);
                scale
            } else {
                0.0
            }
        });

        let new_x: DMat<f32> = DMat::new_zeros(graph_a.num_nodes(), graph_b.num_nodes());

        GraphSimilarityMatrix {
            graph_a: graph_a,
            graph_b: graph_b,
            node_color_matching: node_color_matching,
            current: x,
            previous: new_x,
            num_iterations: 0,
        }
    }

    fn in_eps(&self, eps: f32) -> bool {
        self.previous.approx_eq_eps(&self.current, &eps)
    }

    /// Calculates the next iteration of the similarity matrix.
    pub fn next(&mut self) {
        next_x(&self.current,
               &mut self.previous,
               self.graph_a.in_edges,
               self.graph_b.in_edges,
               self.graph_a.out_edges,
               self.graph_b.out_edges,
               &self.node_color_matching);
        mem::swap(&mut self.previous, &mut self.current);
        self.num_iterations += 1;
    }

    #[inline]
    /// Iteratively calculate the similarity matrix.
    ///
    /// `eps`:   When to stop the iteration
    /// `stop_after_iter`: Stop after iteration (Calculate x(stop_after_iter))
    pub fn iterate(&mut self, eps: f32, stop_after_iter: usize) {
        for _ in 0..stop_after_iter {
            if self.in_eps(eps) {
                break;
            }
            self.next();
        }
    }

    pub fn matrix(&self) -> &DMat<f32> {
        &self.current
    }

    pub fn num_iterations(&self) -> usize {
        self.num_iterations
    }

    fn optimal_node_assignment(&self, n: usize) -> Vec<(usize, usize)> {
        let x = &self.current;
        assert!(n > 0);
        let mut w = WeightMatrix::from_fn(n, |ij| x[ij]);
        let assignment = solve_assignment(&mut w);
        assert!(assignment.len() == n);
        assignment
    }

    fn score_optimal_sum(&self, n: usize) -> f32 {
        self.optimal_node_assignment(n).iter().fold(0.0, |acc, &ab| acc + self.current[ab])
    }

    /// Calculate a measure how good the edge weights match up.
    ///
    /// We start by calculating the optimal node assignment between nodes of graph A and
    /// graph B, then compare all outgoing edges of similar nodes by again using an
    /// assignment.
    pub fn score_outgoing_edge_weights(&self) -> f32 {
        // XXX
        0.0
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min degree of both graphs.
    /// Used as default in the paper.
    pub fn score_sum_norm_min_degree(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        if n > 0 {
            self.score_optimal_sum(n) / n as f32
        } else {
            0.0
        }
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min degree of both graphs.
    /// Penalizes the difference in size of graphs.
    pub fn score_sum_norm_max_degree(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        let m = cmp::max(x.nrows(), x.ncols());

        if n > 0 {
            assert!(m > 0);
            self.score_optimal_sum(n) / m as f32
        } else {
            0.0
        }
    }

    /// Calculates the average over the whole node similarity matrix. This is faster,
    /// as no assignment has to be found. "Graphs with greater number of automorphisms
    /// would be considered to be more self-similar than graphs without automorphisms."
    pub fn score_average(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        if n > 0 {
            let sum: f32 = x.as_vec().iter().fold(0.0, |acc, &v| acc + v);
            let len = x.as_vec().len();
            assert!(len > 0);
            sum / len as f32
        } else {
            0.0
        }
    }
}

#[test]
fn test_matrix() {
    // A: 0 --> 1
    let in_a = vec![vec![], vec![0]];
    let out_a = vec![vec![1], vec![]];

    // B: 0 <-- 1
    let in_b = vec![vec![1], vec![]];
    let out_b = vec![vec![], vec![0]];

    let mut s = GraphSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                           Graph::new(&in_b, &out_b),
                                           IgnoreNodeColors);
    s.iterate(0.1, 100);

    // println!("{:?}", s);
    assert_eq!(1, s.num_iterations());
    let mat = s.matrix();
    assert_eq!(2, mat.nrows());
    assert_eq!(2, mat.ncols());

    // A and B are isomorphic
    assert_eq!(1.0, mat[(0, 0)]);
    assert_eq!(1.0, mat[(0, 1)]);
    assert_eq!(1.0, mat[(1, 0)]);
    assert_eq!(1.0, mat[(1, 1)]);
}

#[test]
fn test_matrix_iter1() {
    let in_a = vec![vec![0, 0, 0]];
    let out_a = vec![vec![0, 0, 0]];

    let in_b = vec![vec![0, 0, 0, 0, 0]];
    let out_b = vec![vec![0, 0, 0, 0, 0]];

    let mut s = GraphSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                           Graph::new(&in_b, &out_b),
                                           IgnoreNodeColors);
    s.iterate(0.1, 1);

    assert_eq!(1, s.num_iterations());
    let mat = s.matrix();
    assert_eq!(3.0 / 5.0, mat[(0, 0)]);
}

#[test]
fn test_score() {
    // A: 0 --> 1
    let in_a = vec![vec![], vec![0]];
    let out_a = vec![vec![1], vec![]];

    // B: 0 <-- 1
    let in_b = vec![vec![1], vec![]];
    let out_b = vec![vec![], vec![0]];

    let mut s = GraphSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                           Graph::new(&in_b, &out_b),
                                           IgnoreNodeColors);
    s.iterate(0.1, 100);

    assert_eq!(1, s.num_iterations());

    // The score is 1.0 <=> A and B are isomorphic
    assert_eq!(1.0, s.score_sum_norm_min_degree());

    // The score is 1.0 <=> A and B are isomorphic
    assert_eq!(1.0, s.score_sum_norm_max_degree());
}
<|endoftext|>


<commit_before>#[derive(Clone, Debug)]
pub enum Type {
    Int8,
    Int16,
    Int32,
    Int64,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    Float32,
    Float64,
    Char,
    Bool,
    Array(Box<Type>, usize),
    Compound(Vec<Field>)
}

#[derive(Clone, Debug)]
pub struct Field {
    pub ty: Type,
    pub name: String,
    pub offset: usize,
}

impl Field {
    pub fn new<S, U>(ty: &Type, name: S, offset: U) -> Field
    where S: Into<String>, U: Into<usize> {
        Field {
            ty: ty.clone(),
            name: name.into(),
            offset: offset.into()
        }
    }
}

pub trait TypeInfo : Copy {
    fn type_info() -> Type;
}

macro_rules! impl_atomic {
    ($t:ty, $i:ident) => (
        impl $crate::TypeInfo for $t {
            #[inline(always)]
            fn type_info() -> $crate::Type {
                $crate::Type::$i
            }
        }
    )
}

impl_atomic!(i8, Int8);
impl_atomic!(i16, Int16);
impl_atomic!(i32, Int32);
impl_atomic!(i64, Int64);
impl_atomic!(u8, UInt8);
impl_atomic!(u16, UInt16);
impl_atomic!(u32, UInt32);
impl_atomic!(u64, UInt64);
impl_atomic!(f32, Float32);
impl_atomic!(f64, Float64);
impl_atomic!(char, Char);
impl_atomic!(bool, Bool);

macro_rules! impl_array {
    ($($n:expr),*$(,)*) => {
        $(
            impl<T: $crate::TypeInfo> $crate::TypeInfo for [T; $n] {
                #[inline(always)]
                fn type_info() -> $crate::Type {
                    $crate::Type::Array(
                        Box::new(<T as $crate::TypeInfo>::type_info()),
                        $n
                    )
                }
            }
        )*
    };
}

impl_array!(
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
);

#[macro_export]
macro_rules! def {
    ($($(#[$attr:meta])* struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    ($($(#[$attr:meta])* pub struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    ($($(#[$attr:meta])* pub struct $s:ident { $(pub $i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { pub $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    (@impl $s:ident { $($i:ident: $t:ty),+ }) => (
        impl $crate::TypeInfo for $s {
            fn type_info() -> $crate::Type {
                let base = 0usize as *const $s;
                $crate::Type::Compound(vec![$(
                    $crate::Field::new(
                        &<$t as $crate::TypeInfo>::type_info(),
                        stringify!($i),
                        unsafe { &((*base).$i) as *const $t as usize}
                    )
                ),*])
            }
        }
    );
}

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

    def! {
        #[derive(Debug)]
        pub struct Foo {
            pub a: bool,
            pub b: i32,
        }

        pub struct Bar {
            pub foo: [Foo; 2],
            pub s: f64
        }
    }

    #[test]
    fn test_smoke() {
        println!("{:#?}", Bar::type_info());
    }
}
<commit_msg>Implement TypeInfo for isize/usize<commit_after>#[derive(Clone, Debug)]
pub enum Type {
    Int8,
    Int16,
    Int32,
    Int64,
    ISize,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    USize,
    Float32,
    Float64,
    Char,
    Bool,
    Array(Box<Type>, usize),
    Compound(Vec<Field>)
}

#[derive(Clone, Debug)]
pub struct Field {
    pub ty: Type,
    pub name: String,
    pub offset: usize,
}

impl Field {
    pub fn new<S, U>(ty: &Type, name: S, offset: U) -> Field
    where S: Into<String>, U: Into<usize> {
        Field {
            ty: ty.clone(),
            name: name.into(),
            offset: offset.into()
        }
    }
}

pub trait TypeInfo : Copy {
    fn type_info() -> Type;
}

macro_rules! impl_atomic {
    ($t:ty, $i:ident) => (
        impl $crate::TypeInfo for $t {
            #[inline(always)]
            fn type_info() -> $crate::Type {
                $crate::Type::$i
            }
        }
    )
}

impl_atomic!(i8, Int8);
impl_atomic!(i16, Int16);
impl_atomic!(i32, Int32);
impl_atomic!(i64, Int64);
impl_atomic!(isize, ISize);
impl_atomic!(u8, UInt8);
impl_atomic!(u16, UInt16);
impl_atomic!(u32, UInt32);
impl_atomic!(u64, UInt64);
impl_atomic!(usize, USize);
impl_atomic!(f32, Float32);
impl_atomic!(f64, Float64);
impl_atomic!(char, Char);
impl_atomic!(bool, Bool);

macro_rules! impl_array {
    ($($n:expr),*$(,)*) => {
        $(
            impl<T: $crate::TypeInfo> $crate::TypeInfo for [T; $n] {
                #[inline(always)]
                fn type_info() -> $crate::Type {
                    $crate::Type::Array(
                        Box::new(<T as $crate::TypeInfo>::type_info()),
                        $n
                    )
                }
            }
        )*
    };
}

impl_array!(
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
);

#[macro_export]
macro_rules! def {
    ($($(#[$attr:meta])* struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    ($($(#[$attr:meta])* pub struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    ($($(#[$attr:meta])* pub struct $s:ident { $(pub $i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { pub $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    (@impl $s:ident { $($i:ident: $t:ty),+ }) => (
        impl $crate::TypeInfo for $s {
            fn type_info() -> $crate::Type {
                let base = 0usize as *const $s;
                $crate::Type::Compound(vec![$(
                    $crate::Field::new(
                        &<$t as $crate::TypeInfo>::type_info(),
                        stringify!($i),
                        unsafe { &((*base).$i) as *const $t as usize}
                    )
                ),*])
            }
        }
    );
}

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

    def! {
        #[derive(Debug)]
        pub struct Foo {
            pub a: bool,
            pub b: i32,
        }

        pub struct Bar {
            pub foo: [Foo; 2],
            pub s: f64
        }
    }

    #[test]
    fn test_smoke() {
        println!("{:#?}", Bar::type_info());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// TODO we're going to allocate a whole bunch of temp Strings, is it worth
// keeping some scratch mem for this and running our own StrPool?
// TODO for lint violations of names, emit a refactor script

#[macro_use]
extern crate log;

extern crate syntex_syntax as syntax;
extern crate rustc_serialize;

extern crate strings;

extern crate unicode_segmentation;
extern crate regex;
extern crate diff;
extern crate term;

use syntax::ast;
use syntax::codemap::{mk_sp, CodeMap, Span};
use syntax::errors::{Handler, DiagnosticBuilder};
use syntax::errors::emitter::{ColorConfig, EmitterWriter};
use syntax::parse::{self, ParseSess};

use std::io::{stdout, Write};
use std::ops::{Add, Sub};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::collections::HashMap;
use std::fmt;

use issues::{BadIssueSeeker, Issue};
use filemap::FileMap;
use visitor::FmtVisitor;
use config::{Config, WriteMode};

#[macro_use]
mod utils;
pub mod config;
pub mod filemap;
mod visitor;
mod checkstyle;
mod items;
mod missed_spans;
mod lists;
mod types;
mod expr;
mod imports;
mod issues;
mod rewrite;
mod string;
mod comment;
mod modules;
pub mod rustfmt_diff;
mod chains;
mod macros;
mod patterns;

const MIN_STRING: usize = 10;
// When we get scoped annotations, we should have rustfmt::skip.
const SKIP_ANNOTATION: &'static str = "rustfmt_skip";

pub trait Spanned {
    fn span(&self) -> Span;
}

impl Spanned for ast::Expr {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Pat {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Ty {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Arg {
    fn span(&self) -> Span {
        if items::is_named_arg(self) {
            mk_sp(self.pat.span.lo, self.ty.span.hi)
        } else {
            self.ty.span
        }
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Indent {
    // Width of the block indent, in characters. Must be a multiple of
    // Config::tab_spaces.
    pub block_indent: usize,
    // Alignment in characters.
    pub alignment: usize,
}

impl Indent {
    pub fn new(block_indent: usize, alignment: usize) -> Indent {
        Indent {
            block_indent: block_indent,
            alignment: alignment,
        }
    }

    pub fn empty() -> Indent {
        Indent::new(0, 0)
    }

    pub fn block_indent(mut self, config: &Config) -> Indent {
        self.block_indent += config.tab_spaces;
        self
    }

    pub fn block_unindent(mut self, config: &Config) -> Indent {
        self.block_indent -= config.tab_spaces;
        self
    }

    pub fn width(&self) -> usize {
        self.block_indent + self.alignment
    }

    pub fn to_string(&self, config: &Config) -> String {
        let (num_tabs, num_spaces) = if config.hard_tabs {
            (self.block_indent / config.tab_spaces, self.alignment)
        } else {
            (0, self.block_indent + self.alignment)
        };
        let num_chars = num_tabs + num_spaces;
        let mut indent = String::with_capacity(num_chars);
        for _ in 0..num_tabs {
            indent.push('\t')
        }
        for _ in 0..num_spaces {
            indent.push(' ')
        }
        indent
    }
}

impl Add for Indent {
    type Output = Indent;

    fn add(self, rhs: Indent) -> Indent {
        Indent {
            block_indent: self.block_indent + rhs.block_indent,
            alignment: self.alignment + rhs.alignment,
        }
    }
}

impl Sub for Indent {
    type Output = Indent;

    fn sub(self, rhs: Indent) -> Indent {
        Indent::new(self.block_indent - rhs.block_indent,
                    self.alignment - rhs.alignment)
    }
}

impl Add<usize> for Indent {
    type Output = Indent;

    fn add(self, rhs: usize) -> Indent {
        Indent::new(self.block_indent, self.alignment + rhs)
    }
}

impl Sub<usize> for Indent {
    type Output = Indent;

    fn sub(self, rhs: usize) -> Indent {
        Indent::new(self.block_indent, self.alignment - rhs)
    }
}

pub enum ErrorKind {
    // Line has exceeded character limit
    LineOverflow,
    // Line ends in whitespace
    TrailingWhitespace,
    // TO-DO or FIX-ME item without an issue number
    BadIssue(Issue),
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ErrorKind::LineOverflow => write!(fmt, "line exceeded maximum length"),
            ErrorKind::TrailingWhitespace => write!(fmt, "left behind trailing whitespace"),
            ErrorKind::BadIssue(issue) => write!(fmt, "found {}", issue),
        }
    }
}

// Formatting errors that are identified *after* rustfmt has run.
pub struct FormattingError {
    line: u32,
    kind: ErrorKind,
}

impl FormattingError {
    fn msg_prefix(&self) -> &str {
        match self.kind {
            ErrorKind::LineOverflow |
            ErrorKind::TrailingWhitespace => "Rustfmt failed at",
            ErrorKind::BadIssue(_) => "WARNING:",
        }
    }

    fn msg_suffix(&self) -> &str {
        match self.kind {
            ErrorKind::LineOverflow |
            ErrorKind::TrailingWhitespace => "(sorry)",
            ErrorKind::BadIssue(_) => "",
        }
    }
}

pub struct FormatReport {
    // Maps stringified file paths to their associated formatting errors.
    file_error_map: HashMap<String, Vec<FormattingError>>,
}

impl FormatReport {
    pub fn warning_count(&self) -> usize {
        self.file_error_map.iter().map(|(_, ref errors)| errors.len()).fold(0, |acc, x| acc + x)
    }
}

impl fmt::Display for FormatReport {
    // Prints all the formatting errors.
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        for (file, errors) in &self.file_error_map {
            for error in errors {
                try!(write!(fmt,
                            "{} {}:{}: {} {}\n",
                            error.msg_prefix(),
                            file,
                            error.line,
                            error.kind,
                            error.msg_suffix()));
            }
        }
        Ok(())
    }
}

// Formatting which depends on the AST.
fn format_ast(krate: &ast::Crate,
              parse_session: &ParseSess,
              main_file: &Path,
              config: &Config)
              -> FileMap {
    let mut file_map = FileMap::new();
    for (path, module) in modules::list_files(krate, parse_session.codemap()) {
        if config.skip_children && path.as_path() != main_file {
            continue;
        }
        let path = path.to_str().unwrap();
        if config.verbose {
            println!("Formatting {}", path);
        }
        let mut visitor = FmtVisitor::from_codemap(parse_session, config);
        visitor.format_separate_mod(module);
        file_map.insert(path.to_owned(), visitor.buffer);
    }
    file_map
}

// Formatting done on a char by char or line by line basis.
// TODO(#209) warn on bad license
// TODO(#20) other stuff for parity with make tidy
fn format_lines(file_map: &mut FileMap, config: &Config) -> FormatReport {
    let mut truncate_todo = Vec::new();
    let mut report = FormatReport { file_error_map: HashMap::new() };

    // Iterate over the chars in the file map.
    for (f, text) in file_map.iter() {
        let mut trims = vec![];
        let mut last_wspace: Option<usize> = None;
        let mut line_len = 0;
        let mut cur_line = 1;
        let mut newline_count = 0;
        let mut errors = vec![];
        let mut issue_seeker = BadIssueSeeker::new(config.report_todo, config.report_fixme);

        for (c, b) in text.chars() {
            if c == '\r' {
                line_len += c.len_utf8();
                continue;
            }

            // Add warnings for bad todos/ fixmes
            if let Some(issue) = issue_seeker.inspect(c) {
                errors.push(FormattingError {
                    line: cur_line,
                    kind: ErrorKind::BadIssue(issue),
                });
            }

            if c == '\n' {
                // Check for (and record) trailing whitespace.
                if let Some(lw) = last_wspace {
                    trims.push((cur_line, lw, b));
                    line_len -= b - lw;
                }
                // Check for any line width errors we couldn't correct.
                if line_len > config.max_width {
                    errors.push(FormattingError {
                        line: cur_line,
                        kind: ErrorKind::LineOverflow,
                    });
                }
                line_len = 0;
                cur_line += 1;
                newline_count += 1;
                last_wspace = None;
            } else {
                newline_count = 0;
                line_len += c.len_utf8();
                if c.is_whitespace() {
                    if last_wspace.is_none() {
                        last_wspace = Some(b);
                    }
                } else {
                    last_wspace = None;
                }
            }
        }

        if newline_count > 1 {
            debug!("track truncate: {} {} {}", f, text.len, newline_count);
            truncate_todo.push((f.to_owned(), text.len - newline_count + 1))
        }

        for &(l, _, _) in &trims {
            errors.push(FormattingError {
                line: l,
                kind: ErrorKind::TrailingWhitespace,
            });
        }

        report.file_error_map.insert(f.to_owned(), errors);
    }

    for (f, l) in truncate_todo {
        file_map.get_mut(&f).unwrap().truncate(l);
    }

    report
}

fn parse_input(input: Input, parse_session: &ParseSess) -> Result<ast::Crate, DiagnosticBuilder> {
    let krate = match input {
        Input::File(file) => parse::parse_crate_from_file(&file, Vec::new(), &parse_session),
        Input::Text(text) => {
            parse::parse_crate_from_source_str("stdin".to_owned(), text, Vec::new(), &parse_session)
        }
    };

    krate
}

pub fn format_input(input: Input, config: &Config) -> (FileMap, FormatReport) {
    let codemap = Rc::new(CodeMap::new());

    let tty_handler = Handler::with_tty_emitter(ColorConfig::Auto,
                                                None,
                                                true,
                                                false,
                                                codemap.clone());
    let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());

    let main_file = match input {
        Input::File(ref file) => file.clone(),
        Input::Text(..) => PathBuf::from("stdin"),
    };

    let krate = match parse_input(input, &parse_session) {
        Ok(krate) => krate,
        Err(mut diagnostic) => {
            diagnostic.emit();
            panic!("Unrecoverable parse error");
        }
    };

    // Suppress error output after parsing.
    let silent_emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None, codemap.clone()));
    parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);

    let mut file_map = format_ast(&krate, &parse_session, &main_file, config);

    // For some reason, the codemap does not include terminating
    // newlines so we must add one on for each file. This is sad.
    filemap::append_newlines(&mut file_map);

    let report = format_lines(&mut file_map, config);
    (file_map, report)
}

pub enum Input {
    File(PathBuf),
    Text(String),
}

pub fn run(input: Input, config: &Config) {
    let (file_map, report) = format_input(input, config);

    let ignore_errors = config.write_mode == WriteMode::Plain;
    if !ignore_errors {
        msg!("{}", report);
    }

    let mut out = stdout();
    let write_result = filemap::write_all_files(&file_map, &mut out, config);

    if let Err(msg) = write_result {
        msg!("Error writing files: {}", msg);
    }
}
<commit_msg>don't silence error unnecessary <commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// TODO we're going to allocate a whole bunch of temp Strings, is it worth
// keeping some scratch mem for this and running our own StrPool?
// TODO for lint violations of names, emit a refactor script

#[macro_use]
extern crate log;

extern crate syntex_syntax as syntax;
extern crate rustc_serialize;

extern crate strings;

extern crate unicode_segmentation;
extern crate regex;
extern crate diff;
extern crate term;

use syntax::ast;
use syntax::codemap::{mk_sp, CodeMap, Span};
use syntax::errors::{Handler, DiagnosticBuilder};
use syntax::errors::emitter::{ColorConfig, EmitterWriter};
use syntax::parse::{self, ParseSess};

use std::io::{stdout, Write};
use std::ops::{Add, Sub};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::collections::HashMap;
use std::fmt;

use issues::{BadIssueSeeker, Issue};
use filemap::FileMap;
use visitor::FmtVisitor;
use config::{Config, WriteMode};

#[macro_use]
mod utils;
pub mod config;
pub mod filemap;
mod visitor;
mod checkstyle;
mod items;
mod missed_spans;
mod lists;
mod types;
mod expr;
mod imports;
mod issues;
mod rewrite;
mod string;
mod comment;
mod modules;
pub mod rustfmt_diff;
mod chains;
mod macros;
mod patterns;

const MIN_STRING: usize = 10;
// When we get scoped annotations, we should have rustfmt::skip.
const SKIP_ANNOTATION: &'static str = "rustfmt_skip";

pub trait Spanned {
    fn span(&self) -> Span;
}

impl Spanned for ast::Expr {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Pat {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Ty {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Arg {
    fn span(&self) -> Span {
        if items::is_named_arg(self) {
            mk_sp(self.pat.span.lo, self.ty.span.hi)
        } else {
            self.ty.span
        }
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Indent {
    // Width of the block indent, in characters. Must be a multiple of
    // Config::tab_spaces.
    pub block_indent: usize,
    // Alignment in characters.
    pub alignment: usize,
}

impl Indent {
    pub fn new(block_indent: usize, alignment: usize) -> Indent {
        Indent {
            block_indent: block_indent,
            alignment: alignment,
        }
    }

    pub fn empty() -> Indent {
        Indent::new(0, 0)
    }

    pub fn block_indent(mut self, config: &Config) -> Indent {
        self.block_indent += config.tab_spaces;
        self
    }

    pub fn block_unindent(mut self, config: &Config) -> Indent {
        self.block_indent -= config.tab_spaces;
        self
    }

    pub fn width(&self) -> usize {
        self.block_indent + self.alignment
    }

    pub fn to_string(&self, config: &Config) -> String {
        let (num_tabs, num_spaces) = if config.hard_tabs {
            (self.block_indent / config.tab_spaces, self.alignment)
        } else {
            (0, self.block_indent + self.alignment)
        };
        let num_chars = num_tabs + num_spaces;
        let mut indent = String::with_capacity(num_chars);
        for _ in 0..num_tabs {
            indent.push('\t')
        }
        for _ in 0..num_spaces {
            indent.push(' ')
        }
        indent
    }
}

impl Add for Indent {
    type Output = Indent;

    fn add(self, rhs: Indent) -> Indent {
        Indent {
            block_indent: self.block_indent + rhs.block_indent,
            alignment: self.alignment + rhs.alignment,
        }
    }
}

impl Sub for Indent {
    type Output = Indent;

    fn sub(self, rhs: Indent) -> Indent {
        Indent::new(self.block_indent - rhs.block_indent,
                    self.alignment - rhs.alignment)
    }
}

impl Add<usize> for Indent {
    type Output = Indent;

    fn add(self, rhs: usize) -> Indent {
        Indent::new(self.block_indent, self.alignment + rhs)
    }
}

impl Sub<usize> for Indent {
    type Output = Indent;

    fn sub(self, rhs: usize) -> Indent {
        Indent::new(self.block_indent, self.alignment - rhs)
    }
}

pub enum ErrorKind {
    // Line has exceeded character limit
    LineOverflow,
    // Line ends in whitespace
    TrailingWhitespace,
    // TO-DO or FIX-ME item without an issue number
    BadIssue(Issue),
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ErrorKind::LineOverflow => write!(fmt, "line exceeded maximum length"),
            ErrorKind::TrailingWhitespace => write!(fmt, "left behind trailing whitespace"),
            ErrorKind::BadIssue(issue) => write!(fmt, "found {}", issue),
        }
    }
}

// Formatting errors that are identified *after* rustfmt has run.
pub struct FormattingError {
    line: u32,
    kind: ErrorKind,
}

impl FormattingError {
    fn msg_prefix(&self) -> &str {
        match self.kind {
            ErrorKind::LineOverflow |
            ErrorKind::TrailingWhitespace => "Rustfmt failed at",
            ErrorKind::BadIssue(_) => "WARNING:",
        }
    }

    fn msg_suffix(&self) -> &str {
        match self.kind {
            ErrorKind::LineOverflow |
            ErrorKind::TrailingWhitespace => "(sorry)",
            ErrorKind::BadIssue(_) => "",
        }
    }
}

pub struct FormatReport {
    // Maps stringified file paths to their associated formatting errors.
    file_error_map: HashMap<String, Vec<FormattingError>>,
}

impl FormatReport {
    pub fn warning_count(&self) -> usize {
        self.file_error_map.iter().map(|(_, ref errors)| errors.len()).fold(0, |acc, x| acc + x)
    }
}

impl fmt::Display for FormatReport {
    // Prints all the formatting errors.
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        for (file, errors) in &self.file_error_map {
            for error in errors {
                try!(write!(fmt,
                            "{} {}:{}: {} {}\n",
                            error.msg_prefix(),
                            file,
                            error.line,
                            error.kind,
                            error.msg_suffix()));
            }
        }
        Ok(())
    }
}

// Formatting which depends on the AST.
fn format_ast(krate: &ast::Crate,
              parse_session: &ParseSess,
              main_file: &Path,
              config: &Config)
              -> FileMap {
    let mut file_map = FileMap::new();
    for (path, module) in modules::list_files(krate, parse_session.codemap()) {
        if config.skip_children && path.as_path() != main_file {
            continue;
        }
        let path = path.to_str().unwrap();
        if config.verbose {
            println!("Formatting {}", path);
        }
        let mut visitor = FmtVisitor::from_codemap(parse_session, config);
        visitor.format_separate_mod(module);
        file_map.insert(path.to_owned(), visitor.buffer);
    }
    file_map
}

// Formatting done on a char by char or line by line basis.
// TODO(#209) warn on bad license
// TODO(#20) other stuff for parity with make tidy
fn format_lines(file_map: &mut FileMap, config: &Config) -> FormatReport {
    let mut truncate_todo = Vec::new();
    let mut report = FormatReport { file_error_map: HashMap::new() };

    // Iterate over the chars in the file map.
    for (f, text) in file_map.iter() {
        let mut trims = vec![];
        let mut last_wspace: Option<usize> = None;
        let mut line_len = 0;
        let mut cur_line = 1;
        let mut newline_count = 0;
        let mut errors = vec![];
        let mut issue_seeker = BadIssueSeeker::new(config.report_todo, config.report_fixme);

        for (c, b) in text.chars() {
            if c == '\r' {
                line_len += c.len_utf8();
                continue;
            }

            // Add warnings for bad todos/ fixmes
            if let Some(issue) = issue_seeker.inspect(c) {
                errors.push(FormattingError {
                    line: cur_line,
                    kind: ErrorKind::BadIssue(issue),
                });
            }

            if c == '\n' {
                // Check for (and record) trailing whitespace.
                if let Some(lw) = last_wspace {
                    trims.push((cur_line, lw, b));
                    line_len -= b - lw;
                }
                // Check for any line width errors we couldn't correct.
                if line_len > config.max_width {
                    errors.push(FormattingError {
                        line: cur_line,
                        kind: ErrorKind::LineOverflow,
                    });
                }
                line_len = 0;
                cur_line += 1;
                newline_count += 1;
                last_wspace = None;
            } else {
                newline_count = 0;
                line_len += c.len_utf8();
                if c.is_whitespace() {
                    if last_wspace.is_none() {
                        last_wspace = Some(b);
                    }
                } else {
                    last_wspace = None;
                }
            }
        }

        if newline_count > 1 {
            debug!("track truncate: {} {} {}", f, text.len, newline_count);
            truncate_todo.push((f.to_owned(), text.len - newline_count + 1))
        }

        for &(l, _, _) in &trims {
            errors.push(FormattingError {
                line: l,
                kind: ErrorKind::TrailingWhitespace,
            });
        }

        report.file_error_map.insert(f.to_owned(), errors);
    }

    for (f, l) in truncate_todo {
        file_map.get_mut(&f).unwrap().truncate(l);
    }

    report
}

fn parse_input(input: Input, parse_session: &ParseSess) -> Result<ast::Crate, DiagnosticBuilder> {
    let krate = match input {
        Input::File(file) => parse::parse_crate_from_file(&file, Vec::new(), &parse_session),
        Input::Text(text) => {
            parse::parse_crate_from_source_str("stdin".to_owned(), text, Vec::new(), &parse_session)
        }
    };

    krate
}

pub fn format_input(input: Input, config: &Config) -> (FileMap, FormatReport) {
    let codemap = Rc::new(CodeMap::new());

    let tty_handler = Handler::with_tty_emitter(ColorConfig::Auto,
                                                None,
                                                true,
                                                false,
                                                codemap.clone());
    let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());

    let main_file = match input {
        Input::File(ref file) => file.clone(),
        Input::Text(..) => PathBuf::from("stdin"),
    };

    let krate = match parse_input(input, &parse_session) {
        Ok(krate) => krate,
        Err(mut diagnostic) => {
            diagnostic.emit();
            panic!("Unrecoverable parse error");
        }
    };

    // Suppress error output after parsing.
    let silent_emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None, codemap.clone()));
    parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);

    let mut file_map = format_ast(&krate, &parse_session, &main_file, config);

    // For some reason, the codemap does not include terminating
    // newlines so we must add one on for each file. This is sad.
    filemap::append_newlines(&mut file_map);

    let report = format_lines(&mut file_map, config);
    (file_map, report)
}

pub enum Input {
    File(PathBuf),
    Text(String),
}

pub fn run(input: Input, config: &Config) {
    let (file_map, report) = format_input(input, config);
    msg!("{}", report);

    let mut out = stdout();
    let write_result = filemap::write_all_files(&file_map, &mut out, config);

    if let Err(msg) = write_result {
        msg!("Error writing files: {}", msg);
    }
}
<|endoftext|>