<commit_before>//! Timestamp formatting.

use std::time::{SystemTime, UNIX_EPOCH};

use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
use datetime::fmt::DateFormat;
use locale;
use std::cmp;

/// Every timestamp in exa needs to be rendered by a **time format**.
/// Formatting times is tricky, because how a timestamp is rendered can
/// depend on one or more of the following:
///
/// - The user’s locale, for printing the month name as “Feb”, or as “fév”,
///   or as “2月”;
/// - The current year, because certain formats will be less precise when
///   dealing with dates far in the past;
/// - The formatting style that the user asked for on the command-line.
///
/// Because not all formatting styles need the same data, they all have their
/// own enum variants. It’s not worth looking the locale up if the formatter
/// prints month names as numbers.
///
/// Currently exa does not support *custom* styles, where the user enters a
/// format string in an environment variable or something. Just these four.
#[derive(Debug)]
pub enum TimeFormat {

    /// The **default format** uses the user’s locale to print month names,
    /// and specifies the timestamp down to the minute for recent times, and
    /// day for older times.
    DefaultFormat(DefaultFormat),

    /// Use the **ISO format**, which specifies the timestamp down to the
    /// minute for recent times, and day for older times. It uses a number
    /// for the month so it doesn’t need a locale.
    ISOFormat(ISOFormat),

    /// Use the **long ISO format**, which specifies the timestamp down to the
    /// minute using only numbers, without needing the locale or year.
    LongISO,

    /// Use the **full ISO format**, which specifies the timestamp down to the
    /// millisecond and includes its offset down to the minute. This too uses
    /// only numbers so doesn’t require any special consideration.
    FullISO,
}

// There are two different formatting functions because local and zoned
// timestamps are separate types.

impl TimeFormat {
    pub fn format_local(&self, time: SystemTime) -> String {
        match *self {
            TimeFormat::DefaultFormat(ref fmt) => fmt.format_local(time),
            TimeFormat::ISOFormat(ref iso)     => iso.format_local(time),
            TimeFormat::LongISO                => long_local(time),
            TimeFormat::FullISO                => full_local(time),
        }
    }

    pub fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
        match *self {
            TimeFormat::DefaultFormat(ref fmt) => fmt.format_zoned(time, zone),
            TimeFormat::ISOFormat(ref iso)     => iso.format_zoned(time, zone),
            TimeFormat::LongISO                => long_zoned(time, zone),
            TimeFormat::FullISO                => full_zoned(time, zone),
        }
    }
}

#[derive(Debug, Clone)]
pub struct DefaultFormat {

    /// The year of the current time. This gets used to determine which date
    /// format to use.
    pub current_year: i64,

    /// Localisation rules for formatting timestamps.
    pub locale: locale::Time,

    /// Date format for printing out timestamps that are in the current year.
    pub date_and_time: DateFormat<'static>,

    /// Date format for printing out timestamps that *aren’t*.
    pub date_and_year: DateFormat<'static>,
}

impl DefaultFormat {
    pub fn load() -> DefaultFormat {
        use unicode_width::UnicodeWidthStr;

        let locale = locale::Time::load_user_locale()
                       .unwrap_or_else(|_| locale::Time::english());

        let current_year = LocalDateTime::now().year();

        // Some locales use a three-character wide month name (Jan to Dec);
        // others vary between three to four (1月 to 12月, juil.). We check each month width
        // to detect the longest and set the output format accordingly.
        let mut maximum_month_width = 0;
        for i in 0..11 {
            let current_month_width = UnicodeWidthStr::width(&*locale.short_month_name(i));
            maximum_month_width = cmp::max(maximum_month_width, current_month_width);
        }

        let date_and_time = match maximum_month_width {
            4  => DateFormat::parse("{2>:D} {4<:M} {2>:h}:{02>:m}").unwrap(),
            5  => DateFormat::parse("{2>:D} {5<:M} {2>:h}:{02>:m}").unwrap(),
            _  => DateFormat::parse("{2>:D} {:M} {2>:h}:{02>:m}").unwrap(),
        };

        let date_and_year = match maximum_month_width {
            4 => DateFormat::parse("{2>:D} {4<:M} {5>:Y}").unwrap(),
            5 => DateFormat::parse("{2>:D} {5<:M} {5>:Y}").unwrap(),
            _ => DateFormat::parse("{2>:D} {:M} {5>:Y}").unwrap()
        };

        DefaultFormat { current_year, locale, date_and_time, date_and_year }
    }
}

impl DefaultFormat {
    fn is_recent(&self, date: LocalDateTime) -> bool {
        date.year() == self.current_year
    }

    fn month_to_abbrev(month: datetime::Month) -> &'static str {
        match month {
            datetime::Month::January => "Jan",
            datetime::Month::February => "Feb",
            datetime::Month::March => "Mar",
            datetime::Month::April => "Apr",
            datetime::Month::May => "May",
            datetime::Month::June => "Jun",
            datetime::Month::July => "Jul",
            datetime::Month::August => "August",
            datetime::Month::September => "Sep",
            datetime::Month::October => "Oct",
            datetime::Month::November => "Nov",
            datetime::Month::December => "Dec",
        }
    }

    #[allow(trivial_numeric_casts)]
    fn format_local(&self, time: SystemTime) -> String {
        if time == UNIX_EPOCH {
            return "-".to_string();
        }
        let date = LocalDateTime::at(systemtime_epoch(time));

        if self.is_recent(date) {
            format!("{:2} {} {:02}:{:02}",
            date.day(), DefaultFormat::month_to_abbrev(date.month()),
            date.hour(), date.minute())
        }
        else {
            self.date_and_year.format(&date, &self.locale)
        }
    }

    #[allow(trivial_numeric_casts)]
    fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
        if time == UNIX_EPOCH {
            return "-".to_string();
        }

        let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));

        if self.is_recent(date) {
            format!("{:2} {} {:02}:{:02}",
            date.day(), DefaultFormat::month_to_abbrev(date.month()),
            date.hour(), date.minute())
        }
        else {
            self.date_and_year.format(&date, &self.locale)
        }
    }
}

fn systemtime_epoch(time: SystemTime) -> i64 {
    time
        .duration_since(UNIX_EPOCH)
        .map(|t| t.as_secs() as i64)
        .unwrap_or_else(|e| {
            -(e.duration().as_secs() as i64) - e.duration().subsec_nanos().min(1) as i64
        })
}

fn systemtime_nanos(time: SystemTime) -> u32 {
    time
        .duration_since(UNIX_EPOCH)
        .map(|t| t.subsec_nanos())
        .unwrap_or_else(|e| {
            let nanos = e.duration().subsec_nanos();
            if nanos == 0 {
                0
            } else {
                1_000_000_000 - nanos
            }
        })
}

#[allow(trivial_numeric_casts)]
fn long_local(time: SystemTime) -> String {
    let date = LocalDateTime::at(systemtime_epoch(time));
    format!("{:04}-{:02}-{:02} {:02}:{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute())
}

#[allow(trivial_numeric_casts)]
fn long_zoned(time: SystemTime, zone: &TimeZone) -> String {
    let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
    format!("{:04}-{:02}-{:02} {:02}:{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute())
}

#[allow(trivial_numeric_casts)]
fn full_local(time: SystemTime) -> String {
    let date = LocalDateTime::at(systemtime_epoch(time));
    format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute(), date.second(), systemtime_nanos(time))
}

#[allow(trivial_numeric_casts)]
fn full_zoned(time: SystemTime, zone: &TimeZone) -> String {
    use datetime::Offset;

    let local = LocalDateTime::at(systemtime_epoch(time));
    let date = zone.to_zoned(local);
    let offset = Offset::of_seconds(zone.offset(local) as i32).expect("Offset out of range");
    format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09} {:+03}{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute(), date.second(), systemtime_nanos(time),
            offset.hours(), offset.minutes().abs())
}

#[derive(Debug, Clone)]
pub struct ISOFormat {

    /// The year of the current time. This gets used to determine which date
    /// format to use.
    pub current_year: i64,
}

impl ISOFormat {
    pub fn load() -> ISOFormat {
        let current_year = LocalDateTime::now().year();
        ISOFormat { current_year }
    }
}

impl ISOFormat {
    fn is_recent(&self, date: LocalDateTime) -> bool {
        date.year() == self.current_year
    }

    #[allow(trivial_numeric_casts)]
    fn format_local(&self, time: SystemTime) -> String {
        let date = LocalDateTime::at(systemtime_epoch(time));

        if self.is_recent(date) {
            format!("{:02}-{:02} {:02}:{:02}",
                    date.month() as usize, date.day(),
                    date.hour(), date.minute())
        }
        else {
            format!("{:04}-{:02}-{:02}",
                    date.year(), date.month() as usize, date.day())
        }
    }

    #[allow(trivial_numeric_casts)]
    fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
        let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));

        if self.is_recent(date) {
            format!("{:02}-{:02} {:02}:{:02}",
                    date.month() as usize, date.day(),
                    date.hour(), date.minute())
        }
        else {
            format!("{:04}-{:02}-{:02}",
                    date.year(), date.month() as usize, date.day())
        }
    }
}
<commit_msg>Make logic a little clearer<commit_after>//! Timestamp formatting.

use std::time::{SystemTime, UNIX_EPOCH};

use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
use datetime::fmt::DateFormat;
use locale;
use std::cmp;

/// Every timestamp in exa needs to be rendered by a **time format**.
/// Formatting times is tricky, because how a timestamp is rendered can
/// depend on one or more of the following:
///
/// - The user’s locale, for printing the month name as “Feb”, or as “fév”,
///   or as “2月”;
/// - The current year, because certain formats will be less precise when
///   dealing with dates far in the past;
/// - The formatting style that the user asked for on the command-line.
///
/// Because not all formatting styles need the same data, they all have their
/// own enum variants. It’s not worth looking the locale up if the formatter
/// prints month names as numbers.
///
/// Currently exa does not support *custom* styles, where the user enters a
/// format string in an environment variable or something. Just these four.
#[derive(Debug)]
pub enum TimeFormat {

    /// The **default format** uses the user’s locale to print month names,
    /// and specifies the timestamp down to the minute for recent times, and
    /// day for older times.
    DefaultFormat(DefaultFormat),

    /// Use the **ISO format**, which specifies the timestamp down to the
    /// minute for recent times, and day for older times. It uses a number
    /// for the month so it doesn’t need a locale.
    ISOFormat(ISOFormat),

    /// Use the **long ISO format**, which specifies the timestamp down to the
    /// minute using only numbers, without needing the locale or year.
    LongISO,

    /// Use the **full ISO format**, which specifies the timestamp down to the
    /// millisecond and includes its offset down to the minute. This too uses
    /// only numbers so doesn’t require any special consideration.
    FullISO,
}

