<commit_before>// Syntax: master_server [ip address (0.0.0.0 by default)]
// This was created for HaloMD.

// If the server does not respond in at least this many seconds, it will be dropped from the list.
const DROP_TIME : i64 = 60;

// Blacklist for blocking IPs. Separate with newlines. Any line that starts with a # is ignored.
// Blacklisting IPs ignores heartbeat and keepalive packets from an IP address.
// That means that servers that are banned will not be immediately removed, but will time out, instead.
const BLACKLIST_FILE : &'static str = "blacklist.txt";

// Read the blacklist every x amount of seconds.
const BLACKLIST_UPDATE_TIME : u32 = 60;

// Note: The master server must have TCP 29920 open and UDP 27900 open.
const BROADCAST_PORT_UDP : u16 = 27900;
const SERVER_LIST_PORT_TCP : u16 = 29920;

// Opcode info from game packets
const OPCODE_INDEX : usize = 0;
const OPCODE_AND_HANDSHAKE_LENGTH : usize = 5;

// Broadcast packet type opcodes.
const KEEPALIVE : u8 = 8;
const HEARTBEAT : u8 = 3;

const TCP_SERVER_THREAD_NAME : &'static str = "halomd_thread";
const DESTRUCTION_THREAD_NAME : &'static str = "destruction_thread";
const BLACKLIST_THREAD_NAME : &'static str = "blacklist_thread";

use std::net::{UdpSocket,TcpListener,SocketAddr};
use std::net::SocketAddr::{V4,V6};
use std::io::{Write,BufReader,BufRead};
use std::env;
use std::fs::File;
use std::thread;
use std::thread::Builder;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;

extern crate time;
use time::{SteadyTime,Duration};

mod error_macros;

mod halo_server;
use halo_server::HaloServer;

mod heartbeat_packet;
use heartbeat_packet::{HeartbeatPacket,GAMEEXITED,VALID_GAME_VERSIONS,HALO_RETAIL};

trait IPString {
    fn ip_string(&self) -> String;
}

impl IPString for SocketAddr {
    fn ip_string(&self) -> String {
        match *self {
            V4(ipv4) => ipv4.ip().to_string(),
            V6(ipv6) => "[".to_owned() + &ipv6.ip().to_string() + "]"
        }
    }
}

fn main() {
    let count = env::args().count();
    let ip = if count == 2 {
        let args : Vec<_> = env::args().collect();
        args[1].to_owned()
    }
    else if count == 1 {
        "0.0.0.0".to_owned()
    }
    else {
        println!("Only one argument is allowed: the IP to bind to.");
        return;
    };

    // We need to bind on two different ports. If it failed to bind (invalid IP, port is taken), then we must make sure this is known.
    let halo_socket = unwrap_result_or_bail!(UdpSocket::bind((&ip as &str,BROADCAST_PORT_UDP)), {
        println!("Failed creating a UDP socket at {}:{}.",ip,BROADCAST_PORT_UDP);
        return;
    });

    let client_socket = unwrap_result_or_bail!(TcpListener::bind((&ip as &str,SERVER_LIST_PORT_TCP)), {
        println!("Failed listening to TCP at {}:{}.",ip,SERVER_LIST_PORT_TCP);
        return;
    });

    // Mutex for thread safety.
    let servers_halo: Vec<HaloServer> = Vec::new();
    let servers_mut_udp = Arc::new(Mutex::new(servers_halo));
    let servers_mut_tcp = servers_mut_udp.clone();
    let servers_mut_destruction = servers_mut_udp.clone();

    // Destruction thread. This will remove servers that have not broadcasted their presence in a while.
    let _ = Builder::new().name(DESTRUCTION_THREAD_NAME.to_owned()).spawn(move || {
        loop {
            thread::sleep_ms(10 * 1000);
            let mut servers = servers_mut_destruction.lock().unwrap();
            let timenow = SteadyTime::now();
            servers.retain(|x| x.last_alive + Duration::seconds(DROP_TIME) > timenow);
        }
    });

    // Blacklist mutex. Concurrency needs to be safe, my friend.
    let blacklist_update = Arc::new(Mutex::new(None as Option<HashMap<String, Option<Vec<u16>>>>));
    let blacklist_udp = blacklist_update.clone();

    // Takes a line fed from the blacklist file which is in the format:
    // ip_address \t port1, port2, port3,...
    // and returns a tuple of the IP and ports
    // If no ports are specified, then all ports are considered (should act like a wildcard)
    fn blacklist_info(line: &str) -> (String, Option<Vec<u16>>) {
        let components: Vec<&str> = line.split("\t").map(|x| x.trim()).collect();

        if components.len() <= 1 {
            (line.to_owned(), None)
        }
        else {
            let ports: Vec<u16> = components[1].split(",").flat_map(|x| x.trim().parse::<u16>().ok()).collect();
            (components[0].to_owned(), Some(ports))
        }
    }

    // Blacklist read thread.
    let _ = Builder::new().name(BLACKLIST_THREAD_NAME.to_owned()).spawn(move || {
        let valid_line = |x: &str| -> bool { x.trim().len() > 0 && !x.starts_with("#") };
        loop {
            // Placed in a block so blacklist is unlocked before sleeping to prevent threads from being locked for too long.
            {
                let mut blacklist_ref = blacklist_update.lock().unwrap();
                *blacklist_ref =
                    File::open(BLACKLIST_FILE).
                    map(|file|
                        BufReader::new(&file).lines().
                        filter_map(|line| line.ok().and_then(|x| if valid_line(&x) { Some(blacklist_info(&x)) } else { None })).
                        collect()
                    ).ok();
            }
            thread::sleep_ms(BLACKLIST_UPDATE_TIME * 1000);
        }
    });

    // TCP server thread. This is for the HaloMD application.
    let _ = Builder::new().name(TCP_SERVER_THREAD_NAME.to_owned()).spawn(move || {
        loop {
            for stream in client_socket.incoming() {
                let mut client = unwrap_option_or_bail!(stream.ok(), { continue });
                let ip = unwrap_option_or_bail!(client.peer_addr().ok(), { continue }).ip_string();
                let mut ips = String::new();

                // Make servers_ref go out of scope to unlock it for other threads, since we don't need it.
                {
                    let servers_ref = servers_mut_tcp.lock().unwrap();
                    let servers = (*servers_ref).iter();

                    for j in servers {
                        ips.push_str(&(j.to_string()));
                        ips.push('\n');
                    }
                }

                // Some number placed after the requester's IP. If you ask me, the source code was abducted by aliens, and this is a tracking number. Regardless, it's needed.
                ips.push_str(&ip);
                ips.push_str(":49149:3425");

                // We may be here a while. Just in case...
                // May want to consider some lightweight thread library in the future, or some non-blocking polling mechanism
                thread::spawn( move || {
                    let _ = client.write_all(ips.as_bytes());
                });
            }
        }
    });

    // UDP server is run on the main thread. Servers broadcast their presence here.

    let mut buffer = [0; 2048];
    loop {
        let (length, source) = unwrap_option_or_bail!(halo_socket.recv_from(&mut buffer).ok(), { continue });

        if length <= OPCODE_INDEX {
            continue;
        }

        if buffer[OPCODE_INDEX] != KEEPALIVE && buffer[OPCODE_INDEX] != HEARTBEAT {
            continue;
        }

        let client_ip = source.ip_string();

        let blacklist_ref = blacklist_udp.lock().unwrap();
        let ignore_host = match *blacklist_ref {
            Some(ref blacklist) => {
                match (*blacklist).get(&client_ip) {
                    Some(ports_opt) => {
                        match *ports_opt {
                            Some(ref ports) => ports.contains(&source.port()), // ignore if port is blacklisted
                            None => true // wildcard for all ports
                        }
                    },
                    None => false // no IP was found
                }
            },
            None => false // no blacklist available
        };

        if ignore_host {
            continue;
        }

        // Heartbeat packet. These contain null-terminated C strings and are ordered in key1[0]value1[0]key2[0]value2[0]key3[0]value3[0] where [0] is a byte equal to 0x00.
        if buffer[OPCODE_INDEX] == HEARTBEAT && length > OPCODE_AND_HANDSHAKE_LENGTH {
            let mut servers = servers_mut_udp.lock().unwrap();

            match HeartbeatPacket::from_buffer(&buffer[OPCODE_AND_HANDSHAKE_LENGTH..length]) {
                None => {},
                Some(packet) => {
                    let updatetime = SteadyTime::now();
                    match servers.iter_mut().position(|x| *x == (&client_ip, packet.localport)) {
                        None => {
                            if packet.gamename == HALO_RETAIL && VALID_GAME_VERSIONS.contains(&&*packet.gamever) {
                                (*servers).push(HaloServer { ip:client_ip, port: packet.localport, last_alive: updatetime });
                            }
                        }
                        Some(k) => {
                            servers[k].last_alive = updatetime;
                            if packet.statechanged == GAMEEXITED {
                                servers.remove(k);
                            }
                        }
                    };
                }
            };
        }

        // Keepalive packet. We need to rely on the origin's port for this, unfortunately. This may mean that the source port is incorrect if the port was changed with NAT.
        else if buffer[OPCODE_INDEX] == KEEPALIVE {
            let mut servers_ref = servers_mut_udp.lock().unwrap();
            let servers = (*servers_ref).iter_mut();

            for i in servers {
                if *i == (&client_ip, source.port()) {
                    i.last_alive = SteadyTime::now();
                    break;
                }
            }
        }
    }
}
<commit_msg>Replace deprecated functions<commit_after>// Syntax: master_server [ip address (0.0.0.0 by default)]
// This was created for HaloMD.

// If the server does not respond in at least this many seconds, it will be dropped from the list.
const DROP_TIME : i64 = 60;

// Blacklist for blocking IPs. Separate with newlines. Any line that starts with a # is ignored.
// Blacklisting IPs ignores heartbeat and keepalive packets from an IP address.
// That means that servers that are banned will not be immediately removed, but will time out, instead.
const BLACKLIST_FILE : &'static str = "blacklist.txt";

// Read the blacklist every x amount of seconds.
const BLACKLIST_UPDATE_TIME : u64 = 60;