// There are two different formatting functions because local and zoned
// timestamps are separate types.

impl TimeFormat {
    pub fn format_local(&self, time: SystemTime) -> String {
        match *self {
            TimeFormat::DefaultFormat(ref fmt) => fmt.format_local(time),
            TimeFormat::ISOFormat(ref iso)     => iso.format_local(time),
            TimeFormat::LongISO                => long_local(time),
            TimeFormat::FullISO                => full_local(time),
        }
    }

    pub fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
        match *self {
            TimeFormat::DefaultFormat(ref fmt) => fmt.format_zoned(time, zone),
            TimeFormat::ISOFormat(ref iso)     => iso.format_zoned(time, zone),
            TimeFormat::LongISO                => long_zoned(time, zone),
            TimeFormat::FullISO                => full_zoned(time, zone),
        }
    }
}

#[derive(Debug, Clone)]
pub struct DefaultFormat {

    /// The year of the current time. This gets used to determine which date
    /// format to use.
    pub current_year: i64,

    /// Localisation rules for formatting timestamps.
    pub locale: locale::Time,

    /// Date format for printing out timestamps that are in the current year.
    pub date_and_time: DateFormat<'static>,

    /// Date format for printing out timestamps that *aren’t*.
    pub date_and_year: DateFormat<'static>,
}

impl DefaultFormat {
    pub fn load() -> DefaultFormat {
        use unicode_width::UnicodeWidthStr;

        let locale = locale::Time::load_user_locale()
                       .unwrap_or_else(|_| locale::Time::english());

        let current_year = LocalDateTime::now().year();

        // Some locales use a three-character wide month name (Jan to Dec);
        // others vary between three to four (1月 to 12月, juil.). We check each month width
        // to detect the longest and set the output format accordingly.
        let mut maximum_month_width = 0;
        for i in 0..11 {
            let current_month_width = UnicodeWidthStr::width(&*locale.short_month_name(i));
            maximum_month_width = cmp::max(maximum_month_width, current_month_width);
        }

        let date_and_time = match maximum_month_width {
            4  => DateFormat::parse("{2>:D} {4<:M} {2>:h}:{02>:m}").unwrap(),
            5  => DateFormat::parse("{2>:D} {5<:M} {2>:h}:{02>:m}").unwrap(),
            _  => DateFormat::parse("{2>:D} {:M} {2>:h}:{02>:m}").unwrap(),
        };

        let date_and_year = match maximum_month_width {
            4 => DateFormat::parse("{2>:D} {4<:M} {5>:Y}").unwrap(),
            5 => DateFormat::parse("{2>:D} {5<:M} {5>:Y}").unwrap(),
            _ => DateFormat::parse("{2>:D} {:M} {5>:Y}").unwrap()
        };

        DefaultFormat { current_year, locale, date_and_time, date_and_year }
    }
}

impl DefaultFormat {
    fn is_recent(&self, date: LocalDateTime) -> bool {
        date.year() == self.current_year
    }

    fn month_to_abbrev(month: datetime::Month) -> &'static str {
        match month {
            datetime::Month::January => "Jan",
            datetime::Month::February => "Feb",
            datetime::Month::March => "Mar",
            datetime::Month::April => "Apr",
            datetime::Month::May => "May",
            datetime::Month::June => "Jun",
            datetime::Month::July => "Jul",
            datetime::Month::August => "August",
            datetime::Month::September => "Sep",
            datetime::Month::October => "Oct",
            datetime::Month::November => "Nov",
            datetime::Month::December => "Dec",
        }
    }

    #[allow(trivial_numeric_casts)]
    fn format_local(&self, time: SystemTime) -> String {
        if time == UNIX_EPOCH {
            return "-".to_string();
        }
        let date = LocalDateTime::at(systemtime_epoch(time));

        if self.is_recent(date) {
            format!("{:2} {} {:02}:{:02}",
            date.day(), DefaultFormat::month_to_abbrev(date.month()),
            date.hour(), date.minute())
        }
        else {
            self.date_and_year.format(&date, &self.locale)
        }
    }

    #[allow(trivial_numeric_casts)]
    fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
        if time == UNIX_EPOCH {
            return "-".to_string();
        }

        let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));

        if self.is_recent(date) {
            format!("{:2} {} {:02}:{:02}",
            date.day(), DefaultFormat::month_to_abbrev(date.month()),
            date.hour(), date.minute())
        }
        else {
            self.date_and_year.format(&date, &self.locale)
        }
    }
}

fn systemtime_epoch(time: SystemTime) -> i64 {
    time
        .duration_since(UNIX_EPOCH)
        .map(|t| t.as_secs() as i64)
        .unwrap_or_else(|e| {
            let diff = e.duration();
            let mut secs = diff.as_secs();
            if diff.subsec_nanos() > 0 {
                secs += 1;
            }
            -(secs as i64)
        })
}

fn systemtime_nanos(time: SystemTime) -> u32 {
    time
        .duration_since(UNIX_EPOCH)
        .map(|t| t.subsec_nanos())
        .unwrap_or_else(|e| {
            let nanos = e.duration().subsec_nanos();
            if nanos > 0 {
                1_000_000_000 - nanos
            } else {
                nanos
            }
        })
}

#[allow(trivial_numeric_casts)]
fn long_local(time: SystemTime) -> String {
    let date = LocalDateTime::at(systemtime_epoch(time));
    format!("{:04}-{:02}-{:02} {:02}:{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute())
}

#[allow(trivial_numeric_casts)]
fn long_zoned(time: SystemTime, zone: &TimeZone) -> String {
    let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
    format!("{:04}-{:02}-{:02} {:02}:{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute())
}

#[allow(trivial_numeric_casts)]
fn full_local(time: SystemTime) -> String {
    let date = LocalDateTime::at(systemtime_epoch(time));
    format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute(), date.second(), systemtime_nanos(time))
}

#[allow(trivial_numeric_casts)]
fn full_zoned(time: SystemTime, zone: &TimeZone) -> String {
    use datetime::Offset;

    let local = LocalDateTime::at(systemtime_epoch(time));
    let date = zone.to_zoned(local);
    let offset = Offset::of_seconds(zone.offset(local) as i32).expect("Offset out of range");
    format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09} {:+03}{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute(), date.second(), systemtime_nanos(time),
            offset.hours(), offset.minutes().abs())
}

#[derive(Debug, Clone)]
pub struct ISOFormat {

    /// The year of the current time. This gets used to determine which date
    /// format to use.
    pub current_year: i64,
}

impl ISOFormat {
    pub fn load() -> ISOFormat {
        let current_year = LocalDateTime::now().year();
        ISOFormat { current_year }
    }
}

impl ISOFormat {
    fn is_recent(&self, date: LocalDateTime) -> bool {
        date.year() == self.current_year
    }

    #[allow(trivial_numeric_casts)]
    fn format_local(&self, time: SystemTime) -> String {
        let date = LocalDateTime::at(systemtime_epoch(time));

        if self.is_recent(date) {
            format!("{:02}-{:02} {:02}:{:02}",
                    date.month() as usize, date.day(),
                    date.hour(), date.minute())
        }
        else {
            format!("{:04}-{:02}-{:02}",
                    date.year(), date.month() as usize, date.day())
        }
    }

    #[allow(trivial_numeric_casts)]
    fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
        let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));

        if self.is_recent(date) {
            format!("{:02}-{:02} {:02}:{:02}",
                    date.month() as usize, date.day(),
                    date.hour(), date.minute())
        }
        else {
            format!("{:04}-{:02}-{:02}",
                    date.year(), date.month() as usize, date.day())
        }
    }
}
<|endoftext|>


<commit_before>
use argparse;
use amqp::{ConsumerCallBackFn, Session, Table, Basic, Channel, Options, Consumer};
use amqp::protocol;
use std::default::Default;
use rustc_serialize::json;
use std::slice;

#[derive(Debug, Default, RustcDecodable, RustcEncodable, Clone)]
pub struct ParticipantPort {
    pub id: String, // port name
    pub queue: String, // the associated message queue
    // FIXME: support. Is a keyword so needs some special handling   type: String, // datatype, ex: "boolean"
    // options: queue options as specified by the message queue implementation
    // description?? TODO: standardize
}

#[derive(Debug, Default, RustcDecodable, RustcEncodable, Clone)]
pub struct ParticipantInfo {
    pub id: String, // unique name
    pub role: String ,// role participant has
    pub component: String, // component the participant is instance of
    pub label: Option<String>, // (optional) short human-readable description
    pub icon: Option<String>, // (optional)
    pub inports: Vec<ParticipantPort>,
    pub outports: Vec<ParticipantPort>,
}

pub struct InfoBuilder {
    info: ParticipantInfo
}

impl InfoBuilder {
    pub fn new(component: &str) -> InfoBuilder {
        InfoBuilder {
            info:  ParticipantInfo { component: component.to_string(),  .. Default::default() }
        }
    }

    pub fn label(&mut self, label: &str) -> &mut InfoBuilder {
        self.info.label = Some(label.to_string());
        self
    }

    pub fn role(&mut self, role: &str) -> &mut InfoBuilder {
        self.info.role = role.to_string();
        self
    }

    pub fn inport(&mut self, id: &str) -> &mut InfoBuilder {
        let port = ParticipantPort { id: id.to_string(), queue: "".to_string() };
        self.info.inports.push(port);
        self
    }
    pub fn outport(&mut self, id: &str) -> &mut InfoBuilder {
        let port = ParticipantPort { id: id.to_string(), queue: "".to_string() };
        self.info.outports.push(port);
        self
    }
    pub fn build(&self) -> ParticipantInfo {
        return self.info.clone();
    }
}

type SendFunction = fn(String, Vec<u8>);
pub type ProcessFunction = fn(Vec<u8>) -> Result<Vec<u8>, Vec<u8>>;
pub struct Participant {
    pub info: ParticipantInfo,
    pub process: ProcessFunction,
}

struct Connection {
    session: Session,
    channel: Channel,
}

// FIXME: wrap in protocol/command/payload envelope
fn create_queue_and_send(channel: &mut Channel, queue_name: &str, payload: String) {

    let queue_declare = channel.queue_declare(queue_name, false, true, false, false, false, Table::new());
    queue_declare.expect("queue creation failed");
    let content_type = Some("application/json".to_string());
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };
    debug!("sending on {}: {}", queue_name, payload);
    let res = channel.basic_publish("", queue_name, true, false, props, payload.into_bytes());
    res.expect("send on new queue failed");
}

fn send_discovery(channel: &mut Channel, info: &ParticipantInfo) {
    let queue_name = "fbp"; // TODO: use an exchange istead, requires protocol change in msgflo

    let payload = json::encode(&info).unwrap();
    create_queue_and_send(channel, queue_name, payload);
    info!("sent participant discovery: {}  {}({})", info.id, info.role, info.component);
}

struct PortConsumer {
    process: ProcessFunction,
    portname: String,
    outqueue: String, // FIXME: allow sending on any port, also multiple times
}

fn send_out(channel: &mut Channel, exchange: String, data: Vec<u8>) {

    let routing_key = "".to_string();
    let content_type = Some("application/json".to_string()); // TODO: should be parameter
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };
    let s = channel.basic_publish(exchange, routing_key, true, false, props, data);
    s.expect("failed to send");
    debug!("sent output");
}

impl Consumer for PortConsumer {
    fn handle_delivery(&mut self,
                       channel: &mut Channel,
                       deliver: protocol::basic::Deliver,
                       headers: protocol::basic::BasicProperties,
                       body: Vec<u8>) {

        debug!("calling process()");
        let f = self.process;
        let res = f(body);
        debug!("process() returned");

        if res.is_ok() {
            debug!("ACKing and sending");
            send_out(channel, self.outqueue.to_string(), res.unwrap());
            let r = channel.basic_ack(deliver.delivery_tag, false);
        } else {
            error!("process() errored");
            let r = channel.basic_nack(deliver.delivery_tag, true, true);
        }
        // FIXME: send error data
    }
}

// FIXME: actually call ProcessFunction
fn setup_inport(participant: &Participant, port: &ParticipantPort, connection: &mut Connection) {
    debug!("setup inport: {}", port.queue.to_string());

    let consumer = PortConsumer {
        process: participant.process,
        portname: port.id.to_string(),
        outqueue: participant.info.outports[0].queue.to_string(),
    };

    // create
    let declare = connection.channel.queue_declare(port.queue.to_string(), false, true, false, false, false, Table::new());
    declare.expect("inport queue creation failed");

    // subscribe
    let q = port.queue.to_string();
    let cons = connection.channel.basic_consume(consumer, q,
                                                "".to_string(), false, false, false, false, Table::new());

    cons.expect("iport setup failed");

    debug!("inport setup done: {:?}, {:?}", port.id.to_string(), port.queue.to_string());
}

fn setup_outport(participant: &Participant, port: &ParticipantPort, connection: &mut Connection) {

    let exchange_type = "fanout".to_string();
    let declare = connection.channel.exchange_declare(port.queue.to_string(), exchange_type,
                                                    false, true, false, false, false, Table::new());
    declare.expect("outport setup failed");

    debug!("setup outport done: {:?}, {:?}", port.id.to_string(), port.queue.to_string());
}

fn start_participant(participant: &Participant, options: &ParticipantOptions) -> Connection {

    let mut session = Session::open_url(&options.broker).expect("Can't create AMQP session");
    let mut channel = session.open_channel(1).expect("could not open AMQP channel");

    let mut conn = Connection { session: session, channel: channel };

    // setup ports
    setup_inport(&participant, &participant.info.inports[0], &mut conn);
    setup_outport(&participant, &participant.info.outports[0], &mut conn);

    // send MsgFlo participant discovery message
    send_discovery(&mut conn.channel, &participant.info);

    return conn;
}

fn stop_participant(participant: &Participant, connection: &mut Connection) {

    let closed = connection.channel.close(200, "Bye".to_string());
    connection.session.close(200, "Good Bye".to_string());
}

#[derive(Debug)]
struct ParticipantOptions {
    role: String,
    broker: String,
}

impl Default for ParticipantOptions {
    fn default() -> ParticipantOptions {

        ParticipantOptions {
            broker: "amqp://localhost//".to_string(),
            role: "".to_string(),
        }
    }
}

fn normalize_info(old: &ParticipantInfo, options: &ParticipantOptions) -> ParticipantInfo {
    use rand::{thread_rng, Rng};

    let mut new = old.clone();

    // normalize role name
    if options.role != "" {
        new.role = options.role.to_string();
    }
    if new.role == "" {
        let role_rnd: String = thread_rng().gen_ascii_chars().take(5).collect();
        new.role = format!("msgflo-rust-{}", role_rnd);
    }

    // generate ID
    let id_rnd: String = thread_rng().gen_ascii_chars().take(5).collect();
    new.id = format!("{}-{}", new.role, id_rnd);

    // generate port defaults
    let role = new.role.to_string(); // NOTE: would be nice to be able to pass reference to info?
    let normalize_port = | o: &ParticipantPort | -> ParticipantPort {
        let mut p = o.clone();
        if p.queue == "" {
            p.queue = default_queue(role.to_string(), p.id.to_string());
        }
        return p;
    };
    new.inports = old.inports.iter().map(&normalize_port).collect();
    new.outports = old.outports.iter().map(&normalize_port).collect();

    return new;
}

fn default_queue(role: String, port_name: String) -> String {
    return format!("{}.{}", role, port_name.to_uppercase());
}

fn parse(options: &mut ParticipantOptions) {

    use argparse::{StoreTrue, Store};
    let mut parser = argparse::ArgumentParser::new();

    parser.refer(&mut options.role)
        .add_option(&["--role"], Store, "Participant role name");
    parser.refer(&mut options.broker)
        .add_option(&["--broker"], Store, "Address of messaging broker")
        .envvar("MSGFLO_BROKER");

    parser.parse_args_or_exit(); // XXX: should return out
}

// XXX: seems rust-amqp makes program hangs forever if error occurs / channel is borked?
// TODO: pass port info in/out of process()
// TODO: nicer way to declare ports? ideally they are enums not stringly typed?
pub fn main(orig: Participant) {

    let mut options = ParticipantOptions { .. Default::default() };
    parse(&mut options);

    let info = normalize_info(&orig.info, &options);
    let p = Participant { info: info, process: orig.process }; // XXX: hack

    let mut c = start_participant(&p, &options);
    println!("{}({}) started", &options.role, &p.info.component);
    c.channel.start_consuming();

    stop_participant(&p, &mut c);
}

<commit_msg>participant: Remove prefix on Options<commit_after>
use argparse;
use amqp::{ConsumerCallBackFn, Session, Table, Basic, Channel, Consumer};
use amqp::protocol;
use std::default::Default;
use rustc_serialize::json;
use std::slice;

#[derive(Debug, Default, RustcDecodable, RustcEncodable, Clone)]
pub struct ParticipantPort {
    pub id: String, // port name
    pub queue: String, // the associated message queue
    // FIXME: support. Is a keyword so needs some special handling   type: String, // datatype, ex: "boolean"
    // options: queue options as specified by the message queue implementation
    // description?? TODO: standardize
}

#[derive(Debug, Default, RustcDecodable, RustcEncodable, Clone)]
pub struct ParticipantInfo {
    pub id: String, // unique name
    pub role: String ,// role participant has
    pub component: String, // component the participant is instance of
    pub label: Option<String>, // (optional) short human-readable description
    pub icon: Option<String>, // (optional)
    pub inports: Vec<ParticipantPort>,
    pub outports: Vec<ParticipantPort>,
}

pub struct InfoBuilder {
    info: ParticipantInfo
}

impl InfoBuilder {
    pub fn new(component: &str) -> InfoBuilder {
        InfoBuilder {
            info:  ParticipantInfo { component: component.to_string(),  .. Default::default() }
        }
    }

    pub fn label(&mut self, label: &str) -> &mut InfoBuilder {
        self.info.label = Some(label.to_string());
        self
    }

    pub fn role(&mut self, role: &str) -> &mut InfoBuilder {
        self.info.role = role.to_string();
        self
    }

    pub fn inport(&mut self, id: &str) -> &mut InfoBuilder {
        let port = ParticipantPort { id: id.to_string(), queue: "".to_string() };
        self.info.inports.push(port);
        self
    }
    pub fn outport(&mut self, id: &str) -> &mut InfoBuilder {
        let port = ParticipantPort { id: id.to_string(), queue: "".to_string() };
        self.info.outports.push(port);
        self
    }
    pub fn build(&self) -> ParticipantInfo {
        return self.info.clone();
    }
}

type SendFunction = fn(String, Vec<u8>);
pub type ProcessFunction = fn(Vec<u8>) -> Result<Vec<u8>, Vec<u8>>;
pub struct Participant {
    pub info: ParticipantInfo,
    pub process: ProcessFunction,
}

struct Connection {
    session: Session,
    channel: Channel,
}

// FIXME: wrap in protocol/command/payload envelope
fn create_queue_and_send(channel: &mut Channel, queue_name: &str, payload: String) {

    let queue_declare = channel.queue_declare(queue_name, false, true, false, false, false, Table::new());
    queue_declare.expect("queue creation failed");
    let content_type = Some("application/json".to_string());
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };
    debug!("sending on {}: {}", queue_name, payload);
    let res = channel.basic_publish("", queue_name, true, false, props, payload.into_bytes());
    res.expect("send on new queue failed");
}

fn send_discovery(channel: &mut Channel, info: &ParticipantInfo) {
    let queue_name = "fbp"; // TODO: use an exchange istead, requires protocol change in msgflo

    let payload = json::encode(&info).unwrap();
    create_queue_and_send(channel, queue_name, payload);
    info!("sent participant discovery: {}  {}({})", info.id, info.role, info.component);
}

struct PortConsumer {
    process: ProcessFunction,
    portname: String,
    outqueue: String, // FIXME: allow sending on any port, also multiple times
}

fn send_out(channel: &mut Channel, exchange: String, data: Vec<u8>) {

    let routing_key = "".to_string();
    let content_type = Some("application/json".to_string()); // TODO: should be parameter
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };
    let s = channel.basic_publish(exchange, routing_key, true, false, props, data);
    s.expect("failed to send");
    debug!("sent output");
}

impl Consumer for PortConsumer {
    fn handle_delivery(&mut self,
                       channel: &mut Channel,
                       deliver: protocol::basic::Deliver,
                       headers: protocol::basic::BasicProperties,
                       body: Vec<u8>) {

        debug!("calling process()");
        let f = self.process;
        let res = f(body);
        debug!("process() returned");

        if res.is_ok() {
            debug!("ACKing and sending");
            send_out(channel, self.outqueue.to_string(), res.unwrap());
            let r = channel.basic_ack(deliver.delivery_tag, false);
        } else {
            error!("process() errored");
            let r = channel.basic_nack(deliver.delivery_tag, true, true);
        }
        // FIXME: send error data
    }
}