// Note: The master server must have TCP 29920 open and UDP 27900 open.
const BROADCAST_PORT_UDP : u16 = 27900;
const SERVER_LIST_PORT_TCP : u16 = 29920;

// Opcode info from game packets
const OPCODE_INDEX : usize = 0;
const OPCODE_AND_HANDSHAKE_LENGTH : usize = 5;

// Broadcast packet type opcodes.
const KEEPALIVE : u8 = 8;
const HEARTBEAT : u8 = 3;

const TCP_SERVER_THREAD_NAME : &'static str = "halomd_thread";
const DESTRUCTION_THREAD_NAME : &'static str = "destruction_thread";
const BLACKLIST_THREAD_NAME : &'static str = "blacklist_thread";

use std::net::{UdpSocket,TcpListener,SocketAddr};
use std::net::SocketAddr::{V4,V6};
use std::io::{Write,BufReader,BufRead};
use std::env;
use std::fs::File;
use std::thread;
use std::thread::Builder;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::time::Duration;

extern crate time;
use time::{SteadyTime,Duration as TimeDuration};

mod error_macros;

mod halo_server;
use halo_server::HaloServer;

mod heartbeat_packet;
use heartbeat_packet::{HeartbeatPacket,GAMEEXITED,VALID_GAME_VERSIONS,HALO_RETAIL};

trait IPString {
    fn ip_string(&self) -> String;
}

impl IPString for SocketAddr {
    fn ip_string(&self) -> String {
        match *self {
            V4(ipv4) => ipv4.ip().to_string(),
            V6(ipv6) => "[".to_owned() + &ipv6.ip().to_string() + "]"
        }
    }
}

fn main() {
    let count = env::args().count();
    let ip = if count == 2 {
        let args : Vec<_> = env::args().collect();
        args[1].to_owned()
    }
    else if count == 1 {
        "0.0.0.0".to_owned()
    }
    else {
        println!("Only one argument is allowed: the IP to bind to.");
        return;
    };

    // We need to bind on two different ports. If it failed to bind (invalid IP, port is taken), then we must make sure this is known.
    let halo_socket = unwrap_result_or_bail!(UdpSocket::bind((&ip as &str,BROADCAST_PORT_UDP)), {
        println!("Failed creating a UDP socket at {}:{}.",ip,BROADCAST_PORT_UDP);
        return;
    });

    let client_socket = unwrap_result_or_bail!(TcpListener::bind((&ip as &str,SERVER_LIST_PORT_TCP)), {
        println!("Failed listening to TCP at {}:{}.",ip,SERVER_LIST_PORT_TCP);
        return;
    });

    // Mutex for thread safety.
    let servers_halo: Vec<HaloServer> = Vec::new();
    let servers_mut_udp = Arc::new(Mutex::new(servers_halo));
    let servers_mut_tcp = servers_mut_udp.clone();
    let servers_mut_destruction = servers_mut_udp.clone();

    // Destruction thread. This will remove servers that have not broadcasted their presence in a while.
    let _ = Builder::new().name(DESTRUCTION_THREAD_NAME.to_owned()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(10));
            let mut servers = servers_mut_destruction.lock().unwrap();
            let timenow = SteadyTime::now();
            servers.retain(|x| x.last_alive + TimeDuration::seconds(DROP_TIME) > timenow);
        }
    });

    // Blacklist mutex. Concurrency needs to be safe, my friend.
    let blacklist_update = Arc::new(Mutex::new(None as Option<HashMap<String, Option<Vec<u16>>>>));
    let blacklist_udp = blacklist_update.clone();

    // Takes a line fed from the blacklist file which is in the format:
    // ip_address \t port1, port2, port3,...
    // and returns a tuple of the IP and ports
    // If no ports are specified, then all ports are considered (should act like a wildcard)
    fn blacklist_info(line: &str) -> (String, Option<Vec<u16>>) {
        let components: Vec<&str> = line.split("\t").map(|x| x.trim()).collect();

        if components.len() <= 1 {
            (line.to_owned(), None)
        }
        else {
            let ports: Vec<u16> = components[1].split(",").flat_map(|x| x.trim().parse::<u16>().ok()).collect();
            (components[0].to_owned(), Some(ports))
        }
    }

    // Blacklist read thread.
    let _ = Builder::new().name(BLACKLIST_THREAD_NAME.to_owned()).spawn(move || {
        let valid_line = |x: &str| -> bool { x.trim().len() > 0 && !x.starts_with("#") };
        loop {
            // Placed in a block so blacklist is unlocked before sleeping to prevent threads from being locked for too long.
            {
                let mut blacklist_ref = blacklist_update.lock().unwrap();
                *blacklist_ref =
                    File::open(BLACKLIST_FILE).
                    map(|file|
                        BufReader::new(&file).lines().
                        filter_map(|line| line.ok().and_then(|x| if valid_line(&x) { Some(blacklist_info(&x)) } else { None })).
                        collect()
                    ).ok();
            }
            thread::sleep(Duration::from_secs(BLACKLIST_UPDATE_TIME));
        }
    });

    // TCP server thread. This is for the HaloMD application.
    let _ = Builder::new().name(TCP_SERVER_THREAD_NAME.to_owned()).spawn(move || {
        loop {
            for stream in client_socket.incoming() {
                let mut client = unwrap_option_or_bail!(stream.ok(), { continue });
                let ip = unwrap_option_or_bail!(client.peer_addr().ok(), { continue }).ip_string();
                let mut ips = String::new();

                // Make servers_ref go out of scope to unlock it for other threads, since we don't need it.
                {
                    let servers_ref = servers_mut_tcp.lock().unwrap();
                    let servers = (*servers_ref).iter();

                    for j in servers {
                        ips.push_str(&(j.to_string()));
                        ips.push('\n');
                    }
                }

                // Some number placed after the requester's IP. If you ask me, the source code was abducted by aliens, and this is a tracking number. Regardless, it's needed.
                ips.push_str(&ip);
                ips.push_str(":49149:3425");

                // We may be here a while. Just in case...
                // May want to consider some lightweight thread library in the future, or some non-blocking polling mechanism
                thread::spawn( move || {
                    let _ = client.write_all(ips.as_bytes());
                });
            }
        }
    });

    // UDP server is run on the main thread. Servers broadcast their presence here.

    let mut buffer = [0; 2048];
    loop {
        let (length, source) = unwrap_option_or_bail!(halo_socket.recv_from(&mut buffer).ok(), { continue });

        if length <= OPCODE_INDEX {
            continue;
        }

        if buffer[OPCODE_INDEX] != KEEPALIVE && buffer[OPCODE_INDEX] != HEARTBEAT {
            continue;
        }

        let client_ip = source.ip_string();

        let blacklist_ref = blacklist_udp.lock().unwrap();
        let ignore_host = match *blacklist_ref {
            Some(ref blacklist) => {
                match (*blacklist).get(&client_ip) {
                    Some(ports_opt) => {
                        match *ports_opt {
                            Some(ref ports) => ports.contains(&source.port()), // ignore if port is blacklisted
                            None => true // wildcard for all ports
                        }
                    },
                    None => false // no IP was found
                }
            },
            None => false // no blacklist available
        };

        if ignore_host {
            continue;
        }

        // Heartbeat packet. These contain null-terminated C strings and are ordered in key1[0]value1[0]key2[0]value2[0]key3[0]value3[0] where [0] is a byte equal to 0x00.
        if buffer[OPCODE_INDEX] == HEARTBEAT && length > OPCODE_AND_HANDSHAKE_LENGTH {
            let mut servers = servers_mut_udp.lock().unwrap();

            match HeartbeatPacket::from_buffer(&buffer[OPCODE_AND_HANDSHAKE_LENGTH..length]) {
                None => {},
                Some(packet) => {
                    let updatetime = SteadyTime::now();
                    match servers.iter_mut().position(|x| *x == (&client_ip, packet.localport)) {
                        None => {
                            if packet.gamename == HALO_RETAIL && VALID_GAME_VERSIONS.contains(&&*packet.gamever) {
                                (*servers).push(HaloServer { ip:client_ip, port: packet.localport, last_alive: updatetime });
                            }
                        }
                        Some(k) => {
                            servers[k].last_alive = updatetime;
                            if packet.statechanged == GAMEEXITED {
                                servers.remove(k);
                            }
                        }
                    };
                }
            };
        }

        // Keepalive packet. We need to rely on the origin's port for this, unfortunately. This may mean that the source port is incorrect if the port was changed with NAT.
        else if buffer[OPCODE_INDEX] == KEEPALIVE {
            let mut servers_ref = servers_mut_udp.lock().unwrap();
            let servers = (*servers_ref).iter_mut();

            for i in servers {
                if *i == (&client_ip, source.port()) {
                    i.last_alive = SteadyTime::now();
                    break;
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>
extern crate git2;

use std::env;
use std::process::Command;
use git2::Repository;

fn main() {
    let url = "https://github.com/iris-edu/libmseed";
    match Repository::clone(url, "src/libmseed") {
        Ok(_repo) => {},
        Err(e) => {
            match e.code() {
                git2::ErrorCode::Exists => {
                    println!("directory exists");
                },
                _ => panic!("Failed to update/clone repo: {}", e),
            }
        }
    };

    let dir = "src/libmseed";
    let path = std::fs::canonicalize(dir).unwrap();
    let _ok = env::set_current_dir(&path).is_ok();
    let _output = Command::new("make").output().expect("make failed");

    println!("cargo:rustc-link-search={}", path.to_str().unwrap());
    println!("cargo:rustc-link-lib={}", "mseed");
}
<commit_msg>Check out libmseed v2<commit_after>
extern crate git2;

use std::env;
use std::process::Command;
use git2::Repository;

const REPO_URL: &str = "https://github.com/iris-edu/libmseed";
const BUILD_DIR: &str = "src/libmseed";
const GIT_REF: &str = "refs/heads/2.x"; // libmseed v3 broke the ABI

fn main() {
    let repo = match Repository::clone(REPO_URL, BUILD_DIR) {
        Ok(_repo) => _repo,
        Err(e) => match e.code() {
            git2::ErrorCode::Exists =>
                Repository::open(BUILD_DIR)
                    .expect("Failed to open existing repo"),
            _ => panic!("Failed to update/clone repo: {}", e),
        }
    };
    let branch = repo.revparse_single(GIT_REF).unwrap();
    repo.reset(&branch, git2::ResetType::Hard, None).unwrap();

    let path = std::fs::canonicalize(BUILD_DIR).unwrap();
    env::set_current_dir(&path).unwrap();
    Command::new("make").output().expect("make failed");

    println!("cargo:rustc-link-search={}", path.to_str().unwrap());
    println!("cargo:rustc-link-lib={}", "mseed");
}
<|endoftext|>


<commit_before>//! Structural const qualification.
//!
//! See the `Qualif` trait for more info.

use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::{
    self, ImplSource, Obligation, ObligationCause, SelectionContext,
};

use super::ConstCx;

pub fn in_any_value_of_ty(
    cx: &ConstCx<'_, 'tcx>,
    ty: Ty<'tcx>,
    error_occured: Option<ErrorReported>,
) -> ConstQualifs {
    ConstQualifs {
        has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
        needs_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
        custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
        error_occured,
    }
}

/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
/// code for promotion or prevent it from evaluating at compile time.
///
/// Normally, we would determine what qualifications apply to each type and error when an illegal
/// operation is performed on such a type. However, this was found to be too imprecise, especially
/// in the presence of `enum`s. If only a single variant of an enum has a certain qualification, we
/// needn't reject code unless it actually constructs and operates on the qualified variant.
///
/// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a
/// type-based one). Qualifications propagate structurally across variables: If a local (or a
/// projection of a local) is assigned a qualified value, that local itself becomes qualified.
pub trait Qualif {
    /// The name of the file used to debug the dataflow analysis that computes this qualif.
    const ANALYSIS_NAME: &'static str;

    /// Whether this `Qualif` is cleared when a local is moved from.
    const IS_CLEARED_ON_MOVE: bool = false;

    /// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`.
    fn in_qualifs(qualifs: &ConstQualifs) -> bool;

    /// Returns `true` if *any* value of the given type could possibly have this `Qualif`.
    ///
    /// This function determines `Qualif`s when we cannot do a value-based analysis. Since qualif
    /// propagation is context-insenstive, this includes function arguments and values returned
    /// from a call to another function.
    ///
    /// It also determines the `Qualif`s for primitive types.
    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool;

    /// Returns `true` if this `Qualif` is inherent to the given struct or enum.
    ///
    /// By default, `Qualif`s propagate into ADTs in a structural way: An ADT only becomes
    /// qualified if part of it is assigned a value with that `Qualif`. However, some ADTs *always*
    /// have a certain `Qualif`, regardless of whether their fields have it. For example, a type
    /// with a custom `Drop` impl is inherently `NeedsDrop`.
    ///
    /// Returning `true` for `in_adt_inherently` but `false` for `in_any_value_of_ty` is unsound.
    fn in_adt_inherently(
        cx: &ConstCx<'_, 'tcx>,
        adt: &'tcx AdtDef,
        substs: SubstsRef<'tcx>,
    ) -> bool;
}

/// Constant containing interior mutability (`UnsafeCell<T>`).
/// This must be ruled out to make sure that evaluating the constant at compile-time
/// and at *any point* during the run-time would produce the same result. In particular,
/// promotion of temporaries must not change program behavior; if the promoted could be
/// written to, that would be a problem.
pub struct HasMutInterior;

impl Qualif for HasMutInterior {
    const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.has_mut_interior
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env)
    }

    fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
        // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
        // It arises structurally for all other types.
        Some(adt.did) == cx.tcx.lang_items().unsafe_cell_type()
    }
}

/// Constant containing an ADT that implements `Drop`.
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
/// as that might not be a `const fn`, and (b) because implicit promotion would
/// remove side-effects that occur as part of dropping that value.
pub struct NeedsNonConstDrop;

impl Qualif for NeedsNonConstDrop {
    const ANALYSIS_NAME: &'static str = "flow_needs_nonconst_drop";
    const IS_CLEARED_ON_MOVE: bool = true;

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.needs_drop
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        let drop_trait = if let Some(did) = cx.tcx.lang_items().drop_trait() {
            did
        } else {
            // there is no way to define a type that needs non-const drop
            // without having the lang item present.
            return false;
        };
        let trait_ref =
            ty::TraitRef { def_id: drop_trait, substs: cx.tcx.mk_substs_trait(ty, &[]) };
        let obligation = Obligation::new(
            ObligationCause::dummy(),
            cx.param_env,
            ty::Binder::dummy(ty::TraitPredicate {
                trait_ref,
                constness: ty::BoundConstness::ConstIfConst,
            }),
        );

        let implsrc = cx.tcx.infer_ctxt().enter(|infcx| {
            let mut selcx = SelectionContext::with_constness(&infcx, hir::Constness::Const);
            selcx.select(&obligation)
        });
        match implsrc {
            Ok(Some(ImplSource::ConstDrop(_)))
            | Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => false,
            _ => true,
        }
    }

    fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
        adt.has_non_const_dtor(cx.tcx)
    }
}