// FIXME: actually call ProcessFunction
fn setup_inport(participant: &Participant, port: &ParticipantPort, connection: &mut Connection) {
    debug!("setup inport: {}", port.queue.to_string());

    let consumer = PortConsumer {
        process: participant.process,
        portname: port.id.to_string(),
        outqueue: participant.info.outports[0].queue.to_string(),
    };

    // create
    let declare = connection.channel.queue_declare(port.queue.to_string(), false, true, false, false, false, Table::new());
    declare.expect("inport queue creation failed");

    // subscribe
    let q = port.queue.to_string();
    let cons = connection.channel.basic_consume(consumer, q,
                                                "".to_string(), false, false, false, false, Table::new());

    cons.expect("iport setup failed");

    debug!("inport setup done: {:?}, {:?}", port.id.to_string(), port.queue.to_string());
}

fn setup_outport(participant: &Participant, port: &ParticipantPort, connection: &mut Connection) {

    let exchange_type = "fanout".to_string();
    let declare = connection.channel.exchange_declare(port.queue.to_string(), exchange_type,
                                                    false, true, false, false, false, Table::new());
    declare.expect("outport setup failed");

    debug!("setup outport done: {:?}, {:?}", port.id.to_string(), port.queue.to_string());
}

fn start_participant(participant: &Participant, options: &Options) -> Connection {

    let mut session = Session::open_url(&options.broker).expect("Can't create AMQP session");
    let mut channel = session.open_channel(1).expect("could not open AMQP channel");

    let mut conn = Connection { session: session, channel: channel };

    // setup ports
    setup_inport(&participant, &participant.info.inports[0], &mut conn);
    setup_outport(&participant, &participant.info.outports[0], &mut conn);

    // send MsgFlo participant discovery message
    send_discovery(&mut conn.channel, &participant.info);

    return conn;
}

fn stop_participant(participant: &Participant, connection: &mut Connection) {

    let closed = connection.channel.close(200, "Bye".to_string());
    connection.session.close(200, "Good Bye".to_string());
}

#[derive(Debug)]
struct Options {
    role: String,
    broker: String,
}

impl Default for Options {
    fn default() -> Options {

        Options {
            broker: "amqp://localhost//".to_string(),
            role: "".to_string(),
        }
    }
}

fn normalize_info(old: &ParticipantInfo, options: &Options) -> ParticipantInfo {
    use rand::{thread_rng, Rng};

    let mut new = old.clone();

    // normalize role name
    if options.role != "" {
        new.role = options.role.to_string();
    }
    if new.role == "" {
        let role_rnd: String = thread_rng().gen_ascii_chars().take(5).collect();
        new.role = format!("msgflo-rust-{}", role_rnd);
    }

    // generate ID
    let id_rnd: String = thread_rng().gen_ascii_chars().take(5).collect();
    new.id = format!("{}-{}", new.role, id_rnd);

    // generate port defaults
    let role = new.role.to_string(); // NOTE: would be nice to be able to pass reference to info?
    let normalize_port = | o: &ParticipantPort | -> ParticipantPort {
        let mut p = o.clone();
        if p.queue == "" {
            p.queue = default_queue(role.to_string(), p.id.to_string());
        }
        return p;
    };
    new.inports = old.inports.iter().map(&normalize_port).collect();
    new.outports = old.outports.iter().map(&normalize_port).collect();

    return new;
}

fn default_queue(role: String, port_name: String) -> String {
    return format!("{}.{}", role, port_name.to_uppercase());
}

fn parse(options: &mut Options) {

    use argparse::{StoreTrue, Store};
    let mut parser = argparse::ArgumentParser::new();

    parser.refer(&mut options.role)
        .add_option(&["--role"], Store, "Participant role name");
    parser.refer(&mut options.broker)
        .add_option(&["--broker"], Store, "Address of messaging broker")
        .envvar("MSGFLO_BROKER");

    parser.parse_args_or_exit(); // XXX: should return out
}

// XXX: seems rust-amqp makes program hangs forever if error occurs / channel is borked?
// TODO: pass port info in/out of process()
// TODO: nicer way to declare ports? ideally they are enums not stringly typed?
pub fn main(orig: Participant) {

    let mut options = Options { .. Default::default() };
    parse(&mut options);

    let info = normalize_info(&orig.info, &options);
    let p = Participant { info: info, process: orig.process }; // XXX: hack

    let mut c = start_participant(&p, &options);
    println!("{}({}) started", &options.role, &p.info.component);
    c.channel.start_consuming();

    stop_participant(&p, &mut c);
}

<|endoftext|>


<commit_before>use union_find::{UnionFind, WeightedQuickUnion};

pub trait Percolation {
    fn open(&mut self, row: usize, col: usize);

    fn is_open(&self, row: usize, col: usize) -> bool;

    fn is_full(&self, row: usize, col: usize) -> bool;

    fn percolates(&self) -> bool;
}

trait PercolationExt: Percolation {

    fn cell_index(&self, row: usize, col: usize) -> usize;

    fn fill_neighbors(&mut self, row: usize, col: usize);
}

pub struct BruteForcePercolation {
    openness: Vec<bool>,
    fullness: Vec<bool>,
    side_size: usize
}

impl BruteForcePercolation {

    pub fn new(side_size: usize) -> BruteForcePercolation {
        let length = side_size * side_size;
        let mut openness = Vec::with_capacity(length);
        let mut fullness = Vec::with_capacity(length);
        for _ in 0..length {
            openness.push(false);
            fullness.push(false);
        }
        BruteForcePercolation {
            openness: openness,
            fullness: fullness,
            side_size: side_size
        }
    }
}

impl PercolationExt for BruteForcePercolation {

    #[inline]
    fn cell_index(&self, row: usize, col: usize) -> usize {
        (row - 1) * self.side_size + col - 1
    }

    fn fill_neighbors(&mut self, row: usize, col: usize) {
        if row < self.side_size && self.is_open(row + 1, col) && !self.is_full(row + 1, col) {
            let index = self.cell_index(row + 1, col);
            self.fullness[index] = true;
            self.fill_neighbors(row + 1, col);
        }
        if col < self.side_size && self.is_open(row, col + 1) && !self.is_full(row, col + 1) {
            let index = self.cell_index(row, col + 1);
            self.fullness[index] = true;
            self.fill_neighbors(row, col + 1);
        }
        if col > 1 && self.is_open(row, col - 1) && !self.is_full(row, col - 1) {
            let index = self.cell_index(row, col - 1);
            self.fullness[index] = true;
            self.fill_neighbors(row, col - 1);
        }
        if row > 1 && self.is_open(row - 1, col) && !self.is_full(row - 1, col) {
            let index = self.cell_index(row - 1, col);
            self.fullness[index] = true;
            self.fill_neighbors(row - 1, col);
        }
    }
}

impl Percolation for BruteForcePercolation {
    fn open(&mut self, row: usize, col: usize) {
        if !self.is_open(row, col) {
            let index = self.cell_index(row, col);
            self.openness[index] = true;
            if row > 1 && self.is_full(row - 1, col)
                    || row < self.side_size && self.is_full(row + 1, col) {
                self.fullness[index] = true;
                self.fill_neighbors(row, col);
            }
            if col > 1 && self.is_full(row, col - 1)
                    || col < self.side_size && self.is_full(row, col + 1){
                self.fullness[index] = true;
                self.fill_neighbors(row, col);
            }
            if row == 1 {
                self.fullness[index] = true;
                self.fill_neighbors(row, col);
            }
        }
    }

    fn is_open(&self, row: usize, col: usize) -> bool {
        let index = self.cell_index(row, col);
        self.openness[index]
    }

    fn is_full(&self, row: usize, col: usize) -> bool {
        let index = self.cell_index(row, col);
        self.fullness[index]
    }

    fn percolates(&self) -> bool {
        for col in 1..self.side_size + 1 {
            if self.is_full(self.side_size, col) {
                return true;
            }
        }
        false
    }
}

pub struct UnionFindPercolation {
    union_find: WeightedQuickUnion,
    openness: Vec<bool>,
    fullness: Vec<bool>,
    side_size: usize
}

impl UnionFindPercolation {

    pub fn new(side_size: usize) -> UnionFindPercolation {
        let length = side_size * side_size;
        let mut openness = Vec::with_capacity(length);
        let mut fullness = Vec::with_capacity(length);
        for _ in 0..length {
            openness.push(false);
            fullness.push(false);
        }
        UnionFindPercolation {
            union_find: WeightedQuickUnion::new(length + 1),
            openness: openness,
            fullness: fullness,
            side_size: side_size
        }
    }

    #[inline]
    fn is_open_by_index(&self, index: usize) -> bool {
        self.openness[index]
    }

    #[inline]
    fn is_full_by_index(&self, index: usize) -> bool {
        self.fullness[index]
    }

    fn connect_with_top(&mut self, index: usize) -> bool {
        if index <= self.side_size - 1 {
            self.union_find.union(index + 1, 0);
            true
        } else if self.has_top_neighbor(index) {
            let upper_index = index - self.side_size;
            self.union_find.union(index + 1, upper_index + 1);
            true
        }
        else {
            false
        }
    }

    #[inline]
    fn has_top_neighbor(&self, index: usize) -> bool {
        index >= self.side_size
    }

    fn connect_with_left(&mut self, index: usize) -> bool {
        if self.has_left_neighbor(index) {
            let left_index = index - 1;
            self.union_find.union(index + 1, left_index + 1);
            true
        }
        else {
            false
        }
    }

    #[inline]
    fn has_left_neighbor(&self, index: usize) -> bool {
        index % self.side_size > 0
    }

    fn connect_with_right(&mut self, index: usize) -> bool {
        if self.has_right_neighbor(index) {
            let right_index = index + 1;
            self.union_find.union(index + 1, right_index + 1);
            true
        }
        else {
            false
        }
    }

    #[inline]
    fn has_right_neighbor(&self, index: usize) -> bool {
        index % self.side_size < self.side_size - 1
    }

    fn connect_with_bottom(&mut self, index: usize) -> bool {
        if self.has_bottom_neighbor(index) {
            let bottom_index = index + self.side_size;
            self.union_find.union(index + 1, bottom_index + 1);
            true
        }
        else {
            false
        }
    }

    #[inline]
    fn has_bottom_neighbor(&self, index: usize) -> bool {
        index < self.side_size * (self.side_size - 1)
    }
}

impl Percolation for UnionFindPercolation {

    fn open(&mut self, row: usize, col: usize) {
        if !self.is_open(row, col) {
            let index = self.cell_index(row, col);
            self.openness[index] = true;
            let union = self.connect_with_top(index)
                | self.connect_with_left(index)
                | self.connect_with_right(index)
                | self.connect_with_bottom(index);
            if union && self.union_find.connected(index + 1, 0) {
                self.fullness[index] = true;
                self.fill_neighbors(row, col);
            }
        }
    }

    fn is_open(&self, row: usize, col: usize) -> bool {
        let index = self.cell_index(row, col);
        self.is_open_by_index(index)
    }