/// A constant that cannot be used as part of a pattern in a `match` expression.
pub struct CustomEq;

impl Qualif for CustomEq {
    const ANALYSIS_NAME: &'static str = "flow_custom_eq";

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.custom_eq
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        // If *any* component of a composite data type does not implement `Structural{Partial,}Eq`,
        // we know that at least some values of that type are not structural-match. I say "some"
        // because that component may be part of an enum variant (e.g.,
        // `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
        // structural-match (`Option::None`).
        let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id());
        traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).is_some()
    }

    fn in_adt_inherently(
        cx: &ConstCx<'_, 'tcx>,
        adt: &'tcx AdtDef,
        substs: SubstsRef<'tcx>,
    ) -> bool {
        let ty = cx.tcx.mk_ty(ty::Adt(adt, substs));
        !ty.is_structural_eq_shallow(cx.tcx)
    }
}

// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return.

/// Returns `true` if this `Rvalue` contains qualif `Q`.
pub fn in_rvalue<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, rvalue: &Rvalue<'tcx>) -> bool
where
    Q: Qualif,
    F: FnMut(Local) -> bool,
{
    match rvalue {
        Rvalue::ThreadLocalRef(_) | Rvalue::NullaryOp(..) => {
            Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx))
        }

        Rvalue::Discriminant(place) | Rvalue::Len(place) => {
            in_place::<Q, _>(cx, in_local, place.as_ref())
        }

        Rvalue::Use(operand)
        | Rvalue::Repeat(operand, _)
        | Rvalue::UnaryOp(_, operand)
        | Rvalue::Cast(_, operand, _) => in_operand::<Q, _>(cx, in_local, operand),

        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
            in_operand::<Q, _>(cx, in_local, lhs) || in_operand::<Q, _>(cx, in_local, rhs)
        }

        Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
            // Special-case reborrows to be more like a copy of the reference.
            if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() {
                let base_ty = place_base.ty(cx.body, cx.tcx).ty;
                if let ty::Ref(..) = base_ty.kind() {
                    return in_place::<Q, _>(cx, in_local, place_base);
                }
            }

            in_place::<Q, _>(cx, in_local, place.as_ref())
        }

        Rvalue::Aggregate(kind, operands) => {
            // Return early if we know that the struct or enum being constructed is always
            // qualified.
            if let AggregateKind::Adt(def, _, substs, ..) = **kind {
                if Q::in_adt_inherently(cx, def, substs) {
                    return true;
                }
            }

            // Otherwise, proceed structurally...
            operands.iter().any(|o| in_operand::<Q, _>(cx, in_local, o))
        }
    }
}

/// Returns `true` if this `Place` contains qualif `Q`.
pub fn in_place<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, place: PlaceRef<'tcx>) -> bool
where
    Q: Qualif,
    F: FnMut(Local) -> bool,
{
    let mut place = place;
    while let Some((place_base, elem)) = place.last_projection() {
        match elem {
            ProjectionElem::Index(index) if in_local(index) => return true,

            ProjectionElem::Deref
            | ProjectionElem::Field(_, _)
            | ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Downcast(_, _)
            | ProjectionElem::Index(_) => {}
        }

        let base_ty = place_base.ty(cx.body, cx.tcx);
        let proj_ty = base_ty.projection_ty(cx.tcx, elem).ty;
        if !Q::in_any_value_of_ty(cx, proj_ty) {
            return false;
        }

        place = place_base;
    }

    assert!(place.projection.is_empty());
    in_local(place.local)
}

/// Returns `true` if this `Operand` contains qualif `Q`.
pub fn in_operand<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, operand: &Operand<'tcx>) -> bool
where
    Q: Qualif,
    F: FnMut(Local) -> bool,
{
    let constant = match operand {
        Operand::Copy(place) | Operand::Move(place) => {
            return in_place::<Q, _>(cx, in_local, place.as_ref());
        }

        Operand::Constant(c) => c,
    };

    // Check the qualifs of the value of `const` items.
    if let Some(ct) = constant.literal.const_for_ty() {
        if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted }) = ct.val {
            assert!(promoted.is_none());
            // Don't peek inside trait associated constants.
            if cx.tcx.trait_of_item(def.did).is_none() {
                let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
                    cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did))
                } else {
                    cx.tcx.at(constant.span).mir_const_qualif(def.did)
                };

                if !Q::in_qualifs(&qualifs) {
                    return false;
                }

                // Just in case the type is more specific than
                // the definition, e.g., impl associated const
                // with type parameters, take it into account.
            }
        }
    }
    // Otherwise use the qualifs of the type.
    Q::in_any_value_of_ty(cx, constant.literal.ty())
}
<commit_msg>Fast reject for NeedsNonConstDrop<commit_after>//! Structural const qualification.
//!
//! See the `Qualif` trait for more info.

use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::{
    self, ImplSource, Obligation, ObligationCause, SelectionContext,
};

use super::ConstCx;

pub fn in_any_value_of_ty(
    cx: &ConstCx<'_, 'tcx>,
    ty: Ty<'tcx>,
    error_occured: Option<ErrorReported>,
) -> ConstQualifs {
    ConstQualifs {
        has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
        needs_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
        custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
        error_occured,
    }
}