    fn is_full(&self, row: usize, col: usize) -> bool {
        let index = self.cell_index(row, col);
        self.is_full_by_index(index)
    }

    fn percolates(&self) -> bool {
        for col in 1..self.side_size + 1 {
            if self.is_full(self.side_size, col) {
                return true;
            }
        }
        false
    }
}

impl PercolationExt for UnionFindPercolation {

    #[inline]
    fn cell_index(&self, row: usize, col: usize) -> usize {
        (row - 1) * self.side_size + col - 1
    }

    fn fill_neighbors(&mut self, row: usize, col: usize) {
        if row < self.side_size && self.is_open(row + 1, col) && !self.is_full(row + 1, col) {
            let index = self.cell_index(row + 1, col);
            self.fullness[index] = true;
            self.fill_neighbors(row + 1, col);
        }
        if col < self.side_size && self.is_open(row, col + 1) && !self.is_full(row, col + 1) {
            let index = self.cell_index(row, col + 1);
            self.fullness[index] = true;
            self.fill_neighbors(row, col + 1);
        }
        if col > 1 && self.is_open(row, col - 1) && !self.is_full(row, col - 1) {
            let index = self.cell_index(row, col - 1);
            self.fullness[index] = true;
            self.fill_neighbors(row, col - 1);
        }
        if row > 1 && self.is_open(row - 1, col) && !self.is_full(row - 1, col) {
            let index = self.cell_index(row - 1, col);
            self.fullness[index] = true;
            self.fill_neighbors(row - 1, col);
        }
    }
}
<commit_msg>union find percolation performance optimisation<commit_after>use union_find::{UnionFind, PathCompressionWeightedQuickUnion};

pub trait Percolation {
    fn open(&mut self, row: usize, col: usize);

    fn is_open(&self, row: usize, col: usize) -> bool;

    fn is_full(&self, row: usize, col: usize) -> bool;

    fn percolates(&self) -> bool;
}

trait PercolationExt: Percolation {

    fn cell_index(&self, row: usize, col: usize) -> usize;

    fn fill_neighbors(&mut self, row: usize, col: usize);
}

pub struct BruteForcePercolation {
    openness: Vec<bool>,
    fullness: Vec<bool>,
    side_size: usize
}

impl BruteForcePercolation {

    pub fn new(side_size: usize) -> BruteForcePercolation {
        let length = side_size * side_size;
        let mut openness = Vec::with_capacity(length);
        let mut fullness = Vec::with_capacity(length);
        for _ in 0..length {
            openness.push(false);
            fullness.push(false);
        }
        BruteForcePercolation {
            openness: openness,
            fullness: fullness,
            side_size: side_size
        }
    }
}

impl PercolationExt for BruteForcePercolation {

    #[inline]
    fn cell_index(&self, row: usize, col: usize) -> usize {
        (row - 1) * self.side_size + col - 1
    }

    fn fill_neighbors(&mut self, row: usize, col: usize) {
        if row < self.side_size && self.is_open(row + 1, col) && !self.is_full(row + 1, col) {
            let index = self.cell_index(row + 1, col);
            self.fullness[index] = true;
            self.fill_neighbors(row + 1, col);
        }
        if col < self.side_size && self.is_open(row, col + 1) && !self.is_full(row, col + 1) {
            let index = self.cell_index(row, col + 1);
            self.fullness[index] = true;
            self.fill_neighbors(row, col + 1);
        }
        if col > 1 && self.is_open(row, col - 1) && !self.is_full(row, col - 1) {
            let index = self.cell_index(row, col - 1);
            self.fullness[index] = true;
            self.fill_neighbors(row, col - 1);
        }
        if row > 1 && self.is_open(row - 1, col) && !self.is_full(row - 1, col) {
            let index = self.cell_index(row - 1, col);
            self.fullness[index] = true;
            self.fill_neighbors(row - 1, col);
        }
    }
}

impl Percolation for BruteForcePercolation {
    fn open(&mut self, row: usize, col: usize) {
        if !self.is_open(row, col) {
            let index = self.cell_index(row, col);
            self.openness[index] = true;
            if row > 1 && self.is_full(row - 1, col)
                    || row < self.side_size && self.is_full(row + 1, col) {
                self.fullness[index] = true;
                self.fill_neighbors(row, col);
            }
            if col > 1 && self.is_full(row, col - 1)
                    || col < self.side_size && self.is_full(row, col + 1){
                self.fullness[index] = true;
                self.fill_neighbors(row, col);
            }
            if row == 1 {
                self.fullness[index] = true;
                self.fill_neighbors(row, col);
            }
        }
    }

    fn is_open(&self, row: usize, col: usize) -> bool {
        let index = self.cell_index(row, col);
        self.openness[index]
    }

    fn is_full(&self, row: usize, col: usize) -> bool {
        let index = self.cell_index(row, col);
        self.fullness[index]
    }

    fn percolates(&self) -> bool {
        for col in 1..self.side_size + 1 {
            if self.is_full(self.side_size, col) {
                return true;
            }
        }
        false
    }
}

pub struct UnionFindPercolation {
    union_find: PathCompressionWeightedQuickUnion,
    virtual_cell: usize,
    openness: Vec<bool>,
    fullness: Vec<bool>,
    side_size: usize
}

impl UnionFindPercolation {

    pub fn new(side_size: usize) -> UnionFindPercolation {
        let length = side_size * side_size;
        let mut openness = Vec::with_capacity(length);
        let mut fullness = Vec::with_capacity(length);
        for _ in 0..length {
            openness.push(false);
            fullness.push(false);
        }
        let virtual_cell = length;
        let mut union_find = PathCompressionWeightedQuickUnion::new(length + 1);
        for p in 0..side_size {
            union_find.union(p, virtual_cell);
        }
        UnionFindPercolation {
            union_find: union_find,
            virtual_cell: virtual_cell,
            openness: openness,
            fullness: fullness,
            side_size: side_size
        }
    }

    #[inline]
    fn is_open_by_index(&self, index: usize) -> bool {
        self.openness[index]
    }

    #[inline]
    fn is_full_by_index(&self, index: usize) -> bool {
        self.fullness[index]
    }

    fn connect_with_top(&mut self, index: usize) -> bool {
        if self.has_top_neighbor(index) {
            let upper_index = index - self.side_size;
            self.union_find.union(index, upper_index);
            self.is_open_by_index(upper_index)
        }
        else {
            false
        }
    }

    #[inline]
    fn has_top_neighbor(&self, index: usize) -> bool {
        index >= self.side_size
    }

    fn connect_with_left(&mut self, index: usize) -> bool {
        if self.has_left_neighbor(index) {
            let left_index = index - 1;
            self.union_find.union(index, left_index);
            self.is_open_by_index(left_index)
        }
        else {
            false
        }
    }

    #[inline]
    fn has_left_neighbor(&self, index: usize) -> bool {
        index % self.side_size > 0
    }

    fn connect_with_right(&mut self, index: usize) -> bool {
        if self.has_right_neighbor(index) {
            let right_index = index + 1;
            self.union_find.union(index, right_index);
            self.is_open_by_index(right_index)
        }
        else {
            false
        }
    }

    #[inline]
    fn has_right_neighbor(&self, index: usize) -> bool {
        index % self.side_size < self.side_size - 1
    }

    fn connect_with_bottom(&mut self, index: usize) -> bool {
        if self.has_bottom_neighbor(index) {
            let bottom_index = index + self.side_size;
            self.union_find.union(index, bottom_index);
            self.is_open_by_index(bottom_index)
        }
        else {
            false
        }
    }

    #[inline]
    fn has_bottom_neighbor(&self, index: usize) -> bool {
        index < self.side_size * (self.side_size - 1)
    }
}

impl Percolation for UnionFindPercolation {

    fn open(&mut self, row: usize, col: usize) {
        if !self.is_open(row, col) {
            let index = self.cell_index(row, col);
            self.openness[index] = true;
            let union = self.connect_with_top(index)
                | self.connect_with_left(index)
                | self.connect_with_right(index)
                | self.connect_with_bottom(index);
            if (union || row == 1) && self.union_find.connected(index, self.virtual_cell) {
                self.fullness[index] = true;
                self.fill_neighbors(row, col);
            }
        }
    }

    fn is_open(&self, row: usize, col: usize) -> bool {
        let index = self.cell_index(row, col);
        self.is_open_by_index(index)
    }

    fn is_full(&self, row: usize, col: usize) -> bool {
        let index = self.cell_index(row, col);
        self.is_full_by_index(index)
    }

    fn percolates(&self) -> bool {
        for col in 1..self.side_size + 1 {
            if self.is_full(self.side_size, col) {
                return true;
            }
        }
        false
    }
}

impl PercolationExt for UnionFindPercolation {

    #[inline]
    fn cell_index(&self, row: usize, col: usize) -> usize {
        (row - 1) * self.side_size + col - 1
    }

    fn fill_neighbors(&mut self, row: usize, col: usize) {
        if row < self.side_size && self.is_open(row + 1, col) && !self.is_full(row + 1, col) {
            let index = self.cell_index(row + 1, col);
            self.fullness[index] = true;
            self.fill_neighbors(row + 1, col);
        }
        if col < self.side_size && self.is_open(row, col + 1) && !self.is_full(row, col + 1) {
            let index = self.cell_index(row, col + 1);
            self.fullness[index] = true;
            self.fill_neighbors(row, col + 1);
        }
        if col > 1 && self.is_open(row, col - 1) && !self.is_full(row, col - 1) {
            let index = self.cell_index(row, col - 1);
            self.fullness[index] = true;
            self.fill_neighbors(row, col - 1);
        }
        if row > 1 && self.is_open(row - 1, col) && !self.is_full(row - 1, col) {
            let index = self.cell_index(row - 1, col);
            self.fullness[index] = true;
            self.fill_neighbors(row - 1, col);
        }
    }
}
<|endoftext|>


<commit_before>
use std::marker::PhantomData;
use std::default::Default;
use std::ops::{AddAssign, MulAssign};

use num::BigUint;

use Construct;
use Count;
use Data;
use Of;
use ToPos;
use ToIndex;
use Zero;

/// Dimension is natural number, position is a list of numbers.
pub struct Permutation<T = Data>(PhantomData<T>);

impl<T> Construct for Permutation<T> {
    fn new() -> Permutation<T> {
        Permutation(PhantomData)
    }
}

impl Count<usize> for Permutation<Data> {
    type N = usize;
    fn count(&self, dim: &usize) -> usize {
        let mut res = 1;
        for x in 1..dim + 1 {
            res *= x;
        }
        res
    }
}

impl Count<BigUint> for Permutation<Data> {
    type N = BigUint;
    fn count(&self, dim: &BigUint) -> BigUint {
        let _1: BigUint = 1usize.into();
        let mut res: BigUint = _1.clone();
        let mut x = _1.clone();
        loop {
            if &x > dim {break}
            res *= &x;
            x += &_1;
        }
        res
    }
}

impl<T, U> Count<U> for Permutation<Of<T>>
    where
        T: Construct + Count<U>,
        for<'a> <T as Count<U>>::N: From<usize> + Clone + Ord +
            AddAssign<&'a <T as Count<U>>::N> +
            MulAssign<&'a <T as Count<U>>::N>
{
    type N = <T as Count<U>>::N;
    fn count(&self, dim: &U) -> Self::N {
        let of: T = Construct::new();
        let _1: Self::N = 1usize.into();
        let mut x = _1.clone();
        let mut res = _1.clone();
        let of_count = of.count(dim);
        loop {
            if &x > &of_count {break}
            res *= &x;
            x += &_1;
        }
        res
    }
}

impl Zero<usize, Vec<usize>> for Permutation<Data> {
    fn zero(&self, dim: &usize) -> Vec<usize> {
        vec![0; *dim]
    }
}

impl Zero<BigUint, Vec<BigUint>> for Permutation<Data> {
    fn zero(&self, dim: &BigUint) -> Vec<BigUint> {
        use std::convert::TryInto;

        let dim: usize = dim.try_into().unwrap();
        vec![0usize.into(); dim]
    }
}

impl<T, U, V> Zero<U, Vec<V>> for Permutation<Of<T>>
    where
        T: Construct + Count<U, N = usize> + Zero<U, V>,
        V: Default + Clone
{
    fn zero(&self, dim: &U) -> Vec<V> {
        let of: T = Construct::new();
        vec![of.zero(dim); of.count(dim)]
    }
}

impl ToIndex<usize, Vec<usize>> for Permutation<Data> {
    fn to_index(&self, dim: &usize, pos: &Vec<usize>) -> usize {
        let mut index = 0;
        let mut count = 1;
        for (i, &x) in pos.iter().enumerate().rev() {
            let lower = pos[..i].iter().filter(|&&y| y < x).count();
            index += count * (x - lower);
            count *= dim - i;
        }
        index
    }
}

impl<T, U, V> ToIndex<U, Vec<V>> for Permutation<Of<T>>
    where
        T: Construct + ToIndex<U, V> + Count<U, N = usize>,
        V: Clone
{
    fn to_index(&self, dim: &U, pos: &Vec<V>) -> usize {
        let of: T = Construct::new();
        let mut index = 0;
        let dim_count = of.count(dim);
        let mut count = 1;
        for (i, x) in pos.iter()
            .map(|x| of.to_index(dim, x))
            .enumerate().rev() {
            let lower = pos[..i].iter()
                .map(|y| of.to_index(dim, y))
                .filter(|&y| y < x).count();
            index += count * (x - lower);
            count *= dim_count - i;
        }
        index
    }
}

impl ToPos<usize, Vec<usize>> for Permutation<Data> {
    fn to_pos(&self, dim: &usize, mut index: usize, pos: &mut Vec<usize>) {
        unsafe { pos.set_len(0); }

        let mut count = 1;
        for (j, x) in (1..dim + 1).enumerate() {
            count *= x;
            pos.push(j);
        }

        for i in 0..*dim {
            let block = count / (dim - i);
            let ind = index / block;
            let item = pos.remove(ind);
            pos.push(item);
            count /= dim - i;
            index -= ind * block;
        }
    }
}

impl<T, U, V> ToPos<U, Vec<V>> for Permutation<Of<T>>
    where
        T: Construct + Count<U, N = usize> + ToPos<U, V>,
        V: Default
{
    fn to_pos(&self, dim: &U, mut index: usize, pos: &mut Vec<V>) {
        let of: T = Construct::new();
        let of_count = of.count(dim);
        pos.clear();

        let mut count = 1;
        for (j, x) in (1..of_count + 1).enumerate() {
            count *= x;
            let mut new_pos: V = Default::default();
            of.to_pos(dim, j, &mut new_pos);
            pos.push(new_pos);
        }

        for i in 0..of_count {
            let block = count / (of_count - i);
            let ind = index / block;
            let item = pos.remove(ind);
            pos.push(item);
            count /= of_count - i;
            index -= ind * block;
        }
    }
}

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

    #[test]
    fn features() {
        is_complete::<Permutation, usize, Vec<usize>>();
        is_complete::<Permutation<Of<Pair>>, usize, Vec<(usize, usize)>>();
    }

    #[test]
    fn data() {
        let permutation: Permutation = Construct::new();
        assert_eq!(permutation.count(&1), 1);
        assert_eq!(permutation.count(&2), 2);
        assert_eq!(permutation.count(&3), 6);
        assert_eq!(permutation.count(&4), 24);

        let mut pos = Vec::new();
        let ref dim = 4;
        let count = permutation.count(dim);
        for i in 0..count {
            permutation.to_pos(dim, i, &mut pos);
            let index = permutation.to_index(dim, &pos);
            assert_eq!(index, i);
        }
    }

    #[test]
    fn of() {
        let space: Permutation<Of<Pair>> = Construct::new();
        let ref dim = 3;
        let count = space.count(dim);
        let mut pos = Vec::new();
        for i in 0..count {
            space.to_pos(dim, i, &mut pos);
            let index = space.to_index(dim, &pos);
            assert_eq!(index, i);
        }
    }
}
<commit_msg>Fixed impl<commit_after>
use std::marker::PhantomData;
use std::default::Default;
use std::ops::{AddAssign, MulAssign};
use std::convert::TryInto;

use num::BigUint;

use Construct;
use Count;
use Data;
use Of;
use ToPos;
use ToIndex;
use Zero;

/// Dimension is natural number, position is a list of numbers.
pub struct Permutation<T = Data>(PhantomData<T>);

impl<T> Construct for Permutation<T> {
    fn new() -> Permutation<T> {
        Permutation(PhantomData)
    }
}

impl Count<usize> for Permutation<Data> {
    type N = usize;
    fn count(&self, dim: &usize) -> usize {
        let mut res = 1;
        for x in 1..dim + 1 {
            res *= x;
        }
        res
    }
}

impl Count<BigUint> for Permutation<Data> {
    type N = BigUint;
    fn count(&self, dim: &BigUint) -> BigUint {
        let _1: BigUint = 1usize.into();
        let mut res: BigUint = _1.clone();
        let mut x = _1.clone();
        loop {
            if &x > dim {break}
            res *= &x;
            x += &_1;
        }
        res
    }
}

impl<T, U> Count<U> for Permutation<Of<T>>
    where
        T: Construct + Count<U>,
        for<'a> <T as Count<U>>::N: From<usize> + Clone + Ord +
            AddAssign<&'a <T as Count<U>>::N> +
            MulAssign<&'a <T as Count<U>>::N>
{
    type N = <T as Count<U>>::N;
    fn count(&self, dim: &U) -> Self::N {
        let of: T = Construct::new();
        let _1: Self::N = 1usize.into();
        let mut x = _1.clone();
        let mut res = _1.clone();
        let of_count = of.count(dim);
        loop {
            if &x > &of_count {break}
            res *= &x;
            x += &_1;
        }
        res
    }
}

impl Zero<usize, Vec<usize>> for Permutation<Data> {
    fn zero(&self, dim: &usize) -> Vec<usize> {
        vec![0; *dim]
    }
}

impl Zero<BigUint, Vec<BigUint>> for Permutation<Data> {
    fn zero(&self, dim: &BigUint) -> Vec<BigUint> {
        let dim: usize = dim.try_into().unwrap();
        vec![0usize.into(); dim]
    }
}

impl<T, U, V> Zero<U, Vec<V>> for Permutation<Of<T>>
    where
        T: Construct + Count<U> + Zero<U, V>,
        <T as Count<U>>::N: TryInto<usize>,
        V: Clone
{
    fn zero(&self, dim: &U) -> Vec<V> {
        let of: T = Construct::new();
        let count = match of.count(dim).try_into() {
            Ok(x) => x,
            Err(_) => panic!("Out of range"),
        };
        vec![of.zero(dim); count]
    }
}

impl ToIndex<usize, Vec<usize>> for Permutation<Data> {
    fn to_index(&self, dim: &usize, pos: &Vec<usize>) -> usize {
        let mut index = 0;
        let mut count = 1;
        for (i, &x) in pos.iter().enumerate().rev() {
            let lower = pos[..i].iter().filter(|&&y| y < x).count();
            index += count * (x - lower);
            count *= dim - i;
        }
        index
    }
}

impl<T, U, V> ToIndex<U, Vec<V>> for Permutation<Of<T>>
    where
        T: Construct + ToIndex<U, V> + Count<U, N = usize>,
        V: Clone
{
    fn to_index(&self, dim: &U, pos: &Vec<V>) -> usize {
        let of: T = Construct::new();
        let mut index = 0;
        let dim_count = of.count(dim);
        let mut count = 1;
        for (i, x) in pos.iter()
            .map(|x| of.to_index(dim, x))
            .enumerate().rev() {
            let lower = pos[..i].iter()
                .map(|y| of.to_index(dim, y))
                .filter(|&y| y < x).count();
            index += count * (x - lower);
            count *= dim_count - i;
        }
        index
    }
}

impl ToPos<usize, Vec<usize>> for Permutation<Data> {
    fn to_pos(&self, dim: &usize, mut index: usize, pos: &mut Vec<usize>) {
        unsafe { pos.set_len(0); }

        let mut count = 1;
        for (j, x) in (1..dim + 1).enumerate() {
            count *= x;
            pos.push(j);
        }

        for i in 0..*dim {
            let block = count / (dim - i);
            let ind = index / block;
            let item = pos.remove(ind);
            pos.push(item);
            count /= dim - i;
            index -= ind * block;
        }
    }
}