/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
/// code for promotion or prevent it from evaluating at compile time.
///
/// Normally, we would determine what qualifications apply to each type and error when an illegal
/// operation is performed on such a type. However, this was found to be too imprecise, especially
/// in the presence of `enum`s. If only a single variant of an enum has a certain qualification, we
/// needn't reject code unless it actually constructs and operates on the qualified variant.
///
/// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a
/// type-based one). Qualifications propagate structurally across variables: If a local (or a
/// projection of a local) is assigned a qualified value, that local itself becomes qualified.
pub trait Qualif {
    /// The name of the file used to debug the dataflow analysis that computes this qualif.
    const ANALYSIS_NAME: &'static str;

    /// Whether this `Qualif` is cleared when a local is moved from.
    const IS_CLEARED_ON_MOVE: bool = false;

    /// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`.
    fn in_qualifs(qualifs: &ConstQualifs) -> bool;

    /// Returns `true` if *any* value of the given type could possibly have this `Qualif`.
    ///
    /// This function determines `Qualif`s when we cannot do a value-based analysis. Since qualif
    /// propagation is context-insenstive, this includes function arguments and values returned
    /// from a call to another function.
    ///
    /// It also determines the `Qualif`s for primitive types.
    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool;

    /// Returns `true` if this `Qualif` is inherent to the given struct or enum.
    ///
    /// By default, `Qualif`s propagate into ADTs in a structural way: An ADT only becomes
    /// qualified if part of it is assigned a value with that `Qualif`. However, some ADTs *always*
    /// have a certain `Qualif`, regardless of whether their fields have it. For example, a type
    /// with a custom `Drop` impl is inherently `NeedsDrop`.
    ///
    /// Returning `true` for `in_adt_inherently` but `false` for `in_any_value_of_ty` is unsound.
    fn in_adt_inherently(
        cx: &ConstCx<'_, 'tcx>,
        adt: &'tcx AdtDef,
        substs: SubstsRef<'tcx>,
    ) -> bool;
}

/// Constant containing interior mutability (`UnsafeCell<T>`).
/// This must be ruled out to make sure that evaluating the constant at compile-time
/// and at *any point* during the run-time would produce the same result. In particular,
/// promotion of temporaries must not change program behavior; if the promoted could be
/// written to, that would be a problem.
pub struct HasMutInterior;

impl Qualif for HasMutInterior {
    const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.has_mut_interior
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env)
    }

    fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
        // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
        // It arises structurally for all other types.
        Some(adt.did) == cx.tcx.lang_items().unsafe_cell_type()
    }
}

/// Constant containing an ADT that implements `Drop`.
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
/// as that might not be a `const fn`, and (b) because implicit promotion would
/// remove side-effects that occur as part of dropping that value.
pub struct NeedsNonConstDrop;

impl Qualif for NeedsNonConstDrop {
    const ANALYSIS_NAME: &'static str = "flow_needs_nonconst_drop";
    const IS_CLEARED_ON_MOVE: bool = true;

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.needs_drop
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, mut ty: Ty<'tcx>) -> bool {
        // Avoid selecting for simple cases.
        match ty::util::needs_drop_components(ty, &cx.tcx.data_layout).as_deref() {
            Ok([]) => return false,
            Err(ty::util::AlwaysRequiresDrop) => return true,
            // If we've got a single component, select with that
            // to increase the chance that we hit the selection cache.
            Ok([t]) => ty = t,
            Ok([..]) => {}
        }

        let drop_trait = if let Some(did) = cx.tcx.lang_items().drop_trait() {
            did
        } else {
            // there is no way to define a type that needs non-const drop
            // without having the lang item present.
            return false;
        };
        let trait_ref =
            ty::TraitRef { def_id: drop_trait, substs: cx.tcx.mk_substs_trait(ty, &[]) };
        let obligation = Obligation::new(
            ObligationCause::dummy(),
            cx.param_env,
            ty::Binder::dummy(ty::TraitPredicate {
                trait_ref,
                constness: ty::BoundConstness::ConstIfConst,
            }),
        );

        let implsrc = cx.tcx.infer_ctxt().enter(|infcx| {
            let mut selcx = SelectionContext::with_constness(&infcx, hir::Constness::Const);
            selcx.select(&obligation)
        });
        match implsrc {
            Ok(Some(ImplSource::ConstDrop(_)))
            | Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => false,
            _ => true,
        }
    }

    fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
        adt.has_non_const_dtor(cx.tcx)
    }
}

/// A constant that cannot be used as part of a pattern in a `match` expression.
pub struct CustomEq;

impl Qualif for CustomEq {
    const ANALYSIS_NAME: &'static str = "flow_custom_eq";

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.custom_eq
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        // If *any* component of a composite data type does not implement `Structural{Partial,}Eq`,
        // we know that at least some values of that type are not structural-match. I say "some"
        // because that component may be part of an enum variant (e.g.,
        // `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
        // structural-match (`Option::None`).
        let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id());
        traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).is_some()
    }

    fn in_adt_inherently(
        cx: &ConstCx<'_, 'tcx>,
        adt: &'tcx AdtDef,
        substs: SubstsRef<'tcx>,
    ) -> bool {
        let ty = cx.tcx.mk_ty(ty::Adt(adt, substs));
        !ty.is_structural_eq_shallow(cx.tcx)
    }
}

// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return.

/// Returns `true` if this `Rvalue` contains qualif `Q`.
pub fn in_rvalue<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, rvalue: &Rvalue<'tcx>) -> bool
where
    Q: Qualif,
    F: FnMut(Local) -> bool,
{
    match rvalue {
        Rvalue::ThreadLocalRef(_) | Rvalue::NullaryOp(..) => {
            Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx))
        }

        Rvalue::Discriminant(place) | Rvalue::Len(place) => {
            in_place::<Q, _>(cx, in_local, place.as_ref())
        }

        Rvalue::Use(operand)
        | Rvalue::Repeat(operand, _)
        | Rvalue::UnaryOp(_, operand)
        | Rvalue::Cast(_, operand, _) => in_operand::<Q, _>(cx, in_local, operand),

        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
            in_operand::<Q, _>(cx, in_local, lhs) || in_operand::<Q, _>(cx, in_local, rhs)
        }

        Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
            // Special-case reborrows to be more like a copy of the reference.
            if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() {
                let base_ty = place_base.ty(cx.body, cx.tcx).ty;
                if let ty::Ref(..) = base_ty.kind() {
                    return in_place::<Q, _>(cx, in_local, place_base);
                }
            }

            in_place::<Q, _>(cx, in_local, place.as_ref())
        }

        Rvalue::Aggregate(kind, operands) => {
            // Return early if we know that the struct or enum being constructed is always
            // qualified.
            if let AggregateKind::Adt(def, _, substs, ..) = **kind {
                if Q::in_adt_inherently(cx, def, substs) {
                    return true;
                }
            }

            // Otherwise, proceed structurally...
            operands.iter().any(|o| in_operand::<Q, _>(cx, in_local, o))
        }
    }
}

/// Returns `true` if this `Place` contains qualif `Q`.
pub fn in_place<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, place: PlaceRef<'tcx>) -> bool
where
    Q: Qualif,
    F: FnMut(Local) -> bool,
{
    let mut place = place;
    while let Some((place_base, elem)) = place.last_projection() {
        match elem {
            ProjectionElem::Index(index) if in_local(index) => return true,

            ProjectionElem::Deref
            | ProjectionElem::Field(_, _)
            | ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Downcast(_, _)
            | ProjectionElem::Index(_) => {}
        }

        let base_ty = place_base.ty(cx.body, cx.tcx);
        let proj_ty = base_ty.projection_ty(cx.tcx, elem).ty;
        if !Q::in_any_value_of_ty(cx, proj_ty) {
            return false;
        }

        place = place_base;
    }

    assert!(place.projection.is_empty());
    in_local(place.local)
}

/// Returns `true` if this `Operand` contains qualif `Q`.
pub fn in_operand<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, operand: &Operand<'tcx>) -> bool
where
    Q: Qualif,
    F: FnMut(Local) -> bool,
{
    let constant = match operand {
        Operand::Copy(place) | Operand::Move(place) => {
            return in_place::<Q, _>(cx, in_local, place.as_ref());
        }

        Operand::Constant(c) => c,
    };

    // Check the qualifs of the value of `const` items.
    if let Some(ct) = constant.literal.const_for_ty() {
        if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted }) = ct.val {
            assert!(promoted.is_none());
            // Don't peek inside trait associated constants.
            if cx.tcx.trait_of_item(def.did).is_none() {
                let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
                    cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did))
                } else {
                    cx.tcx.at(constant.span).mir_const_qualif(def.did)
                };

                if !Q::in_qualifs(&qualifs) {
                    return false;
                }

                // Just in case the type is more specific than
                // the definition, e.g., impl associated const
                // with type parameters, take it into account.
            }
        }
    }
    // Otherwise use the qualifs of the type.
    Q::in_any_value_of_ty(cx, constant.literal.ty())
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::convert::TryInto;
use std::slice;

use net::NetworkInterface;
use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache, Routes};
#[cfg(feature = "trace")]
use smoltcp::phy::EthernetTracer;
use smoltcp::phy::{self, Device, DeviceCapabilities};
use smoltcp::socket::SocketSet;
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};

extern "Rust" {
	fn sys_get_mac_address() -> Result<[u8; 6], ()>;
	fn sys_get_mtu() -> Result<u16, ()>;
	fn sys_get_tx_buffer(len: usize) -> Result<(*mut u8, usize), ()>;
	fn sys_send_tx_buffer(handle: usize, len: usize) -> Result<(), ()>;
	fn sys_receive_rx_buffer() -> Result<&'static mut [u8], ()>;
	fn sys_rx_buffer_consumed() -> Result<(), ()>;
	fn sys_free_tx_buffer(handle: usize);
}

/// Data type to determine the mac address
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct HermitNet {
	pub mtu: u16,
}

impl HermitNet {
	pub fn new(mtu: u16) -> Self {
		Self { mtu }
	}
}