impl<T, U, V> ToPos<U, Vec<V>> for Permutation<Of<T>>
    where
        T: Construct + Count<U, N = usize> + ToPos<U, V>,
        V: Default
{
    fn to_pos(&self, dim: &U, mut index: usize, pos: &mut Vec<V>) {
        let of: T = Construct::new();
        let of_count = of.count(dim);
        pos.clear();

        let mut count = 1;
        for (j, x) in (1..of_count + 1).enumerate() {
            count *= x;
            let mut new_pos: V = Default::default();
            of.to_pos(dim, j, &mut new_pos);
            pos.push(new_pos);
        }

        for i in 0..of_count {
            let block = count / (of_count - i);
            let ind = index / block;
            let item = pos.remove(ind);
            pos.push(item);
            count /= of_count - i;
            index -= ind * block;
        }
    }
}

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

    #[test]
    fn features() {
        is_complete::<Permutation, usize, Vec<usize>>();
        is_complete::<Permutation<Of<Pair>>, usize, Vec<(usize, usize)>>();
    }

    #[test]
    fn data() {
        let permutation: Permutation = Construct::new();
        assert_eq!(permutation.count(&1), 1);
        assert_eq!(permutation.count(&2), 2);
        assert_eq!(permutation.count(&3), 6);
        assert_eq!(permutation.count(&4), 24);

        let mut pos = Vec::new();
        let ref dim = 4;
        let count = permutation.count(dim);
        for i in 0..count {
            permutation.to_pos(dim, i, &mut pos);
            let index = permutation.to_index(dim, &pos);
            assert_eq!(index, i);
        }
    }

    #[test]
    fn of() {
        let space: Permutation<Of<Pair>> = Construct::new();
        let ref dim = 3;
        let count = space.count(dim);
        let mut pos = Vec::new();
        for i in 0..count {
            space.to_pos(dim, i, &mut pos);
            let index = space.to_index(dim, &pos);
            assert_eq!(index, i);
        }
    }
}
<|endoftext|>


<commit_before>extern crate regex;
extern crate reqwest;
extern crate select;

use irc::client::prelude::*;
use irc::error::Error as IrcError;

use self::regex::Regex;

use std::str;
use std::io::{self, Read};
use self::reqwest::Client;
use self::reqwest::header::Connection;

use self::select::document::Document;
use self::select::predicate::Name;

use plugin::*;

lazy_static! {
    static ref RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap();
}

#[derive(PluginName, Debug)]
pub struct Url {
    max_kib: usize,
}

impl Url {
    /// If a file is larger than `max_kib` KiB the download is stopped
    pub fn new(max_kib: usize) -> Url {
        Url {max_kib: max_kib}
    }

    fn grep_url(&self, msg: &str) -> Option<String> {
        match RE.captures(msg) {
            Some(captures) => {
                debug!("Url captures: {:?}", captures);

                Some(captures.get(2).unwrap().as_str().to_string())
            }
            None => None,
        }
    }

    fn url(&self, server: &IrcServer, message: &str, target: &str) -> Result<(), IrcError> {
        let url = match self.grep_url(message) {
            Some(url) => url,
            None => {
                return Ok(());
            }
        };

        let response = Client::new()
            .get(&url)
            .header(Connection::close())
            .send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();

                // 500 kilobyte buffer
                let mut buf = [0; 500 * 1000];
                let mut written = 0;
                // Read until we reach EOF or max_kib KiB
                loop {
                    let len = match response.read(&mut buf) {
                        Ok(0) => break,
                        Ok(len) => len,
                        Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
                        Err(e) => {
                            debug!("Download from \"{}\" failed: {}", url, e);
                            return Ok(())
                        }
                    };

                    let slice = match str::from_utf8(&buf[..len]) {
                        Ok(slice) => slice,
                        Err(e) => {
                            debug!("Failed to read bytes from \"{}\" as UTF8: {}", url, e);
                            return Ok(());
                        }
                    };

                    body.push_str(slice);
                    written += len;

                    // Check if the file is too large to download
                    if written > self.max_kib * 1024 {
                        debug!("Stopping download - File from \"{}\" is larger than {} KiB", url, self.max_kib);
                        return Ok(());
                    }
                }

                let doc = Document::from(body.as_ref());
                if let Some(title) = doc.find(Name("title")).next() {
                    let text = title.children().next().unwrap();
                    debug!("Title: {:?}", text);

                    server.send_privmsg(target, text.as_text().unwrap())

                } else {
                    Ok(())
                }
            }
            Err(e) => {
                debug!("Bad response from \"{}\": ({})", url, e);
                Ok(())
            }
        }
    }
}

impl Plugin for Url {
    fn is_allowed(&self, _: &IrcServer, message: &Message) -> bool {
        match message.command {
            Command::PRIVMSG(_, ref msg) => RE.is_match(msg),
            _ => false,
        }
    }

    fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError> {
        match message.command {
            Command::PRIVMSG(_, ref content) => {
                self.url(server, content, message.response_target().unwrap())
            }
            _ => Ok(()),
        }
    }

    fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        server.send_notice(&command.source,
                           "This Plugin does not implement any commands.")
    }
}

#[cfg(test)]
mod tests {}
<commit_msg>Trim url titles and move downloading into a function<commit_after>extern crate regex;
extern crate reqwest;
extern crate select;

use irc::client::prelude::*;
use irc::error::Error as IrcError;

use self::regex::Regex;

use std::str;
use std::io::{self, Read};
use self::reqwest::Client;
use self::reqwest::header::Connection;

use self::select::document::Document;
use self::select::predicate::Name;

use plugin::*;

lazy_static! {
    static ref RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap();
}

#[derive(PluginName, Debug)]
pub struct Url {
    max_kib: usize,
}

impl Url {
    /// If a file is larger than `max_kib` KiB the download is stopped
    pub fn new(max_kib: usize) -> Url {
        Url {max_kib: max_kib}
    }

    fn grep_url(&self, msg: &str) -> Option<String> {
        match RE.captures(msg) {
            Some(captures) => {
                debug!("Url captures: {:?}", captures);

                Some(captures.get(2).unwrap().as_str().to_string())
            }
            None => None,
        }
    }

    fn download(&self, url: &str) -> Option<String> {
        let response = Client::new()
            .get(url)
            .header(Connection::close())
            .send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();

                // 500 kilobyte buffer
                let mut buf = [0; 500 * 1000];
                let mut written = 0;
                // Read until we reach EOF or max_kib KiB
                loop {
                    let len = match response.read(&mut buf) {
                        Ok(0) => break,
                        Ok(len) => len,
                        Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
                        Err(e) => {
                            debug!("Download from {:?} failed: {}", url, e);
                            return None;
                        }
                    };

                    let slice = match str::from_utf8(&buf[..len]) {
                        Ok(slice) => slice,
                        Err(e) => {
                            debug!("Failed to read bytes from {:?} as UTF8: {}", url, e);
                            return None;
                        }
                    };

                    body.push_str(slice);
                    written += len;

                    // Check if the file is too large to download
                    if written > self.max_kib * 1024 {
                        debug!("Stopping download - File from {:?} is larger than {} KiB", url, self.max_kib);
                        return None;
                    }

                }
                Some(body) // once told me
            }
            Err(e) => {
                debug!("Bad response from {:?}: ({})", url, e);
                return None;
            }
        }
    }

    fn url(&self, server: &IrcServer, message: &str, target: &str) -> Result<(), IrcError> {
        let url = match self.grep_url(message) {
            Some(url) => url,
            None => {
                return Ok(());
            }
        };

        match self.download(&url) {
            Some(body) => {

                let doc = Document::from(body.as_ref());
                if let Some(title) = doc.find(Name("title")).next() {
                    let text = title.children().next().unwrap();
                    let message = text.as_text().unwrap().trim().replace("\n", "|");
                    debug!("Title: {:?}", text);
                    debug!("Message: {:?}", message);

                    server.send_privmsg(target, &message)

                } else {
                    Ok(())
                }
            }
            None => Ok(()),
        }
    }
}

impl Plugin for Url {
    fn is_allowed(&self, _: &IrcServer, message: &Message) -> bool {
        match message.command {
            Command::PRIVMSG(_, ref msg) => RE.is_match(msg),
            _ => false,
        }
    }

    fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError> {
        match message.command {
            Command::PRIVMSG(_, ref content) => {
                self.url(server, content, message.response_target().unwrap())
            }
            _ => Ok(()),
        }
    }

    fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        server.send_notice(&command.source,
                           "This Plugin does not implement any commands.")
    }
}

#[cfg(test)]
mod tests {}
<|endoftext|>


<commit_before>use crate::config;
use crate::error;

use protobuf;
use std::cmp;
use std::fs;
use std::path;
use std::process;

pub fn add_file(
    paths: &config::Paths,
    relative_to: &path::Path,
    file: &path::Path,
) -> error::Result<()> {
    let rel_file = file
        .strip_prefix(relative_to)
        .unwrap_or_else(|_| file.file_name().map_or(file, path::Path::new));
    let target = paths.preferred_data("proto").join(rel_file);

    if let Some(parent) = target.parent() {
        trace!("Creating directory {:?}", parent);
        fs::create_dir_all(parent)?;
    }

    fs::copy(file, &target)?;
    info!("Added proto file as {:?}", target);
    Ok(())
}

pub fn compile_descriptor_set(
    paths: &config::Paths,
) -> error::Result<protobuf::descriptor::FileDescriptorSet> {
    let proto_includes = paths.find_data("proto")?;
    let proto_files = paths.find_data("proto/**/*.proto")?;
    let cache = paths.preferred_cache("descriptor-cache.pb");

    debug!("Proto includes: {:?}", proto_includes);
    debug!("Proto files: {:?}", proto_files);
    debug!("Proto cache location: {:?}", cache);

    if is_cache_stale(&cache, &proto_files)? {
        info!("Proto descriptor cache is stale; recomputing");

        if let Some(parent) = cache.parent() {
            trace!("Creating directory {:?}", parent);
            fs::create_dir_all(parent)?;
        }

        let include_args = proto_includes
            .into_iter()
            .map(|p| format!("-I{}", p.to_string_lossy()))
            .collect::<Vec<_>>();

        let status = process::Command::new("protoc")
            .arg("-o")
            .arg(&cache)
            .args(&include_args)
            .args(&proto_files)
            .status()?;
        if !status.success() {
            panic!("protoc descriptor compilation failed");
        }

        trace!("Proto descriptor cache regenerated");
    }

    let mut cache_file = fs::File::open(&cache)?;
    let descriptor_set = protobuf::parse_from_reader(&mut cache_file)?;

    trace!("Successfully parsed descriptor set from cache");

    Ok(descriptor_set)
}

fn is_cache_stale<P>(cache: &path::Path, proto_files: &[P]) -> error::Result<bool>
where
    P: AsRef<path::Path>,
{
    use std::os::unix::fs::MetadataExt;

    if cache.exists() {
        let cache_metadata = fs::metadata(&cache)?;
        let cache_mtime = cache_metadata.mtime();
        let mut max_proto_mtime = 0;

        for proto_file in proto_files.iter() {
            let proto_metadata = fs::metadata(&proto_file)?;
            let proto_mtime = proto_metadata.mtime();
            max_proto_mtime = cmp::max(max_proto_mtime, proto_mtime);
        }

        Ok(cache_mtime < max_proto_mtime)
    } else {
        Ok(true)
    }
}
<commit_msg>Stop using UNIX-specific APIs<commit_after>use crate::config;
use crate::error;

use protobuf;
use std::cmp;
use std::fs;
use std::path;
use std::process;

pub fn add_file(
    paths: &config::Paths,
    relative_to: &path::Path,
    file: &path::Path,
) -> error::Result<()> {
    let rel_file = file
        .strip_prefix(relative_to)
        .unwrap_or_else(|_| file.file_name().map_or(file, path::Path::new));
    let target = paths.preferred_data("proto").join(rel_file);

    if let Some(parent) = target.parent() {
        trace!("Creating directory {:?}", parent);
        fs::create_dir_all(parent)?;
    }

    fs::copy(file, &target)?;
    info!("Added proto file as {:?}", target);
    Ok(())
}

pub fn compile_descriptor_set(
    paths: &config::Paths,
) -> error::Result<protobuf::descriptor::FileDescriptorSet> {
    let proto_includes = paths.find_data("proto")?;
    let proto_files = paths.find_data("proto/**/*.proto")?;
    let cache = paths.preferred_cache("descriptor-cache.pb");

    debug!("Proto includes: {:?}", proto_includes);
    debug!("Proto files: {:?}", proto_files);
    debug!("Proto cache location: {:?}", cache);

    if is_cache_stale(&cache, &proto_files)? {
        info!("Proto descriptor cache is stale; recomputing");

        if let Some(parent) = cache.parent() {
            trace!("Creating directory {:?}", parent);
            fs::create_dir_all(parent)?;
        }

        let include_args = proto_includes
            .into_iter()
            .map(|p| format!("-I{}", p.to_string_lossy()))
            .collect::<Vec<_>>();

        let status = process::Command::new("protoc")
            .arg("-o")
            .arg(&cache)
            .args(&include_args)
            .args(&proto_files)
            .status()?;
        if !status.success() {
            panic!("protoc descriptor compilation failed");
        }

        trace!("Proto descriptor cache regenerated");
    }

    let mut cache_file = fs::File::open(&cache)?;
    let descriptor_set = protobuf::parse_from_reader(&mut cache_file)?;

    trace!("Successfully parsed descriptor set from cache");

    Ok(descriptor_set)
}

fn is_cache_stale<P>(cache: &path::Path, proto_files: &[P]) -> error::Result<bool>
where
    P: AsRef<path::Path>,
{
    if cache.exists() {
        let cache_metadata = fs::metadata(&cache)?;
        let cache_mtime = cache_metadata.modified()?;
        let mut max_proto_mtime = std::time::SystemTime::UNIX_EPOCH;

        for proto_file in proto_files.iter() {
            let proto_metadata = fs::metadata(&proto_file)?;
            let proto_mtime = proto_metadata.modified()?;
            max_proto_mtime = cmp::max(max_proto_mtime, proto_mtime);
        }

        Ok(cache_mtime < max_proto_mtime)
    } else {
        Ok(true)
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::Path;
use std::vec::Vec;

use glium::backend::Facade;

use scene::obj_load;
use scene::Vertex;
use scene::mesh::Mesh;

/// Renderer representation of a scene
#[derive(Default)]
pub struct Scene {
    pub meshes: Vec<Mesh>,
    pub min: [f32; 3],
    pub max: [f32; 3],
}

impl Scene {
    pub fn get_center(&self) -> [f32; 3] {
        let mut res = [0.0f32; 3];
        for i in 0..2 {
            res[i] = (self.min[i] + self.max[i]) / 2.0;
        }
        res
    }

    pub fn get_size(&self) -> f32 {
        let mut max = 0.0f32;
        for i in 0..2 {
            max = max.max(self.max[i] - self.min[i]);
        }
        max
    }

    fn update_ranges(&mut self, new_pos: [f32; 3]) {
        for i in 0..2 {
            self.min[i] = self.min[i].min(new_pos[i]);
        }
        for i in 0..2 {
            self.max[i] = self.max[i].max(new_pos[i]);
        }
    }
}

/// Load a scene from the given path bind resources to given facade
pub fn load_scene<F: Facade>(scene_path: &Path, facade: &F) -> Scene {
    let mut scene = Scene { .. Default::default() };
    let obj = obj_load::load_obj(scene_path).expect("Failed to load.");

    // Closure to calculate planar normal for a polygon
    let calculate_normal = |polygon: &obj_load::Polygon| -> [f32; 3] {
        let pos_i1 = polygon.index_vertices[0][0].expect("No vertex positions!");
        let pos_i2 = polygon.index_vertices[1][0].expect("No vertex positions!");
        let pos_i3 = polygon.index_vertices[2][0].expect("No vertex positions!");
        let pos_1 = obj.positions[pos_i1];
        let pos_2 = obj.positions[pos_i2];
        let pos_3 = obj.positions[pos_i3];
        let u = [pos_2[0] - pos_1[0],
                 pos_2[1] - pos_1[1],
                 pos_2[2] - pos_1[2]];
        let v = [pos_3[0] - pos_1[0],
                 pos_3[1] - pos_1[1],
                 pos_3[2] - pos_1[2]];
        [u[1]*v[2] - u[2]*v[1],
         u[2]*v[0] - u[0]*v[2],
         u[0]*v[1] - u[1]*v[0]]
    };

    // Group the polygons by materials for easy rendering
    for range in &obj.material_ranges {
        let obj_mat = obj.materials.get(&range.name)
            .expect(&::std::fmt::format(format_args!("Couldn't find material {}!", range.name)));
        let mut mesh = Mesh::new(&obj_mat);
        let mut vertex_map = HashMap::new();
        for tri in &obj.polygons[range.start_i..range.end_i] {
            let default_tex_coords= [0.0; 2];
            for index_vertex in &tri.index_vertices {
                match vertex_map.get(index_vertex) {
                    // Vertex has already been added
                    Some(&i) => mesh.indices.push(i),
                    None => {
                        // Add vertex to map
                        vertex_map.insert(index_vertex, mesh.vertices.len() as u32);
                        // Panic if there is no positions
                        let pos_i = index_vertex[0].expect("No vertex positions!");
                        let pos = obj.positions[pos_i];
                        scene.update_ranges(pos);

                        let tex_coords = match index_vertex[1] {
                            Some(tex_coords_i) => obj.tex_coords[tex_coords_i],
                            None => default_tex_coords
                        };
                        let normal = match index_vertex[2] {
                            Some(normal_i) => obj.normals[normal_i],
                            None => calculate_normal(tri)
                        };

                        mesh.indices.push(mesh.vertices.len() as u32);
                        mesh.vertices.push(Vertex { position: pos, normal: normal, tex_coords: tex_coords });
                    }
                }
            }
        }
        if !mesh.vertices.is_empty() {
            mesh.upload_data(facade);
            scene.meshes.push(mesh);
        }
    }
    scene
}
<commit_msg>Add comments<commit_after>use std::collections::HashMap;
use std::path::Path;
use std::vec::Vec;

use glium::backend::Facade;

use scene::obj_load;
use scene::Vertex;
use scene::mesh::Mesh;

/// Renderer representation of a scene
#[derive(Default)]
pub struct Scene {
    pub meshes: Vec<Mesh>,
    /// Bounding box of the scene
    pub min: [f32; 3],
    pub max: [f32; 3],
}

impl Scene {
    /// Get the center of the scene as defined by the bounding box
    pub fn get_center(&self) -> [f32; 3] {
        let mut res = [0.0f32; 3];
        for i in 0..2 {
            res[i] = (self.min[i] + self.max[i]) / 2.0;
        }
        res
    }

    /// Get the longest edge of the bounding box
    pub fn get_size(&self) -> f32 {
        let mut max = 0.0f32;
        for i in 0..2 {
            max = max.max(self.max[i] - self.min[i]);
        }
        max
    }

    /// Update the bounding box with new position
    fn update_ranges(&mut self, new_pos: [f32; 3]) {
        for i in 0..2 {
            self.min[i] = self.min[i].min(new_pos[i]);
        }
        for i in 0..2 {
            self.max[i] = self.max[i].max(new_pos[i]);
        }
    }
}

/// Load a scene from the given path bind resources to given facade
pub fn load_scene<F: Facade>(scene_path: &Path, facade: &F) -> Scene {
    let mut scene = Scene { .. Default::default() };
    let obj = obj_load::load_obj(scene_path).expect("Failed to load.");

    // Closure to calculate planar normal for a polygon
    let calculate_normal = |polygon: &obj_load::Polygon| -> [f32; 3] {
        let pos_i1 = polygon.index_vertices[0][0].expect("No vertex positions!");
        let pos_i2 = polygon.index_vertices[1][0].expect("No vertex positions!");
        let pos_i3 = polygon.index_vertices[2][0].expect("No vertex positions!");
        let pos_1 = obj.positions[pos_i1];
        let pos_2 = obj.positions[pos_i2];
        let pos_3 = obj.positions[pos_i3];
        let u = [pos_2[0] - pos_1[0],
                 pos_2[1] - pos_1[1],
                 pos_2[2] - pos_1[2]];
        let v = [pos_3[0] - pos_1[0],
                 pos_3[1] - pos_1[1],
                 pos_3[2] - pos_1[2]];
        [u[1]*v[2] - u[2]*v[1],
         u[2]*v[0] - u[0]*v[2],
         u[0]*v[1] - u[1]*v[0]]
    };

    // Group the polygons by materials for easy rendering
    for range in &obj.material_ranges {
        let obj_mat = obj.materials.get(&range.name)
            .expect(&::std::fmt::format(format_args!("Couldn't find material {}!", range.name)));
        let mut mesh = Mesh::new(&obj_mat);
        let mut vertex_map = HashMap::new();
        for tri in &obj.polygons[range.start_i..range.end_i] {
            let default_tex_coords= [0.0; 2];
            for index_vertex in &tri.index_vertices {
                match vertex_map.get(index_vertex) {
                    // Vertex has already been added
                    Some(&i) => mesh.indices.push(i),
                    None => {
                        // Add vertex to map
                        vertex_map.insert(index_vertex, mesh.vertices.len() as u32);
                        // Panic if there is no positions
                        let pos_i = index_vertex[0].expect("No vertex positions!");
                        let pos = obj.positions[pos_i];
                        scene.update_ranges(pos);

                        let tex_coords = match index_vertex[1] {
                            Some(tex_coords_i) => obj.tex_coords[tex_coords_i],
                            None => default_tex_coords
                        };
                        let normal = match index_vertex[2] {
                            Some(normal_i) => obj.normals[normal_i],
                            None => calculate_normal(tri)
                        };

                        mesh.indices.push(mesh.vertices.len() as u32);
                        mesh.vertices.push(Vertex { position: pos, normal: normal, tex_coords: tex_coords });
                    }
                }
            }
        }
        if !mesh.vertices.is_empty() {
            mesh.upload_data(facade);
            scene.meshes.push(mesh);
        }
    }
    scene
}
<|endoftext|>