impl NetworkInterface<HermitNet> {
	pub fn new() -> Option<Self> {
		let mtu = match unsafe { sys_get_mtu() } {
			Ok(mtu) => mtu,
			Err(_) => {
				return None;
			}
		};
		let device = HermitNet::new(mtu);
		#[cfg(feature = "trace")]
		let device = EthernetTracer::new(device, |_timestamp, printer| {
			trace!("{}", printer);
		});

		let mac: [u8; 6] = match unsafe { sys_get_mac_address() } {
			Ok(mac) => mac,
			Err(_) => {
				return None;
			}
		};
		let myip: [u8; 4] = [10, 0, 5, 3];
		let mygw: [u8; 4] = [10, 0, 5, 1];
		let mymask: [u8; 4] = [255, 255, 255, 0];

		// calculate the netmask length
		// => count the number of contiguous 1 bits,
		// starting at the most significant bit in the first octet
		let mut prefix_len = (!mymask[0]).trailing_zeros();
		if prefix_len == 8 {
			prefix_len += (!mymask[1]).trailing_zeros();
		}
		if prefix_len == 16 {
			prefix_len += (!mymask[2]).trailing_zeros();
		}
		if prefix_len == 24 {
			prefix_len += (!mymask[3]).trailing_zeros();
		}

		let neighbor_cache = NeighborCache::new(BTreeMap::new());
		let ethernet_addr = EthernetAddress([mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]]);
		let ip_addrs = [IpCidr::new(
			IpAddress::v4(myip[0], myip[1], myip[2], myip[3]),
			prefix_len.try_into().unwrap(),
		)];
		let default_v4_gw = Ipv4Address::new(mygw[0], mygw[1], mygw[2], mygw[3]);
		let mut routes = Routes::new(BTreeMap::new());
		routes.add_default_ipv4_route(default_v4_gw).unwrap();

		info!("MAC address {}", ethernet_addr);
		info!("Configure network interface with address {}", ip_addrs[0]);
		info!("Configure gatway with address {}", default_v4_gw);
		info!("MTU: {} bytes", mtu);

		let iface = EthernetInterfaceBuilder::new(device)
			.ethernet_addr(ethernet_addr)
			.neighbor_cache(neighbor_cache)
			.ip_addrs(ip_addrs)
			.routes(routes)
			.finalize();

		Some(Self {
			iface: iface,
			sockets: SocketSet::new(vec![]),
			channels: BTreeMap::new(),
			timestamp: Instant::now(),
		})
	}
}

impl<'a> Device<'a> for HermitNet {
	type RxToken = RxToken;
	type TxToken = TxToken;

	fn capabilities(&self) -> DeviceCapabilities {
		let mut cap = DeviceCapabilities::default();
		cap.max_transmission_unit = self.mtu.into();
		cap
	}

	fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
		match unsafe { sys_receive_rx_buffer() } {
			Ok(buffer) => Some((RxToken::new(buffer), TxToken::new())),
			_ => None,
		}
	}

	fn transmit(&'a mut self) -> Option<Self::TxToken> {
		trace!("create TxToken to transfer data");
		Some(TxToken::new())
	}
}

#[doc(hidden)]
pub struct RxToken {
	buffer: &'static mut [u8],
}

impl RxToken {
	pub fn new(buffer: &'static mut [u8]) -> Self {
		Self { buffer }
	}
}

impl phy::RxToken for RxToken {
	fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> smoltcp::Result<R>
	where
		F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
	{
		let result = f(self.buffer);
		if unsafe { sys_rx_buffer_consumed().is_ok() } {
			result
		} else {
			Err(smoltcp::Error::Exhausted)
		}
	}
}

#[doc(hidden)]
pub struct TxToken;

impl TxToken {
	pub fn new() -> Self {
		Self {}
	}
}

impl phy::TxToken for TxToken {
	fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
	where
		F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
	{
		let (tx_buffer, handle) =
			unsafe { sys_get_tx_buffer(len).map_err(|_| smoltcp::Error::Exhausted)? };
		let tx_slice: &'static mut [u8] = unsafe { slice::from_raw_parts_mut(tx_buffer, len) };
		match f(tx_slice) {
			Ok(result) => {
				if unsafe { sys_send_tx_buffer(handle, len).is_ok() } {
					Ok(result)
				} else {
					Err(smoltcp::Error::Exhausted)
				}
			}
			Err(e) => {
				unsafe { sys_free_tx_buffer(handle) };
				Err(e)
			}
		}
	}
}
<commit_msg>increasing the readability<commit_after>use std::collections::BTreeMap;
use std::convert::TryInto;
use std::slice;

use net::NetworkInterface;
use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache, Routes};
#[cfg(feature = "trace")]
use smoltcp::phy::EthernetTracer;
use smoltcp::phy::{self, Device, DeviceCapabilities};
use smoltcp::socket::SocketSet;
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};

extern "Rust" {
	fn sys_get_mac_address() -> Result<[u8; 6], ()>;
	fn sys_get_mtu() -> Result<u16, ()>;
	fn sys_get_tx_buffer(len: usize) -> Result<(*mut u8, usize), ()>;
	fn sys_send_tx_buffer(handle: usize, len: usize) -> Result<(), ()>;
	fn sys_receive_rx_buffer() -> Result<&'static mut [u8], ()>;
	fn sys_rx_buffer_consumed() -> Result<(), ()>;
	fn sys_free_tx_buffer(handle: usize);
}

/// Data type to determine the mac address
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct HermitNet {
	pub mtu: u16,
}

impl HermitNet {
	pub fn new(mtu: u16) -> Self {
		Self { mtu }
	}
}

impl NetworkInterface<HermitNet> {
	pub fn new() -> Option<Self> {
		let mtu = match unsafe { sys_get_mtu() } {
			Ok(mtu) => mtu,
			Err(_) => {
				return None;
			}
		};
		let device = HermitNet::new(mtu);
		#[cfg(feature = "trace")]
		let device = EthernetTracer::new(device, |_timestamp, printer| {
			trace!("{}", printer);
		});

		let mac: [u8; 6] = match unsafe { sys_get_mac_address() } {
			Ok(mac) => mac,
			Err(_) => {
				return None;
			}
		};
		let myip: [u8; 4] = [10, 0, 5, 3];
		let mygw: [u8; 4] = [10, 0, 5, 1];
		let mymask: [u8; 4] = [255, 255, 255, 0];

		// calculate the netmask length
		// => count the number of contiguous 1 bits,
		// starting at the most significant bit in the first octet
		let mut prefix_len = (!mymask[0]).trailing_zeros();
		if prefix_len == 8 {
			prefix_len += (!mymask[1]).trailing_zeros();
		}
		if prefix_len == 16 {
			prefix_len += (!mymask[2]).trailing_zeros();
		}
		if prefix_len == 24 {
			prefix_len += (!mymask[3]).trailing_zeros();
		}

		let neighbor_cache = NeighborCache::new(BTreeMap::new());
		let ethernet_addr = EthernetAddress([mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]]);
		let ip_addrs = [IpCidr::new(
			IpAddress::v4(myip[0], myip[1], myip[2], myip[3]),
			prefix_len.try_into().unwrap(),
		)];
		let default_v4_gw = Ipv4Address::new(mygw[0], mygw[1], mygw[2], mygw[3]);
		let mut routes = Routes::new(BTreeMap::new());
		routes.add_default_ipv4_route(default_v4_gw).unwrap();

		info!("MAC address {}", ethernet_addr);
		info!("Configure network interface with address {}", ip_addrs[0]);
		info!("Configure gatway with address {}", default_v4_gw);
		info!("MTU: {} bytes", mtu);

		let iface = EthernetInterfaceBuilder::new(device)
			.ethernet_addr(ethernet_addr)
			.neighbor_cache(neighbor_cache)
			.ip_addrs(ip_addrs)
			.routes(routes)
			.finalize();

		Some(Self {
			iface: iface,
			sockets: SocketSet::new(vec![]),
			channels: BTreeMap::new(),
			timestamp: Instant::now(),
		})
	}
}

impl<'a> Device<'a> for HermitNet {
	type RxToken = RxToken;
	type TxToken = TxToken;

	fn capabilities(&self) -> DeviceCapabilities {
		let mut cap = DeviceCapabilities::default();
		cap.max_transmission_unit = self.mtu.into();
		cap
	}

	fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
		match unsafe { sys_receive_rx_buffer() } {
			Ok(buffer) => Some((RxToken::new(buffer), TxToken::new())),
			_ => None,
		}
	}

	fn transmit(&'a mut self) -> Option<Self::TxToken> {
		trace!("create TxToken to transfer data");
		Some(TxToken::new())
	}
}

#[doc(hidden)]
pub struct RxToken {
	buffer: &'static mut [u8],
}

impl RxToken {
	pub fn new(buffer: &'static mut [u8]) -> Self {
		Self { buffer }
	}
}

impl phy::RxToken for RxToken {
	fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> smoltcp::Result<R>
	where
		F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
	{
		let result = f(self.buffer);
		if unsafe { sys_rx_buffer_consumed().is_ok() } {
			result
		} else {
			Err(smoltcp::Error::Exhausted)
		}
	}
}

#[doc(hidden)]
pub struct TxToken;

impl TxToken {
	pub fn new() -> Self {
		Self {}
	}
}

impl phy::TxToken for TxToken {
	fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
	where
		F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
	{
		let (tx_buffer, handle) =
			unsafe { sys_get_tx_buffer(len).map_err(|_| smoltcp::Error::Exhausted)? };
		let tx_slice: &'static mut [u8] = unsafe { slice::from_raw_parts_mut(tx_buffer, len) };
		match f(tx_slice) {
			Ok(result) => {
				if unsafe { sys_send_tx_buffer(handle, len).is_ok() } {
					Ok(result)
				} else {
					Err(smoltcp::Error::Exhausted)
				}
			}
			Err(e) => {
				unsafe { sys_free_tx_buffer(handle) };
				Err(e)
			}
		}
	}
}
<|endoftext|>


<commit_before>use std::fmt;
pub use self::DecodeError::*;

const BASE: u64 = 62;
const ALPHABET: [u8; BASE as usize] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
                                       b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J',
                                       b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T',
                                       b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd',
                                       b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n',
                                       b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x',
                                       b'y', b'z'];

pub fn encode(mut num: u64) -> String {
    if num == 0 {
        return "0".to_owned();
    }
    let mut bytes = Vec::new();

    while num > 0 {
        bytes.push(ALPHABET[(num % BASE) as usize]);
        num /= BASE
    }
    bytes.reverse();

    String::from_utf8(bytes).unwrap()
}

pub enum DecodeError {
    InvalidBase62Byte(char, usize),
    ArithmeticOverflow,
}

impl fmt::Debug for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidBase62Byte(ch, idx) => {
                write!(f, "Invalid character '{}' at position {}", ch, idx)
            }
            ArithmeticOverflow => write!(f, "Decode result is too large"),
        }
    }
}

impl fmt::Display for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self, f)
    }
}

pub fn decode(string: &str) -> Result<u64, DecodeError> {
    let mut result = 0;

    for (i, c) in string.as_bytes().iter().rev().enumerate() {
        let num = BASE.pow(i as u32);
        match ALPHABET.binary_search(&c) {
            Ok(v) => {
                match (v as u64).checked_mul(num) {
                    Some(z) => result += z,
                    None => return Err(ArithmeticOverflow),
                }
            }
            Err(_e) => {
                return Err(InvalidBase62Byte(c.to_owned() as char, string.len() - i));
            }
        }
    }

    Ok(result)
}

#[cfg(test)]
mod tests {
    use base62;

    #[test]
    fn test_encode() {
        assert_eq!(base62::encode(852751187393), "F0ob4rZ");
    }

    #[test]
    fn test_decode() {
        assert_eq!(base62::decode("F0ob4rZ").unwrap(), 852751187393);
    }

    #[test]
    fn test_decode_invalid_char() {
        assert!(base62::decode("ds{Z455f").is_err());
    }

    #[test]
    fn test_decode_long_string() {
        assert!(base62::decode("dsZ455fzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\
                                zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz")
                    .is_err());
    }
}
<commit_msg>Remove needless borrow<commit_after>use std::fmt;
pub use self::DecodeError::*;

const BASE: u64 = 62;
const ALPHABET: [u8; BASE as usize] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
                                       b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J',
                                       b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T',
                                       b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd',
                                       b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n',
                                       b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x',
                                       b'y', b'z'];

pub fn encode(mut num: u64) -> String {
    if num == 0 {
        return "0".to_owned();
    }
    let mut bytes = Vec::new();

    while num > 0 {
        bytes.push(ALPHABET[(num % BASE) as usize]);
        num /= BASE
    }
    bytes.reverse();

    String::from_utf8(bytes).unwrap()
}

pub enum DecodeError {
    InvalidBase62Byte(char, usize),
    ArithmeticOverflow,
}

impl fmt::Debug for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidBase62Byte(ch, idx) => {
                write!(f, "Invalid character '{}' at position {}", ch, idx)
            }
            ArithmeticOverflow => write!(f, "Decode result is too large"),
        }
    }
}

impl fmt::Display for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self, f)
    }
}

pub fn decode(string: &str) -> Result<u64, DecodeError> {
    let mut result = 0;

    for (i, c) in string.as_bytes().iter().rev().enumerate() {
        let num = BASE.pow(i as u32);
        match ALPHABET.binary_search(c) {
            Ok(v) => {
                match (v as u64).checked_mul(num) {
                    Some(z) => result += z,
                    None => return Err(ArithmeticOverflow),
                }
            }
            Err(_e) => {
                return Err(InvalidBase62Byte(c.to_owned() as char, string.len() - i));
            }
        }
    }

    Ok(result)
}

#[cfg(test)]
mod tests {
    use base62;

    #[test]
    fn test_encode() {
        assert_eq!(base62::encode(852751187393), "F0ob4rZ");
    }

    #[test]
    fn test_decode() {
        assert_eq!(base62::decode("F0ob4rZ").unwrap(), 852751187393);
    }

    #[test]
    fn test_decode_invalid_char() {
        assert!(base62::decode("ds{Z455f").is_err());
    }

    #[test]
    fn test_decode_long_string() {
        assert!(base62::decode("dsZ455fzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\
                                zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz")
                    .is_err());
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate coreutils;

use std::env;
use std::fs;
use std::io::{stdout, stderr, Write};
use coreutils::extra::{OptionalExt, WriteExt};

fn print_path(path: &str) {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();
    let mut entries = Vec::new();

    let dir = fs::read_dir(path).try(&mut stderr);

    for entry_result in dir {
        let entry = entry_result.try(&mut stderr);

        let file_type = entry.file_type().try(&mut stderr);
        let directory = file_type.is_dir();

        if let Some(path_str) = entry.file_name().to_str() {
            entries.push(path_str.to_owned());
            if directory {
                entries.last_mut().unwrap().push('/');
            }
        } else {
            stderr.writeln(b"warning: failed to convert path to a valid string.").try(&mut stderr);
        }
    }

    entries.sort();

    for entry in entries.iter() {
        let mut entry_path = path.to_owned();
        if !entry_path.ends_with('/') {
            entry_path.push('/');
        }
        entry_path.push_str(entry);

        let metadata = fs::metadata(&entry_path).try(&mut stderr);
        let size = metadata.len();

        stdout.write(((size + 1023) / 1024).to_string().as_bytes()).try(&mut stderr);
        stdout.write(b"    ").try(&mut stderr);
        stdout.writeln(entry.as_bytes()).try(&mut stderr);
    }
}
fn main() {
    if let Some(ref x) = env::args().nth(1) {
        print_path(x);
    } else {
        print_path(".");
    }
}
<commit_msg>Fix formatting of du<commit_after>#![deny(warnings)]

extern crate coreutils;

use std::env;
use std::fs;
use std::io::{stdout, stderr, Write};
use coreutils::extra::{OptionalExt, WriteExt};

fn print_path(path: &str) {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();
    let mut entries = Vec::new();

    let dir = fs::read_dir(path).try(&mut stderr);

    for entry_result in dir {
        let entry = entry_result.try(&mut stderr);

        let file_type = entry.file_type().try(&mut stderr);
        let directory = file_type.is_dir();

        if let Some(path_str) = entry.file_name().to_str() {
            entries.push(path_str.to_owned());
            if directory {
                entries.last_mut().unwrap().push('/');
            }
        } else {
            stderr.writeln(b"warning: failed to convert path to a valid string.").try(&mut stderr);
        }
    }

    entries.sort();

    for entry in entries.iter() {
        let mut entry_path = path.to_owned();
        if !entry_path.ends_with('/') {
            entry_path.push('/');
        }
        entry_path.push_str(entry);

        let metadata = fs::metadata(&entry_path).try(&mut stderr);
        let size = metadata.len();

        stdout.write(((size + 1023) / 1024).to_string().as_bytes()).try(&mut stderr);
        stdout.write(b"\t").try(&mut stderr);
        stdout.writeln(entry.as_bytes()).try(&mut stderr);
    }
}
fn main() {
    if let Some(ref x) = env::args().nth(1) {
        print_path(x);
    } else {
        print_path(".");
    }
}
<|endoftext|>


<commit_before>use hyper::Client;
use hyper::client::Body;
use hyper::client::response::Response;
use disco;
use rustc_serialize::json;
use rustc_serialize::json::Json;
use rustc_serialize::{ Decodable, Encodable };
use errors::HueError;
use errors::AppError;
use regex::Regex;

just breaking something

#[derive(Show,Copy,Clone,RustcDecodable)]
pub struct LightState {
    pub on: bool,
    pub bri: u8,
    pub hue: u16,
    pub sat: u8
}

#[derive(Show,Clone,RustcDecodable)]
pub struct Light {
    pub name: String,
    pub modelid: String,
    pub swversion: String,
    pub uniqueid: String,
    pub state: LightState,
}

#[derive(Show,Clone)]
pub struct IdentifiedLight {
    pub id: usize,
    pub light: Light,
}

#[derive(Show,Clone,Copy,RustcEncodable,RustcDecodable)]
pub struct CommandLight {
    pub on:Option<bool>,
    pub bri:Option<u8>,
    pub hue:Option<u16>,
    pub sat:Option<u8>,
    pub transitiontime:Option<u16>,
}

impl CommandLight {
    pub fn empty() -> CommandLight {
        CommandLight { on:None, bri:None, hue:None, sat:None, transitiontime:None }
    }
    pub fn on() -> CommandLight {
        CommandLight { on:Some(true), ..CommandLight::empty() }
    }
    pub fn off() -> CommandLight {
        CommandLight { on:Some(false), ..CommandLight::empty() }
    }
}

#[derive(Show)]
pub struct Bridge {
    ip: String,
    username: Option<String>,
}

impl Bridge {
    #[allow(dead_code)]
    pub fn discover() -> Option<Bridge> {
        disco::discover_hue_bridge().ok().map( |i| Bridge{ ip:i, username:None } )
    }

    pub fn discover_required() -> Bridge {
        Bridge::discover().unwrap_or_else( || panic!("No bridge found!") )
    }

    pub fn with_user(self, username:String) -> Bridge {
        Bridge{ username: Some(username), ..self }
    }

    pub fn register_user(&self, devicetype:&str, username:&str) -> Result<Json,HueError> {
        if username.len() < 10 || username.len() > 40 {
            return HueError::wrap("username must be between 10 and 40 characters")
        }
        #[derive(RustcDecodable, RustcEncodable)]
        struct PostApi {
            devicetype: String,
            username:String
        }
        let obtain = PostApi {
            devicetype:devicetype.to_string(),
            username:username.to_string()
        };
        let body = try!(json::encode(&obtain));
        let mut client = Client::new();
        let url = format!("http://{}/api", self.ip);
        let mut resp = try!(client.post(url.as_slice())
            .body(Body::BufBody(body.as_bytes(), body.as_bytes().len())).send());
        self.parse_write_resp(&mut resp)
    }

    pub fn get_all_lights(&self) -> Result<Vec<IdentifiedLight>,HueError> {
        let url = format!("http://{}/api/{}/lights",
            self.ip, self.username.clone().unwrap());
        let mut client = Client::new();
        let mut resp = try!(client.get(url.as_slice()).send());
        let json = try!(::tools::from_reader(&mut resp));
        let lights:Vec<Result<IdentifiedLight,json::DecoderError>> = json.as_object().unwrap().iter().map( |(k,v)| {
            let mut decoder = json::Decoder::new(v.clone());
            <Light as Decodable>::decode(&mut decoder).map( |l|
                IdentifiedLight{ id: k.parse().unwrap(), light: l }
            )
        }).collect();
        for ref l in lights.iter().cloned() {
            match l {
                &Err(ref e) => {
                    return Err(HueError::JsonDecoderError(e.clone()));
                }
                &Ok(ref _l) => ()
            }
        }
        let mut v:Vec<IdentifiedLight> = lights.iter().cloned().map( |l| l.unwrap() ).collect();
        v.sort_by( |a,b| a.id.cmp(&b.id) );
        Ok(v)
    }

    pub fn set_light_state(&self, light:usize, command:CommandLight) -> Result<Json, HueError> {
        let url = format!("http://{}/api/{}/lights/{}/state",
            self.ip, self.username.clone().unwrap(), light);
        let body = try!(json::encode(&command));
        let re1 = Regex::new("\"[a-z]*\":null").unwrap();
        let cleaned1 = re1.replace_all(body.as_slice(),"");
        let re2 = Regex::new(",+").unwrap();
        let cleaned2 = re2.replace_all(cleaned1.as_slice(),",");
        let re3 = Regex::new(",\\}").unwrap();
        let cleaned3 = re3.replace_all(cleaned2.as_slice(),"}");
        let re3 = Regex::new("\\{,").unwrap();
        let cleaned4 = re3.replace_all(cleaned3.as_slice(),"{");
        let mut client = Client::new();
        let mut resp = try!(client.put(url.as_slice())
            .body(Body::BufBody(cleaned4.as_bytes(), cleaned4.as_bytes().len())).send());
        self.parse_write_resp(&mut resp)
    }

    fn parse_write_resp(&self, resp:&mut Response) -> Result<Json,HueError> {
        let json = try!(::tools::from_reader(resp));
        let objects = try!(json.as_array()
            .ok_or(HueError::Error("expected array".to_string())));
        if objects.len() == 0 {
            return HueError::wrap("expected non-empty array");
        }
        let object = try!(objects[0].as_object()
            .ok_or(HueError::Error("expected first item to be an object".to_string())));
        let obj = object.get(&"error".to_string()).and_then( |o| o.as_object() );
        match obj {
            Some(e) => {
                let error = e.clone();
                let mut decoder = json::Decoder::new(json::Json::Object(error));
                let actual_error = AppError::dec(&mut decoder).ok().unwrap();
                //println!("actual: {:?}",actual_error);
                Err(HueError::BridgeError(actual_error))
            },
            None => Ok(json.clone())
        }
    }
}

<commit_msg>Revert "breaking something to test travis notifs"<commit_after>use hyper::Client;
use hyper::client::Body;
use hyper::client::response::Response;
use disco;
use rustc_serialize::json;
use rustc_serialize::json::Json;
use rustc_serialize::{ Decodable, Encodable };
use errors::HueError;
use errors::AppError;
use regex::Regex;

#[derive(Show,Copy,Clone,RustcDecodable)]
pub struct LightState {
    pub on: bool,
    pub bri: u8,
    pub hue: u16,
    pub sat: u8
}

#[derive(Show,Clone,RustcDecodable)]
pub struct Light {
    pub name: String,
    pub modelid: String,
    pub swversion: String,
    pub uniqueid: String,
    pub state: LightState,
}

#[derive(Show,Clone)]
pub struct IdentifiedLight {
    pub id: usize,
    pub light: Light,
}

#[derive(Show,Clone,Copy,RustcEncodable,RustcDecodable)]
pub struct CommandLight {
    pub on:Option<bool>,
    pub bri:Option<u8>,
    pub hue:Option<u16>,
    pub sat:Option<u8>,
    pub transitiontime:Option<u16>,
}

impl CommandLight {
    pub fn empty() -> CommandLight {
        CommandLight { on:None, bri:None, hue:None, sat:None, transitiontime:None }
    }
    pub fn on() -> CommandLight {
        CommandLight { on:Some(true), ..CommandLight::empty() }
    }
    pub fn off() -> CommandLight {
        CommandLight { on:Some(false), ..CommandLight::empty() }
    }
}

#[derive(Show)]
pub struct Bridge {
    ip: String,
    username: Option<String>,
}

impl Bridge {
    #[allow(dead_code)]
    pub fn discover() -> Option<Bridge> {
        disco::discover_hue_bridge().ok().map( |i| Bridge{ ip:i, username:None } )
    }

    pub fn discover_required() -> Bridge {
        Bridge::discover().unwrap_or_else( || panic!("No bridge found!") )
    }

    pub fn with_user(self, username:String) -> Bridge {
        Bridge{ username: Some(username), ..self }
    }

    pub fn register_user(&self, devicetype:&str, username:&str) -> Result<Json,HueError> {
        if username.len() < 10 || username.len() > 40 {
            return HueError::wrap("username must be between 10 and 40 characters")
        }
        #[derive(RustcDecodable, RustcEncodable)]
        struct PostApi {
            devicetype: String,
            username:String
        }
        let obtain = PostApi {
            devicetype:devicetype.to_string(),
            username:username.to_string()
        };
        let body = try!(json::encode(&obtain));
        let mut client = Client::new();
        let url = format!("http://{}/api", self.ip);
        let mut resp = try!(client.post(url.as_slice())
            .body(Body::BufBody(body.as_bytes(), body.as_bytes().len())).send());
        self.parse_write_resp(&mut resp)
    }

    pub fn get_all_lights(&self) -> Result<Vec<IdentifiedLight>,HueError> {
        let url = format!("http://{}/api/{}/lights",
            self.ip, self.username.clone().unwrap());
        let mut client = Client::new();
        let mut resp = try!(client.get(url.as_slice()).send());
        let json = try!(::tools::from_reader(&mut resp));
        let lights:Vec<Result<IdentifiedLight,json::DecoderError>> = json.as_object().unwrap().iter().map( |(k,v)| {
            let mut decoder = json::Decoder::new(v.clone());
            <Light as Decodable>::decode(&mut decoder).map( |l|
                IdentifiedLight{ id: k.parse().unwrap(), light: l }
            )
        }).collect();
        for ref l in lights.iter().cloned() {
            match l {
                &Err(ref e) => {
                    return Err(HueError::JsonDecoderError(e.clone()));
                }
                &Ok(ref _l) => ()
            }
        }
        let mut v:Vec<IdentifiedLight> = lights.iter().cloned().map( |l| l.unwrap() ).collect();
        v.sort_by( |a,b| a.id.cmp(&b.id) );
        Ok(v)
    }

    pub fn set_light_state(&self, light:usize, command:CommandLight) -> Result<Json, HueError> {
        let url = format!("http://{}/api/{}/lights/{}/state",
            self.ip, self.username.clone().unwrap(), light);
        let body = try!(json::encode(&command));
        let re1 = Regex::new("\"[a-z]*\":null").unwrap();
        let cleaned1 = re1.replace_all(body.as_slice(),"");
        let re2 = Regex::new(",+").unwrap();
        let cleaned2 = re2.replace_all(cleaned1.as_slice(),",");
        let re3 = Regex::new(",\\}").unwrap();
        let cleaned3 = re3.replace_all(cleaned2.as_slice(),"}");
        let re3 = Regex::new("\\{,").unwrap();
        let cleaned4 = re3.replace_all(cleaned3.as_slice(),"{");
        let mut client = Client::new();
        let mut resp = try!(client.put(url.as_slice())
            .body(Body::BufBody(cleaned4.as_bytes(), cleaned4.as_bytes().len())).send());
        self.parse_write_resp(&mut resp)
    }

    fn parse_write_resp(&self, resp:&mut Response) -> Result<Json,HueError> {
        let json = try!(::tools::from_reader(resp));
        let objects = try!(json.as_array()
            .ok_or(HueError::Error("expected array".to_string())));
        if objects.len() == 0 {
            return HueError::wrap("expected non-empty array");
        }
        let object = try!(objects[0].as_object()
            .ok_or(HueError::Error("expected first item to be an object".to_string())));
        let obj = object.get(&"error".to_string()).and_then( |o| o.as_object() );
        match obj {
            Some(e) => {
                let error = e.clone();
                let mut decoder = json::Decoder::new(json::Json::Object(error));
                let actual_error = AppError::dec(&mut decoder).ok().unwrap();
                //println!("actual: {:?}",actual_error);
                Err(HueError::BridgeError(actual_error))
            },
            None => Ok(json.clone())
        }
    }
}

<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern mod freetype;
extern mod fontconfig;

use fontconfig::fontconfig::{
    FcChar8, FcResultMatch, FcSetSystem,
    FcResultNoMatch, FcMatchPattern, FC_SLANT_ITALIC, FC_WEIGHT_BOLD
};
use fontconfig::fontconfig::bindgen::{
    FcConfigGetCurrent, FcConfigGetFonts, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy, FcConfigSubstitute,
    FcDefaultSubstitute, FcPatternCreate, FcPatternAddString, FcPatternAddInteger,
    FcFontMatch, FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use font::{FontHandleMethods, UsedFontStyle};
use font_context::FontContextHandleMethods;
use font_list::{FontEntry, FontFamily, FontFamilyMap};
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;

use core::hashmap::HashMap;
use core::libc::c_int;
use core::ptr::Ptr;

pub struct FontListHandle {
    fctx: FontContextHandle,
}

pub impl FontListHandle {
    pub fn new(fctx: &FontContextHandle) -> FontListHandle {
        FontListHandle { fctx: fctx.clone() }
    }

    fn get_available_families(&self) -> FontFamilyMap {
        let mut family_map : FontFamilyMap = HashMap::new();
        unsafe {
            let config = FcConfigGetCurrent();
            let fontSet = FcConfigGetFonts(config, FcSetSystem);
            for uint::range(0, (*fontSet).nfont as uint) |i| {
                let font = (*fontSet).fonts.offset(i);
                let family: *FcChar8 = ptr::null();
                let mut v: c_int = 0;
                do str::as_c_str("family") |FC_FAMILY| {
                    while FcPatternGetString(*font, FC_FAMILY, v, &family) == FcResultMatch {
                        let family_name = str::raw::from_buf(family as *u8);
                        debug!("Creating new FontFamily for family: %s", family_name);
                        let new_family = @mut FontFamily::new(family_name);
                        family_map.insert(family_name, new_family);
                        v += 1;
                    }
                }
            }
        }
        return family_map;
    }

    fn load_variations_for_family(&self, family: @mut FontFamily) {
        debug!("getting variations for %?", family);
        let config = FcConfigGetCurrent();
        let font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = ptr::to_unsafe_ptr(&font_set);
        unsafe {
            let pattern = FcPatternCreate();
            let pattern = FcPatternCreate();
            assert!(pattern.is_not_null());
            do str::as_c_str("family") |FC_FAMILY| {
                do str::as_c_str(family.family_name) |family_name| {
                    let ok = FcPatternAddString(pattern, FC_FAMILY, family_name as *FcChar8);
                    assert!(ok != 0);
                }
            }

            let object_set = FcObjectSetCreate();
            assert!(object_set.is_not_null());

            do str::as_c_str("file") |FC_FILE| {
                FcObjectSetAdd(object_set, FC_FILE);
            }
            do str::as_c_str("index") |FC_INDEX| {
                FcObjectSetAdd(object_set, FC_INDEX);
            }

            let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

            debug!("found %? variations", (*matches).nfont);

            for uint::range(0, (*matches).nfont as uint) |i| {
                let font = (*matches).fonts.offset(i);
                let file = do str::as_c_str("file") |FC_FILE| {
                    let file: *FcChar8 = ptr::null();
                    if FcPatternGetString(*font, FC_FILE, 0, &file) == FcResultMatch {
                        str::raw::from_c_str(file as *libc::c_char)
                    } else {
                        fail!();
                    }
                };
                let index = do str::as_c_str("index") |FC_INDEX| {
                    let index: libc::c_int = 0;
                    if FcPatternGetInteger(*font, FC_INDEX, 0, &index) == FcResultMatch {
                        index
                    } else {
                        fail!();
                    }
                };

                debug!("variation file: %?", file);
                debug!("variation index: %?", index);

                let font_handle = FontHandle::new_from_file_unstyled(&self.fctx,
                                                                     file);
                let font_handle = font_handle.unwrap();

                debug!("Creating new FontEntry for face: %s", font_handle.face_name());
                let entry = @FontEntry::new(family, font_handle);
                family.entries.push(entry);
            }

            FcFontSetDestroy(matches);
            FcPatternDestroy(pattern);
            FcObjectSetDestroy(object_set);
        }
    }
}

pub fn path_from_identifier(name: ~str, style: &UsedFontStyle) -> Result<~str, ()> {
    unsafe {
        let config = FcConfigGetCurrent();
        let pattern = FcPatternCreate();
        let res = do str::as_c_str("family") |FC_FAMILY| {
            do str::as_c_str(name) |family| {
                FcPatternAddString(pattern, FC_FAMILY, family as *FcChar8)
            }
        };
        if res != 1 {
            debug!("adding family to pattern failed");
            return Err(());
        }

        if style.italic {
            let res = do str::as_c_str("slant") |FC_SLANT| {
                FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC)
            };
            if res != 1 {
                debug!("adding slant to pattern failed");
                return Err(());
            }
        }
        if style.weight.is_bold() {
            let res = do str::as_c_str("weight") |FC_WEIGHT| {
                FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD)
            };
            if res != 1 {
                debug!("adding weight to pattern failed");
                return Err(());
            }
        }

        if FcConfigSubstitute(config, pattern, FcMatchPattern) != 1 {
            debug!("substitution failed");
            return Err(());
        }
        FcDefaultSubstitute(pattern);
        let result = FcResultNoMatch;
        let result_pattern = FcFontMatch(config, pattern, &result);
        if result != FcResultMatch && result_pattern.is_null() {
            debug!("obtaining match to pattern failed");
            return Err(());
        }

        let file: *FcChar8 = ptr::null();
        let res = do str::as_c_str("file") |FC_FILE| {
            FcPatternGetString(result_pattern, FC_FILE, 0, &file)
        };
        if res != FcResultMatch {
            debug!("getting filename for font failed");
            return Err(());
        }
        Ok(str::raw::from_buf(file as *u8))
    }
}
<commit_msg>Remove spurious line.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern mod freetype;
extern mod fontconfig;

use fontconfig::fontconfig::{
    FcChar8, FcResultMatch, FcSetSystem,
    FcResultNoMatch, FcMatchPattern, FC_SLANT_ITALIC, FC_WEIGHT_BOLD
};
use fontconfig::fontconfig::bindgen::{
    FcConfigGetCurrent, FcConfigGetFonts, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy, FcConfigSubstitute,
    FcDefaultSubstitute, FcPatternCreate, FcPatternAddString, FcPatternAddInteger,
    FcFontMatch, FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use font::{FontHandleMethods, UsedFontStyle};
use font_context::FontContextHandleMethods;
use font_list::{FontEntry, FontFamily, FontFamilyMap};
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;

use core::hashmap::HashMap;
use core::libc::c_int;
use core::ptr::Ptr;

pub struct FontListHandle {
    fctx: FontContextHandle,
}

pub impl FontListHandle {
    pub fn new(fctx: &FontContextHandle) -> FontListHandle {
        FontListHandle { fctx: fctx.clone() }
    }

    fn get_available_families(&self) -> FontFamilyMap {
        let mut family_map : FontFamilyMap = HashMap::new();
        unsafe {
            let config = FcConfigGetCurrent();
            let fontSet = FcConfigGetFonts(config, FcSetSystem);
            for uint::range(0, (*fontSet).nfont as uint) |i| {
                let font = (*fontSet).fonts.offset(i);
                let family: *FcChar8 = ptr::null();
                let mut v: c_int = 0;
                do str::as_c_str("family") |FC_FAMILY| {
                    while FcPatternGetString(*font, FC_FAMILY, v, &family) == FcResultMatch {
                        let family_name = str::raw::from_buf(family as *u8);
                        debug!("Creating new FontFamily for family: %s", family_name);
                        let new_family = @mut FontFamily::new(family_name);
                        family_map.insert(family_name, new_family);
                        v += 1;
                    }
                }
            }
        }
        return family_map;
    }

    fn load_variations_for_family(&self, family: @mut FontFamily) {
        debug!("getting variations for %?", family);
        let config = FcConfigGetCurrent();
        let font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = ptr::to_unsafe_ptr(&font_set);
        unsafe {
            let pattern = FcPatternCreate();
            assert!(pattern.is_not_null());
            do str::as_c_str("family") |FC_FAMILY| {
                do str::as_c_str(family.family_name) |family_name| {
                    let ok = FcPatternAddString(pattern, FC_FAMILY, family_name as *FcChar8);
                    assert!(ok != 0);
                }
            }

            let object_set = FcObjectSetCreate();
            assert!(object_set.is_not_null());

            do str::as_c_str("file") |FC_FILE| {
                FcObjectSetAdd(object_set, FC_FILE);
            }
            do str::as_c_str("index") |FC_INDEX| {
                FcObjectSetAdd(object_set, FC_INDEX);
            }

            let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

            debug!("found %? variations", (*matches).nfont);

            for uint::range(0, (*matches).nfont as uint) |i| {
                let font = (*matches).fonts.offset(i);
                let file = do str::as_c_str("file") |FC_FILE| {
                    let file: *FcChar8 = ptr::null();
                    if FcPatternGetString(*font, FC_FILE, 0, &file) == FcResultMatch {
                        str::raw::from_c_str(file as *libc::c_char)
                    } else {
                        fail!();
                    }
                };
                let index = do str::as_c_str("index") |FC_INDEX| {
                    let index: libc::c_int = 0;
                    if FcPatternGetInteger(*font, FC_INDEX, 0, &index) == FcResultMatch {
                        index
                    } else {
                        fail!();
                    }
                };

                debug!("variation file: %?", file);
                debug!("variation index: %?", index);

                let font_handle = FontHandle::new_from_file_unstyled(&self.fctx,
                                                                     file);
                let font_handle = font_handle.unwrap();

                debug!("Creating new FontEntry for face: %s", font_handle.face_name());
                let entry = @FontEntry::new(family, font_handle);
                family.entries.push(entry);
            }

            FcFontSetDestroy(matches);
            FcPatternDestroy(pattern);
            FcObjectSetDestroy(object_set);
        }
    }
}

pub fn path_from_identifier(name: ~str, style: &UsedFontStyle) -> Result<~str, ()> {
    unsafe {
        let config = FcConfigGetCurrent();
        let pattern = FcPatternCreate();
        let res = do str::as_c_str("family") |FC_FAMILY| {
            do str::as_c_str(name) |family| {
                FcPatternAddString(pattern, FC_FAMILY, family as *FcChar8)
            }
        };
        if res != 1 {
            debug!("adding family to pattern failed");
            return Err(());
        }

        if style.italic {
            let res = do str::as_c_str("slant") |FC_SLANT| {
                FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC)
            };
            if res != 1 {
                debug!("adding slant to pattern failed");
                return Err(());
            }
        }
        if style.weight.is_bold() {
            let res = do str::as_c_str("weight") |FC_WEIGHT| {
                FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD)
            };
            if res != 1 {
                debug!("adding weight to pattern failed");
                return Err(());
            }
        }

        if FcConfigSubstitute(config, pattern, FcMatchPattern) != 1 {
            debug!("substitution failed");
            return Err(());
        }
        FcDefaultSubstitute(pattern);
        let result = FcResultNoMatch;
        let result_pattern = FcFontMatch(config, pattern, &result);
        if result != FcResultMatch && result_pattern.is_null() {
            debug!("obtaining match to pattern failed");
            return Err(());
        }

        let file: *FcChar8 = ptr::null();
        let res = do str::as_c_str("file") |FC_FILE| {
            FcPatternGetString(result_pattern, FC_FILE, 0, &file)
        };
        if res != FcResultMatch {
            debug!("getting filename for font failed");
            return Err(());
        }
        Ok(str::raw::from_buf(file as *u8))
    }
}
<|endoftext|>