<commit_before>use conrod::{Button,Positionable, Text, Sizeable,Widget,WidgetId,Canvas,Colorable,color,Labelable,Line,Circle};
use piston_window::Glyphs;
use ::{Backend,Graph,GraphNode,GraphEdge,Nid};

use std::collections::HashMap;

pub type Ui = ::conrod::Ui<Glyphs>;

pub trait UiNode: GraphNode {
    fn get_ui(&self) -> &UiBase;
    fn get_ui_mut(&mut self) -> &mut UiBase;
    fn build_ui(&mut self, ui: &mut Ui) {

        if self.get_ui().destroy { return }

        let mut canvas_height = self.get_ui().height;
        let mut cl = "-"; //canvas label
        if self.get_ui().collapse {
            cl = "+";
            canvas_height = 20.0;
        }

        let ui_id_start: WidgetId = self.get_ui().get_id();

        let position = self.get_position().clone();

        let mut color = color::WHITE;
        if self.get_ui().select { color=color::RED; }

        //floating canvas where our node data resides
        Canvas::new()
            .floating(true)
            .title_bar(self.get_name())
            .xy([position[0], position[1]])
            .h(canvas_height)
            .w(self.get_ui().width)
            .color(color::BLUE)
            .label_color(color)
            .set(ui_id_start, ui);

        //build buttons in reverse order, to stay within canvas
        //start at top right, and head left from
        //close button
        Button::new()
            .top_right_of(ui_id_start) //todo: shift up ~5.0
            .label("x")
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().destroy = true)
            .set(ui_id_start + 1, ui);

        //collapse
        Button::new()
            .left(5.0)
            .label(cl)
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().toggle_collapse())
            .set(ui_id_start + 2, ui);

        //connector
        Button::new()
            .top_left_of(ui_id_start) //todo: shift up ~5.0
            .label("o")
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().toggle_select())
            .set(ui_id_start + 3, ui);

        if !self.get_ui().collapse {
            Text::new("Node Data") // TODO: derive actual node data, and represent somehow
                .middle_of(ui_id_start)
                .set(ui_id_start + 4, ui);
        }
    }
}

#[derive(Debug,Clone,PartialEq)]
pub struct UiBase {
    ui_id: WidgetId,
    select: bool,
    collapse: bool,
    destroy: bool,
    width: f64,
    height: f64,
}

impl UiBase {
    pub fn default() -> UiBase {
        UiBase { ui_id: WidgetId(10000),
                 select: false,
                 collapse: false,
                 destroy: false,
                 width: 150.0,
                 height: 100.0,
        }
    }
    pub fn toggle_destroy(&mut self) { self.destroy = !self.destroy; }
    pub fn toggle_collapse(&mut self) { self.collapse = !self.collapse; }
    pub fn toggle_select(&mut self) { self.select = !self.select; }

    pub fn get_id(&self) -> WidgetId { self.ui_id }
    pub fn set_id(&mut self, id: WidgetId) { self.ui_id = id; }
}

pub trait UiGraph {
    fn render(&mut self, ui: &mut Ui);
}

impl<E:GraphEdge,N:UiNode> UiGraph for Graph<E,N> {
    fn render(&mut self, ui: &mut Ui) {
        let mut select: (Option<Nid>,Option<Nid>) = (None,None);
        let mut edges: Vec<(Nid,Vec<Nid>)> = vec!();

        // NOTE: this might change when we start tracking connection types
        let mut sockets_from = HashMap::new(); // track socket connections for layout

        self.with_nodes_mut(|n| {
            let is_select = n.get_ui().select;

            {
                let base = n.get_base();
                if is_select { //selected?
                    if select.0.is_some() {
                        if !select.1.is_some() {
                            select.1 = Some(base.get_id());
                        }
                    }
                    else { select.0 = Some(base.get_id()); }
                }

                let ev = base.get_edges();
                if ev.len() > 0 {
                    edges.push((base.get_id(),ev));
                }
            }

            if let Some(coord) = ui.xy_of(n.get_ui().get_id()) {
                n.set_position(coord);
            }
        });

        let socket_id_out = 1;
        let line_id = 2;
        let socket_id_in = 3;

        let socket_size = 10.;
        let socket_offset = 3.*socket_size;

        // build edges
        // NOTE: these edges represent forward-edges only
        // TODO: break out socket and line positioning into some sort of method
        for &(ref nid,ref ev) in edges.iter() {
            let n = self.get_node(&nid).unwrap();
            let nui = n.get_ui();

            let id = nui.get_id().0 * ::MAX_NODES * (::MAX_CONN_IN+::MAX_CONN_OUT) +1;

            let mut from_pos = *n.get_position();

            for (k,en) in ev.iter().enumerate() {
                if !nui.collapse {
                    from_pos[1] -= k as f64+socket_offset;
                }
                from_pos[0] -= nui.width/2.;

                let k = k + 1;

                let from_count = sockets_from.entry(en).or_insert(0);
                *from_count += 1; //inc sock count from-connections

                if let Some(n2) = self.get_node(&en) {
                    let mut to_pos = *n2.get_position();
                    if !n2.get_ui().collapse {
                        to_pos[1] -= *from_count as f64 * socket_offset;
                    }
                    to_pos[0] += n2.get_ui().width/2.;

                    Line::abs(from_pos, to_pos)
                        .set(WidgetId(id*line_id*k), ui);
                }
            }
        }

        self.with_nodes_mut(|n| {
            n.build_ui(ui);
        });

        // build sockets
        // NOTE: this duplication from 'build_edges' should be fixed
        // only way I could figure out proper draw order
        for (j,&(ref nid,ref ev)) in edges.iter().enumerate() {
            let n = self.get_node(&nid).unwrap();
            let nui = n.get_ui();

            let id = nui.get_id().0 * ::MAX_NODES + 1;

            let mut from_pos = *n.get_position();
            from_pos[0] -= nui.width/2.;

            for (k,en) in ev.iter().enumerate() {
                let k = k + 1;
                let from_count = sockets_from.get(en).unwrap();

                if !nui.collapse {
                    from_pos[1] -= (j + k) as f64+socket_offset;
                }

                Circle::fill_with(socket_size,color::LIGHT_BLUE)
                    .xy(from_pos)
                    .set(WidgetId((id+j+k)*socket_id_out), ui);

                if let Some(n2) = self.get_node(&en) {
                    let mut to_pos = *n2.get_position();
                    if !n2.get_ui().collapse {
                        to_pos[1] -= *from_count as f64 * socket_offset;
                    }
                    to_pos[0] += n2.get_ui().width/2.;

                    Circle::fill_with(socket_size,color::ORANGE)
                        .xy(to_pos)
                        .set(WidgetId((id+j+k)*socket_id_in), ui);
                }
            }
        }

        // build new edge
        match select {
            (Some(first),Some(second)) => {
                //clear node selection
                self.get_node_mut(&first).unwrap().get_ui_mut().select = false;
                self.get_node_mut(&second).unwrap().get_ui_mut().select = false;

                //todo: prompt for edge data!
                self.direct(&first,&second,E::default());
            },
            _ => (),
        }
    }
}
<commit_msg>tweak widget id's<commit_after>use conrod::{Button,Positionable, Text, Sizeable,Widget,WidgetId,Canvas,Colorable,color,Labelable,Line,Circle};
use piston_window::Glyphs;
use ::{Backend,Graph,GraphNode,GraphEdge,Nid};

use std::collections::HashMap;

pub type Ui = ::conrod::Ui<Glyphs>;

pub trait UiNode: GraphNode {
    fn get_ui(&self) -> &UiBase;
    fn get_ui_mut(&mut self) -> &mut UiBase;
    fn build_ui(&mut self, ui: &mut Ui) {

        if self.get_ui().destroy { return }

        let mut canvas_height = self.get_ui().height;
        let mut cl = "-"; //canvas label
        if self.get_ui().collapse {
            cl = "+";
            canvas_height = 20.0;
        }

        let ui_id_start: WidgetId = self.get_ui().get_id();

        let position = self.get_position().clone();

        let mut color = color::WHITE;
        if self.get_ui().select { color=color::RED; }

        //floating canvas where our node data resides
        Canvas::new()
            .floating(true)
            .title_bar(self.get_name())
            .xy([position[0], position[1]])
            .h(canvas_height)
            .w(self.get_ui().width)
            .color(color::BLUE)
            .label_color(color)
            .set(ui_id_start, ui);

        //build buttons in reverse order, to stay within canvas
        //start at top right, and head left from
        //close button
        Button::new()
            .top_right_of(ui_id_start) //todo: shift up ~5.0
            .label("x")
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().destroy = true)
            .set(ui_id_start + 1, ui);

        //collapse
        Button::new()
            .left(5.0)
            .label(cl)
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().toggle_collapse())
            .set(ui_id_start + 2, ui);

        //connector
        Button::new()
            .top_left_of(ui_id_start) //todo: shift up ~5.0
            .label("o")
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().toggle_select())
            .set(ui_id_start + 3, ui);

        if !self.get_ui().collapse {
            Text::new("Node Data") // TODO: derive actual node data, and represent somehow
                .middle_of(ui_id_start)
                .set(ui_id_start + 4, ui);
        }
    }
}

#[derive(Debug,Clone,PartialEq)]
pub struct UiBase {
    ui_id: WidgetId,
    select: bool,
    collapse: bool,
    destroy: bool,
    width: f64,
    height: f64,
}

impl UiBase {
    pub fn default() -> UiBase {
        UiBase { ui_id: WidgetId(10000),
                 select: false,
                 collapse: false,
                 destroy: false,
                 width: 150.0,
                 height: 100.0,
        }
    }
    pub fn toggle_destroy(&mut self) { self.destroy = !self.destroy; }
    pub fn toggle_collapse(&mut self) { self.collapse = !self.collapse; }
    pub fn toggle_select(&mut self) { self.select = !self.select; }

    pub fn get_id(&self) -> WidgetId { self.ui_id }
    pub fn set_id(&mut self, id: WidgetId) { self.ui_id = id; }
}

pub trait UiGraph {
    fn render(&mut self, ui: &mut Ui);
}

impl<E:GraphEdge,N:UiNode> UiGraph for Graph<E,N> {
    fn render(&mut self, ui: &mut Ui) {
        let mut select: (Option<Nid>,Option<Nid>) = (None,None);
        let mut edges: Vec<(Nid,Vec<Nid>)> = vec!();

        // NOTE: this might change when we start tracking connection types
        let mut sockets_from = HashMap::new(); // track socket connections for layout

        self.with_nodes_mut(|n| {
            let is_select = n.get_ui().select;

            {
                let base = n.get_base();
                if is_select { //selected?
                    if select.0.is_some() {
                        if !select.1.is_some() {
                            select.1 = Some(base.get_id());
                        }
                    }
                    else { select.0 = Some(base.get_id()); }
                }

                let ev = base.get_edges();
                if ev.len() > 0 {
                    edges.push((base.get_id(),ev));
                }
            }

            if let Some(coord) = ui.xy_of(n.get_ui().get_id()) {
                n.set_position(coord);
            }
        });

        let socket_id_out = 1;
        let line_id = 2;
        let socket_id_in = 3;

        let socket_size = 10.;
        let socket_offset = 3.*socket_size;

        // build edges
        // NOTE: these edges represent forward-edges only
        // TODO: break out socket and line positioning into some sort of method
        for &(ref nid,ref ev) in edges.iter() {
            let n = self.get_node(&nid).unwrap();
            let nui = n.get_ui();

            let id = nui.get_id().0 * ::MAX_NODES * (::MAX_CONN_IN+::MAX_CONN_OUT) +1;

            let mut from_pos = *n.get_position();

            for (k,en) in ev.iter().enumerate() {
                if !nui.collapse {
                    from_pos[1] -= k as f64+socket_offset;
                }
                from_pos[0] -= nui.width/2.;

                let k = k + 1;

                let from_count = sockets_from.entry(en).or_insert(0);
                *from_count += 1; //inc sock count from-connections

                if let Some(n2) = self.get_node(&en) {
                    let mut to_pos = *n2.get_position();
                    if !n2.get_ui().collapse {
                        to_pos[1] -= *from_count as f64 * socket_offset;
                    }
                    to_pos[0] += n2.get_ui().width/2.;

                    Line::abs(from_pos, to_pos)
                        .set(WidgetId(id*line_id*k), ui);
                }
            }
        }

        self.with_nodes_mut(|n| {
            n.build_ui(ui);
        });

        // build sockets
        // NOTE: this duplication from 'build_edges' should be fixed
        // only way I could figure out proper draw order
        for (j,&(ref nid,ref ev)) in edges.iter().enumerate() {
            let n = self.get_node(&nid).unwrap();
            let nui = n.get_ui();

            let id = nui.get_id().0 * ::MAX_NODES + 1;

            let mut from_pos = *n.get_position();
            from_pos[0] -= nui.width/2.;

            for (k,en) in ev.iter().enumerate() {
                let k = k + 1;
                let from_count = sockets_from.get(en).unwrap();

                if !nui.collapse {
                    from_pos[1] -= (j + k) as f64+socket_offset;
                }

                Circle::fill_with(socket_size,color::LIGHT_BLUE)
                    .xy(from_pos)
                    .set(WidgetId((id+j+k)*socket_id_out), ui);

                if let Some(n2) = self.get_node(&en) {
                    let mut to_pos = *n2.get_position();
                    if !n2.get_ui().collapse {
                        to_pos[1] -= *from_count as f64 * socket_offset;
                    }
                    to_pos[0] += n2.get_ui().width/2.;

                    let id2 = n2.get_ui().get_id().0 + ::MAX_NODES + 1;
                    let id2 = WidgetId((id2+j+k)*socket_id_in + ::MAX_CONN_IN);

                    Circle::fill_with(socket_size,color::ORANGE)
                        .xy(to_pos)
                        .set(id2, ui);

                    println!("{:?}",id2);
                }
            }
        }

        // build new edge
        match select {
            (Some(first),Some(second)) => {
                //clear node selection
                self.get_node_mut(&first).unwrap().get_ui_mut().select = false;
                self.get_node_mut(&second).unwrap().get_ui_mut().select = false;

                //todo: prompt for edge data!
                self.direct(&first,&second,E::default());
            },
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>//! This is code from [Tungstenite project](https://github.com/snapview/tungstenite-rs)
#![cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
use std::slice;
use std::ptr::copy_nonoverlapping;

// Holds a slice guaranteed to be shorter than 8 bytes
struct ShortSlice<'a>(&'a mut [u8]);

impl<'a> ShortSlice<'a> {
    unsafe fn new(slice: &'a mut [u8]) -> Self {
        // Sanity check for debug builds
        debug_assert!(slice.len() < 8);
        ShortSlice(slice)
    }
    fn len(&self) -> usize {
        self.0.len()
    }
}

/// Faster version of `apply_mask()` which operates on 8-byte blocks.
#[inline]
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) {
    // Extend the mask to 64 bits
    let mut mask_u64 = ((mask_u32 as u64) << 32) | (mask_u32 as u64);
    // Split the buffer into three segments
    let (head, mid, tail) = align_buf(buf);

    // Initial unaligned segment
    let head_len = head.len();
    if head_len > 0 {
        xor_short(head, mask_u64);
        if cfg!(target_endian = "big") {
            mask_u64 = mask_u64.rotate_left(8 * head_len as u32);
        } else {
            mask_u64 = mask_u64.rotate_right(8 * head_len as u32);
        }
    }
    // Aligned segment
    for v in mid {
        *v ^= mask_u64;
    }
    // Final unaligned segment
    if tail.len() > 0 {
        xor_short(tail, mask_u64);
    }
}

#[inline]
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
// inefficient, it could be done better. The compiler does not understand that
// a `ShortSlice` must be smaller than a u64.
fn xor_short(buf: ShortSlice, mask: u64) {
    // Unsafe: we know that a `ShortSlice` fits in a u64
    unsafe {
        let (ptr, len) = (buf.0.as_mut_ptr(), buf.0.len());
        let mut b: u64 = 0;
        #[allow(trivial_casts)]
        copy_nonoverlapping(ptr, &mut b as *mut _ as *mut u8, len);
        b ^= mask;
        #[allow(trivial_casts)]
        copy_nonoverlapping(&b as *const _ as *const u8, ptr, len);
    }
}

#[inline]
// Unsafe: caller must ensure the buffer has the correct size and alignment
unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
    // Assert correct size and alignment in debug builds
    debug_assert!(buf.len() & 0x7 == 0);
    debug_assert!(buf.as_ptr() as usize & 0x7 == 0);

    slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u64, buf.len() >> 3)
}

#[inline]
// Splits a slice into three parts: an unaligned short head and tail, plus an aligned
// u64 mid section.
fn align_buf(buf: &mut [u8]) -> (ShortSlice, &mut [u64], ShortSlice) {
    let start_ptr = buf.as_ptr() as usize;
    let end_ptr = start_ptr + buf.len();

    // Round *up* to next aligned boundary for start
    let start_aligned = (start_ptr+7) & !0x7;
    // Round *down* to last aligned boundary for end
    let end_aligned = end_ptr & !0x7;

    if end_aligned >= start_aligned {
        // We have our three segments (head, mid, tail)
        let (tmp, tail) = buf.split_at_mut(end_aligned - start_ptr);
        let (head, mid) = tmp.split_at_mut(start_aligned - start_ptr);

        // Unsafe: we know the middle section is correctly aligned, and the outer
        // sections are smaller than 8 bytes
        unsafe { (ShortSlice::new(head), cast_slice(mid), ShortSlice(tail)) }
    } else {
        // We didn't cross even one aligned boundary!

        // Unsafe: The outer sections are smaller than 8 bytes
        unsafe { (ShortSlice::new(buf), &mut [], ShortSlice::new(&mut [])) }
    }
}

#[cfg(test)]
mod tests {
    use super::apply_mask;
    use byteorder::{ByteOrder, LittleEndian};

    /// A safe unoptimized mask application.
    fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
        for (i, byte) in buf.iter_mut().enumerate() {
            *byte ^= mask[i & 3];
        }
    }

    #[test]
    fn test_apply_mask() {
        let mask = [0x6d, 0xb6, 0xb2, 0x80];
        let mask_u32: u32 = LittleEndian::read_u32(&mask);

        let unmasked = vec![
            0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17,
            0x74, 0xf9, 0x12, 0x03,
        ];

        // Check masking with proper alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked, &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask(&mut masked_fast, mask_u32);

            assert_eq!(masked, masked_fast);
        }

        // Check masking without alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked[1..], &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask(&mut masked_fast[1..], mask_u32);

            assert_eq!(masked, masked_fast);
        }
    }
}
<commit_msg>clippy warnings<commit_after>//! This is code from [Tungstenite project](https://github.com/snapview/tungstenite-rs)
#![cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
use std::ptr::copy_nonoverlapping;
use std::slice;

// Holds a slice guaranteed to be shorter than 8 bytes
struct ShortSlice<'a>(&'a mut [u8]);

impl<'a> ShortSlice<'a> {
    unsafe fn new(slice: &'a mut [u8]) -> Self {
        // Sanity check for debug builds
        debug_assert!(slice.len() < 8);
        ShortSlice(slice)
    }
    fn len(&self) -> usize {
        self.0.len()
    }
}

/// Faster version of `apply_mask()` which operates on 8-byte blocks.
#[inline]
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) {
    // Extend the mask to 64 bits
    let mut mask_u64 = ((mask_u32 as u64) << 32) | (mask_u32 as u64);
    // Split the buffer into three segments
    let (head, mid, tail) = align_buf(buf);

    // Initial unaligned segment
    let head_len = head.len();
    if head_len > 0 {
        xor_short(head, mask_u64);
        if cfg!(target_endian = "big") {
            mask_u64 = mask_u64.rotate_left(8 * head_len as u32);
        } else {
            mask_u64 = mask_u64.rotate_right(8 * head_len as u32);
        }
    }
    // Aligned segment
    for v in mid {
        *v ^= mask_u64;
    }
    // Final unaligned segment
    if tail.len() > 0 {
        xor_short(tail, mask_u64);
    }
}

#[inline]
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
// inefficient, it could be done better. The compiler does not understand that
// a `ShortSlice` must be smaller than a u64.
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
fn xor_short(buf: ShortSlice, mask: u64) {
    // Unsafe: we know that a `ShortSlice` fits in a u64
    unsafe {
        let (ptr, len) = (buf.0.as_mut_ptr(), buf.0.len());
        let mut b: u64 = 0;
        #[allow(trivial_casts)]
        copy_nonoverlapping(ptr, &mut b as *mut _ as *mut u8, len);
        b ^= mask;
        #[allow(trivial_casts)]
        copy_nonoverlapping(&b as *const _ as *const u8, ptr, len);
    }
}

#[inline]
// Unsafe: caller must ensure the buffer has the correct size and alignment
unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
    // Assert correct size and alignment in debug builds
    debug_assert!(buf.len().trailing_zeros() >= 3);
    debug_assert!((buf.as_ptr() as usize).trailing_zeros() >= 3);

    slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u64, buf.len() >> 3)
}

#[inline]
// Splits a slice into three parts: an unaligned short head and tail, plus an aligned
// u64 mid section.
fn align_buf(buf: &mut [u8]) -> (ShortSlice, &mut [u64], ShortSlice) {
    let start_ptr = buf.as_ptr() as usize;
    let end_ptr = start_ptr + buf.len();

    // Round *up* to next aligned boundary for start
    let start_aligned = (start_ptr + 7) & !0x7;
    // Round *down* to last aligned boundary for end
    let end_aligned = end_ptr & !0x7;

    if end_aligned >= start_aligned {
        // We have our three segments (head, mid, tail)
        let (tmp, tail) = buf.split_at_mut(end_aligned - start_ptr);
        let (head, mid) = tmp.split_at_mut(start_aligned - start_ptr);

        // Unsafe: we know the middle section is correctly aligned, and the outer
        // sections are smaller than 8 bytes
        unsafe { (ShortSlice::new(head), cast_slice(mid), ShortSlice(tail)) }
    } else {
        // We didn't cross even one aligned boundary!

        // Unsafe: The outer sections are smaller than 8 bytes
        unsafe { (ShortSlice::new(buf), &mut [], ShortSlice::new(&mut [])) }
    }
}

#[cfg(test)]
mod tests {
    use super::apply_mask;
    use byteorder::{ByteOrder, LittleEndian};

    /// A safe unoptimized mask application.
    fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
        for (i, byte) in buf.iter_mut().enumerate() {
            *byte ^= mask[i & 3];
        }
    }

    #[test]
    fn test_apply_mask() {
        let mask = [0x6d, 0xb6, 0xb2, 0x80];
        let mask_u32: u32 = LittleEndian::read_u32(&mask);

        let unmasked = vec![
            0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17,
            0x74, 0xf9, 0x12, 0x03,
        ];

        // Check masking with proper alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked, &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask(&mut masked_fast, mask_u32);

            assert_eq!(masked, masked_fast);
        }

        // Check masking without alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked[1..], &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask(&mut masked_fast[1..], mask_u32);

            assert_eq!(masked, masked_fast);
        }
    }
}
<|endoftext|>


<commit_before>use cargo::core::Workspace;
use cargo::ops::{resolve_ws_precisely, Packages};
use cargo::util::important_paths::find_root_manifest_for_wd;
use cargo::Config;
use core::Session;
use nameres::RUST_SRC_PATH;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};

/// get crate file from current path & crate name
pub fn get_crate_file(name: &str, from_path: &Path, session: &Session) -> Option<PathBuf> {
    debug!("get_crate_file {}, {:?}", name, from_path);

    if let Some(path) = get_outer_crates(name, from_path, session) {
        debug!("get_outer_crates returned {:?} for {}", path, name);
        return Some(path);
    } else {
        warn!("get_outer_crates returned None");
    }

    // TODO: cache std libs
    let srcpath = &*RUST_SRC_PATH;
    {
        // try lib<name>/lib.rs, like in the rust source dir
        let cratelibname = format!("lib{}", name);
        let filepath = srcpath.join(cratelibname).join("lib.rs");
        if filepath.exists() || session.contains_file(&filepath) {
            return Some(filepath);
        }
    }
    {
        // try <name>/lib.rs
        let filepath = srcpath.join(name).join("lib.rs");
        if filepath.exists() || session.contains_file(&filepath) {
            return Some(filepath);
        }
    }
    None
}

/// get module file from current path & crate name
pub fn get_module_file(name: &str, parentdir: &Path, session: &Session) -> Option<PathBuf> {
    {
        // try just <name>.rs
        let filepath = parentdir.join(format!("{}.rs", name));
        if filepath.exists() || session.contains_file(&filepath) {
            return Some(filepath);
        }
    }
    {
        // try <name>/mod.rs
        let filepath = parentdir.join(name).join("mod.rs");
        if filepath.exists() || session.contains_file(&filepath) {
            return Some(filepath);
        }
    }
    None
}

/// try to get outer crates
fn get_outer_crates(libname: &str, from_path: &Path, session: &Session) -> Option<PathBuf> {
    macro_rules! cargo_res {
        ($r:expr) => {
            match $r {
                Ok(val) => val,
                Err(err) => {
                    warn!("[get_outer_crates]: {}", err);
                    return None;
                }
            }
        };
    }
    debug!(
        "[get_outer_crates] lib name: {:?}, from_path: {:?}",
        libname, from_path
    );
    // we have to try 2 names(e.g. crate a-b has a target name a_b by rustc)
    let libname_hyphened = {
        let tmp_str = libname.to_owned();
        tmp_str.replace("_", "-")
    };
    let manifest = cargo_res!(find_root_manifest_for_wd(from_path));

    if let Some(deps_info) = session.get_deps(&manifest) {
        debug!("[get_outer_crates] cache exists");
        if let Some(p) = deps_info.get_src_path(libname) {
            Some(p)
        } else if let Some(p) = deps_info.get_src_path(&libname_hyphened) {
            Some(p)
        } else {
            None
        }
    } else {
        debug!("[get_outer_crates] cache doesn't exist");
        let config = cargo_res!(Config::default());
        let ws = cargo_res!(Workspace::new(&manifest, &config));
        let pkg_cur = ws.current_opt()?;
        // what we need is only packages in Cargo.toml
        // so, we cache only those packages
        let toml_deps: HashSet<_> = pkg_cur.dependencies().iter().map(|d| d.name()).collect();
        let specs = cargo_res!(Packages::All.into_package_id_specs(&ws));
        // now we resolve dependncies with 'all_features=true'
        let (packages, _) = cargo_res!(resolve_ws_precisely(&ws, None, &[], true, false, &specs));
        let mut deps_map = HashMap::new();
        let mut res = None;
        for package_id in packages.package_ids() {
            let pkg = match packages.get(package_id) {
                Ok(p) => p,
                Err(_) => continue,
            };
            if !toml_deps.contains(&pkg.name()) {
                continue;
            }
            let targets = pkg.manifest().targets();
            let lib_target = targets.into_iter().find(|target| target.is_lib());
            if let Some(target) = lib_target {
                let name = target.name();
                let src_path = target.src_path().to_owned();
                if name == libname || name == libname_hyphened {
                    res = Some(src_path.clone());
                }
                deps_map.insert(name.to_owned(), src_path);
            }
        }
        session.cache_deps(manifest, deps_map);
        res
    }
}
<commit_msg>Refactor fileres.re<commit_after>use cargo::core::Workspace;
use cargo::ops::{resolve_ws_precisely, Packages};
use cargo::util::important_paths::find_root_manifest_for_wd;
use cargo::Config;
use core::Session;
use nameres::RUST_SRC_PATH;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};

/// get crate file from current path & crate name
pub fn get_crate_file(name: &str, from_path: &Path, session: &Session) -> Option<PathBuf> {
    debug!("get_crate_file {}, {:?}", name, from_path);

    if let Some(path) = get_outer_crates(name, from_path, session) {
        debug!("get_outer_crates returned {:?} for {}", path, name);
        return Some(path);
    } else {
        warn!("get_outer_crates returned None");
    }

    // TODO: cache std libs
    let srcpath = &*RUST_SRC_PATH;
    {
        // try lib<name>/lib.rs, like in the rust source dir
        let cratelibname = format!("lib{}", name);
        let filepath = srcpath.join(cratelibname).join("lib.rs");
        if filepath.exists() || session.contains_file(&filepath) {
            return Some(filepath);
        }
    }
    {
        // try <name>/lib.rs
        let filepath = srcpath.join(name).join("lib.rs");
        if filepath.exists() || session.contains_file(&filepath) {
            return Some(filepath);
        }
    }
    None
}

/// get module file from current path & crate name
pub fn get_module_file(name: &str, parentdir: &Path, session: &Session) -> Option<PathBuf> {
    {
        // try just <name>.rs
        let filepath = parentdir.join(format!("{}.rs", name));
        if filepath.exists() || session.contains_file(&filepath) {
            return Some(filepath);
        }
    }
    {
        // try <name>/mod.rs
        let filepath = parentdir.join(name).join("mod.rs");
        if filepath.exists() || session.contains_file(&filepath) {
            return Some(filepath);
        }
    }
    None
}

/// try to get outer crates
/// if we have dependencies in cache, use it.
/// else, call cargo's function to resolve depndencies.
fn get_outer_crates(libname: &str, from_path: &Path, session: &Session) -> Option<PathBuf> {
    macro_rules! cargo_try {
        ($r:expr) => {
            match $r {
                Ok(val) => val,
                Err(err) => {
                    warn!("[get_outer_crates]: {}", err);
                    return None;
                }
            }
        };
    }
    debug!(
        "[get_outer_crates] lib name: {:?}, from_path: {:?}",
        libname, from_path
    );

    // we have to try 2 names(e.g. crate a-b has a target name a_b by rustc)
    let libname_hyphenated = if libname.contains('-') {
        Some(libname.replace("_", "-"))
    } else {
        None
    };
    let manifest = cargo_try!(find_root_manifest_for_wd(from_path));

    if let Some(deps_info) = session.get_deps(&manifest) {
        debug!("[get_outer_crates] cache exists");
        if let Some(p) = deps_info.get_src_path(libname) {
            Some(p)
        } else if let Some(p) = deps_info.get_src_path(&libname_hyphenated?) {
            Some(p)
        } else {
            None
        }
    } else {
        debug!("[get_outer_crates] cache doesn't exist");
        let config = cargo_try!(Config::default());
        let ws = cargo_try!(Workspace::new(&manifest, &config));
        let pkg_cur = ws.current_opt()?;
        // what we need is only packages in Cargo.toml
        // so, we cache only those packages
        let toml_deps: HashSet<_> = pkg_cur.dependencies().iter().map(|d| d.name()).collect();
        let specs = cargo_try!(Packages::All.into_package_id_specs(&ws));
        // now we resolve dependncies with 'all_features=true'
        let (packages, _) = cargo_try!(resolve_ws_precisely(&ws, None, &[], true, false, &specs));
        let mut deps_map = HashMap::new();
        let mut res = None;
        for package_id in packages.package_ids() {
            let pkg = match packages.get(package_id) {
                Ok(p) => p,
                Err(_) => continue,
            };
            if !toml_deps.contains(&pkg.name()) {
                continue;
            }
            let targets = pkg.manifest().targets();
            let lib_target = targets.into_iter().find(|target| target.is_lib());
            if let Some(target) = lib_target {
                let name = target.name();
                let src_path = target.src_path().to_owned();
                if name == libname {
                    res = Some(src_path.clone());
                } else if let Some(ref hyphnated) = libname_hyphenated {
                    if name == hyphnated {
                        res = Some(src_path.clone());
                    }
                }
                deps_map.insert(name.to_owned(), src_path);
            }
        }
        session.cache_deps(manifest, deps_map);
        res
    }
}
<|endoftext|>


<commit_before>use std::cell::UnsafeCell;
use std::rc::Rc;
use std::time::{Duration, Instant};

use futures::{Stream, Poll, Async};

use super::event_loop::TaskId;
use super::{IntoTask, PeriodicTimer};
use slab::{self, Slab};

#[derive(Debug, Clone, Copy)]
pub struct TimerId {
    index: usize,
}

impl From<usize> for TimerId {
    #[inline]
    fn from(index: usize) -> Self {
        TimerId { index }
    }
}

impl From<TimerId> for usize {
    #[inline]
    fn from(timer_id: TimerId) -> Self {
        timer_id.index
    }
}

#[derive(Clone, Copy)]
enum Token {
    Entry(TimerId),
    Slot(usize),
    Nil,
}

struct Entry {
    prev: Token,
    next: Token,
    task: TaskId,
    expiration: Option<Instant>,
}

impl Entry {
    #[inline]
    fn new(task: TaskId) -> Self {
        Entry {
            prev: Token::Nil,
            next: Token::Nil,
            task,
            expiration: None,
        }
    }
}

struct Slot {
    prev: Token,
    next: Token,
}

struct Inner {
    entries: Slab<Entry, TimerId>,
    slots: Vec<Slot>,
    current_slot: usize,
    mask: usize,
}

impl Inner {
    fn new() -> Self {
        const NUM_OF_SLOTS: usize = 128; // must be power of 2
        const INIT_CAPACITY: usize = 512;

        let num_of_slots = NUM_OF_SLOTS.next_power_of_two();
        let mut slots = Vec::<Slot>::with_capacity(num_of_slots);
        let mask = num_of_slots - 1;
        unsafe { slots.set_len(num_of_slots) };
        for (i, slot) in slots.iter_mut().enumerate() {
            slot.prev = Token::Slot(i.wrapping_sub(1) & mask);
            slot.next = Token::Slot((i + 1) & mask);
        }

        Inner {
            entries: slab::with_capacity(INIT_CAPACITY),
            slots,
            current_slot: 0,
            mask,
        }
    }

    #[inline]
    fn get_mut_entry(&mut self, timer_id: TimerId) -> &mut Entry {
        unsafe { self.entries.get_unchecked_mut(timer_id) }
    }

    #[inline]
    fn get_entry(&self, timer_id: TimerId) -> &Entry {
        unsafe { self.entries.get_unchecked(timer_id) }
    }

    #[inline]
    fn get_slot(&mut self, slot_idx: usize) -> &Slot {
        unsafe { self.slots.get_unchecked(slot_idx) }
    }

    #[inline]
    fn get_mut_slot(&mut self, slot_idx: usize) -> &mut Slot {
        unsafe { self.slots.get_unchecked_mut(slot_idx) }
    }

    #[inline]
    fn effective_slot(&self, slot: usize) -> usize {
        slot & self.mask
    }

    #[inline]
    fn is_expired(&self, timer_id: TimerId) -> bool {
        match self.get_entry(timer_id).prev {
            Token::Nil => true,
            _ => false,
        }
    }

    #[inline]
    fn round_to_secs(dur: Duration) -> u64 {
        if dur.subsec_nanos() >= 500_000_000 {
            dur.as_secs() + 1
        } else {
            dur.as_secs()
        }
    }

    #[inline]
    fn schedule(&mut self, dur: Duration, task: TaskId) -> TimerId {
        let mut timeout = dur.as_secs() as usize;
        let mut entry = Entry::new(task);
        if timeout > self.mask {
            timeout = self.mask;
            entry.expiration = Some(Instant::now() + dur);
        }
        let slot_idx = self.effective_slot(self.current_slot.wrapping_add(timeout));
        let prev = self.get_slot(slot_idx).prev;
        entry.prev = prev;
        entry.next = Token::Slot(slot_idx);
        let timer_id = self.entries.insert(entry);
        let token = Token::Entry(timer_id);
        match prev {
            Token::Entry(prev_timer_id) => self.get_mut_entry(prev_timer_id).next = token,
            Token::Slot(prev_slot_idx) => self.get_mut_slot(prev_slot_idx).next = token,
            Token::Nil => ::unreachable(),
        }
        self.get_mut_slot(slot_idx).prev = token;
        timer_id
    }

    fn reschedule(&mut self, dur: Duration, exp: Option<Instant>, timer_id: TimerId) {
        if exp.is_none() {
            // cancel, then reschedule
            let (prev, next) = {
                let entry = self.get_entry(timer_id);
                (entry.prev, entry.next)
            };
            match prev {
                Token::Entry(prev_timer_id) => self.get_mut_entry(prev_timer_id).next = next,
                Token::Slot(prev_slot_idx) => self.get_mut_slot(prev_slot_idx).next = next,
                Token::Nil => ::unreachable(),
            }
            match next {
                Token::Entry(next_timer_id) => self.get_mut_entry(next_timer_id).prev = prev,
                Token::Slot(next_slot_idx) => self.get_mut_slot(next_slot_idx).prev = prev,
                Token::Nil => ::unreachable(),
            }
        }
        let mut timeout = Self::round_to_secs(dur) as usize;
        if timeout > self.mask {
            timeout = self.mask;
            self.get_mut_entry(timer_id).expiration = if exp.is_none() {
                Some(Instant::now() + dur)
            } else {
                exp
            };
        }
        let slot_idx = self.effective_slot(self.current_slot.wrapping_add(timeout));
        let prev = self.get_slot(slot_idx).prev;
        {
            let entry = self.get_mut_entry(timer_id);
            entry.prev = prev;
            entry.next = Token::Slot(slot_idx);
        }
        let token = Token::Entry(timer_id);
        match prev {
            Token::Entry(prev_timer_id) => self.get_mut_entry(prev_timer_id).next = token,
            Token::Slot(prev_slot_idx) => self.get_mut_slot(prev_slot_idx).next = token,
            Token::Nil => ::unreachable(),
        }
        self.get_mut_slot(slot_idx).prev = token;
    }

    #[inline]
    fn cancel(&mut self, timer_id: TimerId) {
        let entry = self.entries.remove(timer_id).unwrap();
        match entry.prev {
            Token::Entry(timer_id) => self.get_mut_entry(timer_id).next = entry.next,
            Token::Slot(slot_idx) => self.get_mut_slot(slot_idx).next = entry.next,
            Token::Nil => return, // expired
        }
        match entry.next {
            Token::Entry(timer_id) => self.get_mut_entry(timer_id).prev = entry.prev,
            Token::Slot(slot_idx) => self.get_mut_slot(slot_idx).prev = entry.prev,
            Token::Nil => ::unreachable(), // expired
        }
    }

    #[inline]
    fn tick(&mut self) {
        let current_slot = self.current_slot;
        let mut timer_id = match self.get_slot(current_slot).next {
            Token::Slot(index) => {
                self.current_slot = index;
                return;
            }
            Token::Entry(id) => id,
            Token::Nil => ::unreachable(),
        };
        let next_slot;
        let mut exit = false;
        loop {
            let (next, expiration, task) = {
                let entry = self.get_mut_entry(timer_id);
                let next = entry.next;
                entry.prev = Token::Nil;
                entry.next = Token::Nil;
                (next, entry.expiration.take(), entry.task)
            };
            if !exit {
                if let Some(exp) = expiration {
                    let now = Instant::now();
                    if exp >= now + Duration::from_secs(1) {
                        self.reschedule(exp - now, Some(exp), timer_id);
                    } else {
                        exit = super::run_expired_task(task);
                    }
                } else {
                    exit = super::run_expired_task(task);
                }
            }
            match next {
                Token::Entry(id) => timer_id = id,
                Token::Slot(index) => {
                    next_slot = index;
                    break;
                }
                Token::Nil => ::unreachable(),
            }
        }
        self.get_mut_slot(current_slot).next = Token::Slot(next_slot);
        self.get_mut_slot(next_slot).prev = Token::Slot(self.current_slot);
        self.current_slot = next_slot;
    }
}

pub(super) struct Wheel {
    inner: Rc<UnsafeCell<Inner>>,
}

impl Wheel {
    pub fn new() -> Self {
        let dur = Duration::from_secs(1);
        let inner = Rc::new(UnsafeCell::new(Inner::new()));
        let wheel = Wheel {
            inner: inner.clone(),
        };
        super::spawn(
            PeriodicTimer::new(dur, dur)
                .for_each(move |_| {
                    unsafe { &mut *inner.as_ref().get() }.tick();
                    Ok(())
                })
                .into_task(),
        );
        wheel
    }

    #[inline]
    pub fn schedule(&self, dur: Duration, task: TaskId) -> TimerId {
        self.as_mut_inner().schedule(dur, task)
    }

    #[inline]
    pub fn reschedule(&self, dur: Duration, timer_id: TimerId) {
        self.as_mut_inner().reschedule(dur, None, timer_id)
    }

    #[inline]
    pub fn cancel(&self, timer_id: TimerId) {
        self.as_mut_inner().cancel(timer_id);
    }

    #[inline]
    pub fn is_expired(&self, timer_id: TimerId) -> bool {
        self.as_inner().is_expired(timer_id)
    }

    #[inline]
    fn as_mut_inner(&self) -> &mut Inner {
        unsafe { &mut *(&self.inner).get() }
    }

    #[inline]
    fn as_inner(&self) -> &Inner {
        unsafe { &*(&self.inner).get() }
    }
}

#[derive(Debug, Clone, Copy)]
enum TimerState {
    Unscheduled(Duration),
    Scheduled(TimerId),
    Expired,
    Cancelled,
}

#[derive(Debug)]
pub(super) struct Timer {
    state: TimerState,
}

impl Timer {
    #[inline]
    pub fn new(secs: u64) -> Self {
        let state = if secs == 0 {
            TimerState::Expired
        } else {
            TimerState::Unscheduled(Duration::from_secs(secs))
        };
        Timer { state }
    }

    #[inline]
    pub fn poll(&mut self) -> Poll<(), ()> {
        match self.state {
            TimerState::Scheduled(timer_id) => {
                match super::wt_is_expired(timer_id) {
                    true => Ok(Async::Ready(())),
                    false => Ok(Async::NotReady),
                }
            }
            TimerState::Unscheduled(dur) => {
                let timer_id = super::wt_schedule(dur);
                self.state = TimerState::Scheduled(timer_id);
                Ok(Async::NotReady)
            }
            TimerState::Expired => Ok(Async::Ready(())),
            TimerState::Cancelled => ::unreachable(),
        }
    }

    #[inline]
    pub fn reschedule(&mut self, secs: u64) -> bool {
        match self.state {
            TimerState::Scheduled(timer_id) => {
                super::wt_reschedule(Duration::from_secs(secs), timer_id);
                true
            }
            TimerState::Unscheduled(dur) => {
                let timer_id = super::wt_schedule(dur);
                self.state = TimerState::Scheduled(timer_id);
                true
            }
            TimerState::Expired => false,
            TimerState::Cancelled => ::unreachable(),
        }
    }
}

impl Drop for Timer {
    fn drop(&mut self) {
        if let TimerState::Scheduled(timer_id) = self.state {
            super::wt_cancel(timer_id);
            self.state = TimerState::Cancelled;
        }
    }
}
<commit_msg>Refine reschedule<commit_after>use std::cell::UnsafeCell;
use std::rc::Rc;
use std::time::{Duration, Instant};

use futures::{Stream, Poll, Async};

use super::event_loop::TaskId;
use super::{IntoTask, PeriodicTimer};
use slab::{self, Slab};

#[derive(Debug, Clone, Copy)]
pub struct TimerId {
    index: usize,
}

impl From<usize> for TimerId {
    #[inline]
    fn from(index: usize) -> Self {
        TimerId { index }
    }
}

impl From<TimerId> for usize {
    #[inline]
    fn from(timer_id: TimerId) -> Self {
        timer_id.index
    }
}

#[derive(Clone, Copy)]
enum Token {
    Entry(TimerId),
    Slot(usize),
    Nil,
}

struct Entry {
    prev: Token,
    next: Token,
    task: TaskId,
    expiration: Option<Instant>,
}

impl Entry {
    #[inline]
    fn new(task: TaskId) -> Self {
        Entry {
            prev: Token::Nil,
            next: Token::Nil,
            task,
            expiration: None,
        }
    }
}

struct Slot {
    prev: Token,
    next: Token,
}

struct Inner {
    entries: Slab<Entry, TimerId>,
    slots: Vec<Slot>,
    current_slot: usize,
    mask: usize,
}

impl Inner {
    fn new() -> Self {
        const NUM_OF_SLOTS: usize = 128; // must be power of 2
        const INIT_CAPACITY: usize = 512;

        let num_of_slots = NUM_OF_SLOTS.next_power_of_two();
        let mut slots = Vec::<Slot>::with_capacity(num_of_slots);
        let mask = num_of_slots - 1;
        unsafe { slots.set_len(num_of_slots) };
        for (i, slot) in slots.iter_mut().enumerate() {
            slot.prev = Token::Slot(i.wrapping_sub(1) & mask);
            slot.next = Token::Slot((i + 1) & mask);
        }

        Inner {
            entries: slab::with_capacity(INIT_CAPACITY),
            slots,
            current_slot: 0,
            mask,
        }
    }

    #[inline]
    fn get_mut_entry(&mut self, timer_id: TimerId) -> &mut Entry {
        unsafe { self.entries.get_unchecked_mut(timer_id) }
    }

    #[inline]
    fn get_entry(&self, timer_id: TimerId) -> &Entry {
        unsafe { self.entries.get_unchecked(timer_id) }
    }

    #[inline]
    fn get_slot(&mut self, slot_idx: usize) -> &Slot {
        unsafe { self.slots.get_unchecked(slot_idx) }
    }

    #[inline]
    fn get_mut_slot(&mut self, slot_idx: usize) -> &mut Slot {
        unsafe { self.slots.get_unchecked_mut(slot_idx) }
    }

    #[inline]
    fn effective_slot(&self, slot: usize) -> usize {
        slot & self.mask
    }

    #[inline]
    fn is_expired(&self, timer_id: TimerId) -> bool {
        match self.get_entry(timer_id).prev {
            Token::Nil => true,
            _ => false,
        }
    }

    #[inline]
    fn round_to_secs(dur: Duration) -> u64 {
        if dur.subsec_nanos() >= 500_000_000 {
            dur.as_secs() + 1
        } else {
            dur.as_secs()
        }
    }

    #[inline]
    fn schedule(&mut self, dur: Duration, task: TaskId) -> TimerId {
        let mut timeout = dur.as_secs() as usize;
        let mut entry = Entry::new(task);
        if timeout > self.mask {
            timeout = self.mask;
            entry.expiration = Some(Instant::now() + dur);
        }
        let slot_idx = self.effective_slot(self.current_slot.wrapping_add(timeout));
        let prev = self.get_slot(slot_idx).prev;
        entry.prev = prev;
        entry.next = Token::Slot(slot_idx);
        let timer_id = self.entries.insert(entry);
        let token = Token::Entry(timer_id);
        match prev {
            Token::Entry(prev_timer_id) => self.get_mut_entry(prev_timer_id).next = token,
            Token::Slot(prev_slot_idx) => self.get_mut_slot(prev_slot_idx).next = token,
            Token::Nil => ::unreachable(),
        }
        self.get_mut_slot(slot_idx).prev = token;
        timer_id
    }

    #[inline]
    fn reschedule_internal(&mut self, dur: Duration, exp: Option<Instant>, timer_id: TimerId) {
        let mut timeout = Self::round_to_secs(dur) as usize;
        if timeout > self.mask {
            timeout = self.mask;
            self.get_mut_entry(timer_id).expiration = match exp {
                None => Some(Instant::now() + dur),
                some => some,
            };
        }
        let slot_idx = self.effective_slot(self.current_slot.wrapping_add(timeout));
        let prev = self.get_slot(slot_idx).prev;
        {
            let entry = self.get_mut_entry(timer_id);
            entry.prev = prev;
            entry.next = Token::Slot(slot_idx);
        }
        let token = Token::Entry(timer_id);
        match prev {
            Token::Entry(prev_timer_id) => self.get_mut_entry(prev_timer_id).next = token,
            Token::Slot(prev_slot_idx) => self.get_mut_slot(prev_slot_idx).next = token,
            Token::Nil => ::unreachable(),
        }
        self.get_mut_slot(slot_idx).prev = token;
    }

    fn reschedule(&mut self, dur: Duration, timer_id: TimerId) {
        let (prev, next) = {
            let entry = self.get_entry(timer_id);
            (entry.prev, entry.next)
        };
        match prev {
            Token::Entry(prev_timer_id) => self.get_mut_entry(prev_timer_id).next = next,
            Token::Slot(prev_slot_idx) => self.get_mut_slot(prev_slot_idx).next = next,
            Token::Nil => ::unreachable(),
        }
        match next {
            Token::Entry(next_timer_id) => self.get_mut_entry(next_timer_id).prev = prev,
            Token::Slot(next_slot_idx) => self.get_mut_slot(next_slot_idx).prev = prev,
            Token::Nil => ::unreachable(),
        }
        self.reschedule_internal(dur, None, timer_id);
    }

    #[inline]
    fn cancel(&mut self, timer_id: TimerId) {
        let entry = self.entries.remove(timer_id).unwrap();
        match entry.prev {
            Token::Entry(timer_id) => self.get_mut_entry(timer_id).next = entry.next,
            Token::Slot(slot_idx) => self.get_mut_slot(slot_idx).next = entry.next,
            Token::Nil => return, // expired
        }
        match entry.next {
            Token::Entry(timer_id) => self.get_mut_entry(timer_id).prev = entry.prev,
            Token::Slot(slot_idx) => self.get_mut_slot(slot_idx).prev = entry.prev,
            Token::Nil => ::unreachable(), // expired
        }
    }

    #[inline]
    fn tick(&mut self) {
        let current_slot = self.current_slot;
        let mut timer_id = match self.get_slot(current_slot).next {
            Token::Slot(index) => {
                self.current_slot = index;
                return;
            }
            Token::Entry(id) => id,
            Token::Nil => ::unreachable(),
        };
        let next_slot;
        let mut exit = false;
        loop {
            let (next, expiration, task) = {
                let entry = self.get_mut_entry(timer_id);
                let next = entry.next;
                entry.prev = Token::Nil;
                entry.next = Token::Nil;
                (next, entry.expiration.take(), entry.task)
            };
            if !exit {
                if let Some(exp) = expiration {
                    let now = Instant::now();
                    if exp >= now + Duration::from_secs(1) {
                        self.reschedule_internal(exp - now, Some(exp), timer_id);
                    } else {
                        exit = super::run_expired_task(task);
                    }
                } else {
                    exit = super::run_expired_task(task);
                }
            }
            match next {
                Token::Entry(id) => timer_id = id,
                Token::Slot(index) => {
                    next_slot = index;
                    break;
                }
                Token::Nil => ::unreachable(),
            }
        }
        self.get_mut_slot(current_slot).next = Token::Slot(next_slot);
        self.get_mut_slot(next_slot).prev = Token::Slot(self.current_slot);
        self.current_slot = next_slot;
    }
}

pub(super) struct Wheel {
    inner: Rc<UnsafeCell<Inner>>,
}

impl Wheel {
    pub fn new() -> Self {
        let dur = Duration::from_secs(1);
        let inner = Rc::new(UnsafeCell::new(Inner::new()));
        let wheel = Wheel {
            inner: inner.clone(),
        };
        super::spawn(
            PeriodicTimer::new(dur, dur)
                .for_each(move |_| {
                    unsafe { &mut *inner.as_ref().get() }.tick();
                    Ok(())
                })
                .into_task(),
        );
        wheel
    }

    #[inline]
    pub fn schedule(&self, dur: Duration, task: TaskId) -> TimerId {
        self.as_mut_inner().schedule(dur, task)
    }

    #[inline]
    pub fn reschedule(&self, dur: Duration, timer_id: TimerId) {
        self.as_mut_inner().reschedule(dur, timer_id)
    }

    #[inline]
    pub fn cancel(&self, timer_id: TimerId) {
        self.as_mut_inner().cancel(timer_id);
    }

    #[inline]
    pub fn is_expired(&self, timer_id: TimerId) -> bool {
        self.as_inner().is_expired(timer_id)
    }

    #[inline]
    fn as_mut_inner(&self) -> &mut Inner {
        unsafe { &mut *(&self.inner).get() }
    }

    #[inline]
    fn as_inner(&self) -> &Inner {
        unsafe { &*(&self.inner).get() }
    }
}

#[derive(Debug, Clone, Copy)]
enum TimerState {
    Unscheduled(Duration),
    Scheduled(TimerId),
    Expired,
    Cancelled,
}

#[derive(Debug)]
pub(super) struct Timer {
    state: TimerState,
}

impl Timer {
    #[inline]
    pub fn new(secs: u64) -> Self {
        let state = if secs == 0 {
            TimerState::Expired
        } else {
            TimerState::Unscheduled(Duration::from_secs(secs))
        };
        Timer { state }
    }

    #[inline]
    pub fn poll(&mut self) -> Poll<(), ()> {
        match self.state {
            TimerState::Scheduled(timer_id) => {
                match super::wt_is_expired(timer_id) {
                    true => Ok(Async::Ready(())),
                    false => Ok(Async::NotReady),
                }
            }
            TimerState::Unscheduled(dur) => {
                let timer_id = super::wt_schedule(dur);
                self.state = TimerState::Scheduled(timer_id);
                Ok(Async::NotReady)
            }
            TimerState::Expired => Ok(Async::Ready(())),
            TimerState::Cancelled => ::unreachable(),
        }
    }

    #[inline]
    pub fn reschedule(&mut self, secs: u64) -> bool {
        match self.state {
            TimerState::Scheduled(timer_id) => {
                super::wt_reschedule(Duration::from_secs(secs), timer_id);
                true
            }
            TimerState::Unscheduled(dur) => {
                let timer_id = super::wt_schedule(dur);
                self.state = TimerState::Scheduled(timer_id);
                true
            }
            TimerState::Expired => false,
            TimerState::Cancelled => ::unreachable(),
        }
    }
}

impl Drop for Timer {
    fn drop(&mut self) {
        if let TimerState::Scheduled(timer_id) = self.state {
            super::wt_cancel(timer_id);
            self.state = TimerState::Cancelled;
        }
    }
}
<|endoftext|>


<commit_before>use super::super::services::image_service;
use super::super::rocket_contrib::JSON;
use super::super::models::{Image, NewImage};
use std::error::Error;
use rocket::response::status;
use rocket::data::Data;
use std::io::prelude::*;
use std::fs::File;
use rocket::response::Stream;

#[get("/images")]
fn get_images() -> Result<JSON<Vec<Image>>, Box<Error>> {
    let result = try!(image_service::get_all());
    Ok(JSON(result))
}

#[get("/images/<id>")]
fn get_image(id: i32) -> Option<JSON<Image>> {
    match image_service::find(id) {
        Ok(t) => Some(JSON(t)),
        Err(_) => None,
    }
}

#[post("/images/new?<new_image>")]
fn new(new_image: NewImage) -> Option<status::Created<JSON<Image>>> {
    match image_service::create(&new_image) {
        Ok(result) => Some(status::Created(format!("/images/{}", result.id), Some(JSON(result)))),
        Err(_) => None,
    }
}

#[post("/images/<id>/file/new", format="text/plain", data="<data>")]
fn new_image_file(id: i32, data: Data) -> Result<status::Created<JSON<Image>>, Box<Error>> {
    let mut stream = data.open();
    let buff = try!(stream.fill_buf());
    let result = try!(image_service::create_image_file(id, buff));
    Ok(status::Created(format!("/images/file/{}", result.id), Some(JSON(result))))
}

#[post("/images/<id>/sidecar/new", format="text/plain", data="<data>")]
fn new_sidecar_file(id: i32, data: Data) -> Result<status::Created<JSON<Image>>, Box<Error>> {
    let mut stream = data.open();
    let buff = try!(stream.fill_buf());
    let result = try!(image_service::create_sidecar_file(id, buff));
    Ok(status::Created(format!("/images/sidecar/{}", result.id), Some(JSON(result))))
}

#[get("/images/<id>/sidecar")]
fn get_sidecar_file(id: i32) -> Option<Stream<File>> {
    match image_service::find_sidecar_file(id) {
        Ok(sf) => Some(Stream::from(sf)),
        Err(_) => None,
    }
}

#[get("/images/<id>/file")]
fn get_image_file(id: i32) -> Option<Stream<File>> {
    match image_service::find_image_file(id) {
        Ok(sf) => Some(Stream::from(sf)),
        Err(_) => None,
    }
}

#[put("/images/update?<image>")]
fn update(image: Image) -> Option<JSON<Image>> {
    match image_service::update(&image) {
        Ok(t) => Some(JSON(t)),
        Err(_) => None,
    }
}
<commit_msg>Remove contentype from image routes<commit_after>use super::super::services::image_service;
use super::super::rocket_contrib::JSON;
use super::super::models::{Image, NewImage};
use std::error::Error;
use rocket::response::status;
use rocket::data::Data;
use std::io::prelude::*;
use std::fs::File;
use rocket::response::Stream;

#[get("/images")]
fn get_images() -> Result<JSON<Vec<Image>>, Box<Error>> {
    let result = try!(image_service::get_all());
    Ok(JSON(result))
}

#[get("/images/<id>")]
fn get_image(id: i32) -> Option<JSON<Image>> {
    match image_service::find(id) {
        Ok(t) => Some(JSON(t)),
        Err(_) => None,
    }
}

#[post("/images/new?<new_image>")]
fn new(new_image: NewImage) -> Option<status::Created<JSON<Image>>> {
    match image_service::create(&new_image) {
        Ok(result) => Some(status::Created(format!("/images/{}", result.id), Some(JSON(result)))),
        Err(_) => None,
    }
}

#[post("/images/<id>/file/new", data="<data>")]
fn new_image_file(id: i32, data: Data) -> Result<status::Created<JSON<Image>>, Box<Error>> {
    let mut stream = data.open();
    let buff = try!(stream.fill_buf());
    let result = try!(image_service::create_image_file(id, buff));
    Ok(status::Created(format!("/images/file/{}", result.id), Some(JSON(result))))
}

#[post("/images/<id>/sidecar/new", data="<data>")]
fn new_sidecar_file(id: i32, data: Data) -> Result<status::Created<JSON<Image>>, Box<Error>> {
    let mut stream = data.open();
    let buff = try!(stream.fill_buf());
    let result = try!(image_service::create_sidecar_file(id, buff));
    Ok(status::Created(format!("/images/sidecar/{}", result.id), Some(JSON(result))))
}

#[get("/images/<id>/sidecar")]
fn get_sidecar_file(id: i32) -> Option<Stream<File>> {
    match image_service::find_sidecar_file(id) {
        Ok(sf) => Some(Stream::from(sf)),
        Err(_) => None,
    }
}

#[get("/images/<id>/file")]
fn get_image_file(id: i32) -> Option<Stream<File>> {
    match image_service::find_image_file(id) {
        Ok(sf) => Some(Stream::from(sf)),
        Err(_) => None,
    }
}

#[put("/images/update?<image>")]
fn update(image: Image) -> Option<JSON<Image>> {
    match image_service::update(&image) {
        Ok(t) => Some(JSON(t)),
        Err(_) => None,
    }
}
<|endoftext|>


<commit_before>use super::{ExecutionStrategy,
            Scope};
use crate::{config,
            error::{Error,
                    Result}};
use futures::stream::StreamExt;
use habitat_common::{cli::FS_ROOT,
                     package_graph::PackageGraph,
                     types::ListenCtlAddr,
                     ui::{Status,
                          UIWriter}};
use habitat_core::{error as herror,
                   fs as hfs,
                   package::{list::temp_package_directory,
                             Identifiable,
                             PackageIdent,
                             PackageInstall}};
use habitat_sup_client::{SrvClient,
                         SrvClientError};
use habitat_sup_protocol;
use std::{fs,
          path::Path,
          str::FromStr};

/// Delete a package and all dependencies which are not used by other packages.
/// We do an ordered traverse of the dependencies, updating the graph as we delete a
/// package. This lets us use simple logic where we continually check if the package
/// we're trying to delete currently has no packages depending on it.
///
/// The full logic is:
/// 1. We find the fully qualified package ident and all its dependencies
/// 2. We find all packages on the filesystem and convert them into a graph
/// 3. We do a BFS on the graph to get the dependencies in order
/// 4. We check if the specified package has any reverse deps
///     4a. If there are, we throw an error
///     4b. If not, we delete the package
/// 5. For each dependency we check if there are any packages which depend on it
///     5a. If there are, we skip it
///     5b. If there are not, we delete it from disk and the graph
///
/// `excludes` is a list of user-supplied `PackageIdent`s.
/// `even_if_running` is a flag indictating that the package should be uninstalled even if it is
/// running.
pub async fn uninstall<U>(ui: &mut U,
                          ident: &PackageIdent,
                          fs_root_path: &Path,
                          execution_strategy: ExecutionStrategy,
                          scope: Scope,
                          excludes: &[PackageIdent],
                          even_if_running: bool)
                          -> Result<()>
    where U: UIWriter
{
    // 1.
    let pkg_install = PackageInstall::load(ident, Some(fs_root_path))?;
    let ident = pkg_install.ident();
    ui.begin(format!("Uninstalling {}", &ident))?;

    let services = if even_if_running {
        // If we want to uninstall the package even if it is running, dont look up the services that
        // are running just use an empty vector.
        Vec::new()
    } else {
        supervisor_services().await?
    };
    if !services.is_empty() {
        ui.status(Status::Determining,
                  "list of running services in supervisor")?;
        for s in services.iter() {
            ui.status(Status::Found, format!("running service {}", s))?;
        }
    }

    // 2.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    // 3.
    let deps = graph.owned_ordered_deps(&ident);

    // 4.
    match graph.count_rdeps(&ident) {
        None => {
            // package not in graph - this shouldn't happen but could be a race condition in Step 2
            // with another hab uninstall. We can continue as what we wanted (package to
            // be removed) has already happened. We're going to continue and try and delete down
            // through the dependency tree anyway
            ui.warn(format!("Tried to find dependant packages of {} but it wasn't in graph.  \
                             Maybe another uninstall command was run at the same time?",
                            &ident))?;
        }
        Some(0) => {
            maybe_delete(ui,
                         &fs_root_path,
                         &pkg_install,
                         execution_strategy,
                         &excludes,
                         &services)?;
            graph.remove(&ident);
        }
        Some(c) => {
            return Err(Error::CannotRemovePackage(ident.clone(), c));
        }
    }

    // 5.
    let mut count = 0;
    match scope {
        Scope::Package => {
            ui.status(Status::Skipping, "dependencies (--no-deps specified)")?;
        }
        Scope::PackageAndDependencies => {
            for p in &deps {
                match graph.count_rdeps(&p) {
                    None => {
                        // package not in graph - this shouldn't happen but could be a race
                        // condition in Step 2 with another hab uninstall. We can
                        // continue as what we wanted (package to be removed) has already happened.
                        // We're going to continue and try and delete down through the
                        // dependency tree anyway
                        ui.warn(format!("Tried to find dependant packages of {} but it wasn't \
                                         in graph.  Maybe another uninstall command was run at \
                                         the same time?",
                                        &p))?;
                    }
                    Some(0) => {
                        let install = PackageInstall::load(&p, Some(fs_root_path))?;
                        maybe_delete(ui,
                                     &fs_root_path,
                                     &install,
                                     execution_strategy,
                                     &excludes,
                                     &services)?;

                        graph.remove(&p);
                        count += 1;
                    }
                    Some(c) => {
                        ui.status(Status::Skipping,
                                  format!("{}. It is a dependency of {} packages", &p, c))?;
                    }
                }
            }
        }
    }

    match execution_strategy {
        ExecutionStrategy::DryRun => {
            ui.end(format!("Would uninstall {} and {} dependencies (Dry run)",
                           &ident, count))?;
        }
        ExecutionStrategy::Run => {
            ui.end(format!("Uninstall of {} and {} dependencies complete",
                           &ident, count))?;
        }
    };
    Ok(())
}

/// Check if we have a launcher/supervisor running out of this habitat root.
/// If the launcher PID file exists then the supervisor is up and running
fn launcher_is_running(fs_root_path: &Path) -> bool {
    let launcher_root = hfs::launcher_root_path(Some(fs_root_path));
    let pid_file_path = launcher_root.join("PID");

    pid_file_path.is_file()
}

async fn supervisor_services() -> Result<Vec<PackageIdent>> {
    if !launcher_is_running(&*FS_ROOT) {
        return Ok(vec![]);
    }

    let cfg = config::load()?;
    let secret_key = config::ctl_secret_key(&cfg)?;
    let listen_ctl_addr = ListenCtlAddr::default();
    let msg = habitat_sup_protocol::ctl::SvcStatus::default();

    let mut out: Vec<PackageIdent> = vec![];
    let mut response = SrvClient::request(&listen_ctl_addr, &secret_key, msg).await?;
    while let Some(message_result) = response.next().await {
        let reply = message_result?;
        match reply.message_id() {
            "ServiceStatus" => {
                let m = reply.parse::<habitat_sup_protocol::types::ServiceStatus>()
                             .map_err(SrvClientError::Decode)?;
                out.push(m.ident.into());
            }
            "NetOk" => (),
            "NetErr" => {
                let err = reply.parse::<habitat_sup_protocol::net::NetErr>()
                               .map_err(SrvClientError::Decode)?;
                return Err(SrvClientError::from(err).into());
            }
            _ => {
                warn!("Unexpected status message, {:?}", reply);
            }
        }
    }
    Ok(out)
}

/// Delete a package from disk, depending upon the ExecutionStrategy supplied
///
/// Returns:
///   Ok(true) - package is deleted
///   Ok(false) - package would be deleted but it's a dry run
///   Err(_) -  IO problem deleting package from filesystem
fn maybe_delete<U>(ui: &mut U,
                   fs_root_path: &Path,
                   install: &PackageInstall,
                   strategy: ExecutionStrategy,
                   excludes: &[PackageIdent],
                   services: &[PackageIdent])
                   -> Result<bool>
    where U: UIWriter
{
    let ident = install.ident();
    let pkg_root_path = hfs::pkg_root_path(Some(fs_root_path));

    let hab = PackageIdent::from_str("core/hab")?;
    if ident.satisfies(&hab) {
        ui.status(Status::Skipping,
                  format!("{}. You can't uninstall core/hab", &ident))?;
        return Ok(false);
    }

    let is_running = services.iter().any(|i| i.satisfies(ident));
    if is_running {
        ui.status(Status::Skipping,
                  format!("{}. It is currently running in the supervisor", &ident))?;
        return Ok(false);
    }

    // The excludes list could be looser than the fully qualified idents.  E.g. if core/redis is on
    // the exclude list then we should exclude core/redis/1.1.0/20180608091936.  We use the
    // `Identifiable` trait which supplies this logic for PackageIdents
    let should_exclude = excludes.iter().any(|i| i.satisfies(ident));
    if should_exclude {
        ui.status(Status::Skipping,
                  format!("{}. It is on the exclusion list", &ident))?;
        return Ok(false);
    }

    match strategy {
        ExecutionStrategy::DryRun => {
            ui.status(Status::DryRunDeleting, &ident)?;
            Ok(false)
        }
        ExecutionStrategy::Run => {
            ui.status(Status::Deleting, &ident)?;
            let pkg_dir = install.installed_path();
            do_clean_delete(&pkg_root_path, &pkg_dir)
        }
    }
}

/// Delete empty parent directories from a given path. don't traverse above
/// the `pkg_root_path`
fn do_clean_delete(pkg_root_path: &Path, real_install_path: &Path) -> Result<bool> {
    // This match will always return Ok(Path) as the install path is always 4 levels
    // below the pkg_root_path
    match real_install_path.parent() {
        Some(real_install_base) => {
            let temp_install_path = temp_package_directory(real_install_path)?.path()
                                                                              .to_path_buf();
            fs::rename(&real_install_path, &temp_install_path)?;
            fs::remove_dir_all(&temp_install_path)?;

            for p in real_install_base.ancestors() {
                // Let's be safe and not rm below the package directories
                if p == pkg_root_path {
                    break;
                }
                match p.read_dir() {
                    Ok(contents) => {
                        // This will calculate the amount of items in the directory
                        match contents.count() {
                            0 => fs::remove_dir(&p)?,
                            _ => break,
                        }
                    }
                    Err(e) => return Err(Error::HabitatCore(herror::Error::IO(e))),
                }
            }
            Ok(true)
        }
        None => unreachable!("Install path doesn't have a parent"),
    }
}
<commit_msg>Add uninstall_many<commit_after>use super::{ExecutionStrategy,
            Scope};
use crate::{config,
            error::{Error,
                    Result}};
use futures::stream::StreamExt;
use habitat_common::{cli::FS_ROOT,
                     package_graph::PackageGraph,
                     types::ListenCtlAddr,
                     ui::{Status,
                          UIWriter}};
use habitat_core::{error as herror,
                   fs as hfs,
                   package::{list::temp_package_directory,
                             Identifiable,
                             PackageIdent,
                             PackageInstall}};
use habitat_sup_client::{SrvClient,
                         SrvClientError};
use habitat_sup_protocol;
use std::{fs,
          path::Path,
          str::FromStr};

pub async fn uninstall<U>(ui: &mut U,
                          ident: impl AsRef<PackageIdent>,
                          fs_root_path: &Path,
                          execution_strategy: ExecutionStrategy,
                          scope: Scope,
                          excludes: &[PackageIdent],
                          even_if_running: bool)
                          -> Result<()>
    where U: UIWriter
{
    uninstall_many(ui,
                   &[ident],
                   fs_root_path,
                   execution_strategy,
                   scope,
                   excludes,
                   even_if_running).await
}

/// Delete packages and all dependencies which are not used by the packages.
/// We do an ordered traverse of the dependencies, updating the graph as we delete a
/// package. This lets us use simple logic where we continually check if the package
/// we're trying to delete currently has no packages depending on it.
///
/// The full logic is:
/// 1. We find all packages on the filesystem and convert them into a graph
/// 2. We find the fully qualified package ident and all its dependencies
/// 3. We do a BFS on the graph to get the dependencies in order
/// 4. We check if the specified package has any reverse deps
///     4a. If there are, we throw an error
///     4b. If not, we delete the package
/// 5. For each dependency we check if there are any packages which depend on it
///     5a. If there are, we skip it
///     5b. If there are not, we delete it from disk and the graph
///
/// `excludes` is a list of user-supplied `PackageIdent`s.
/// `even_if_running` is a flag indictating that the package should be uninstalled even if it is
/// running.
pub async fn uninstall_many<U>(ui: &mut U,
                               idents: &[impl AsRef<PackageIdent>],
                               fs_root_path: &Path,
                               execution_strategy: ExecutionStrategy,
                               scope: Scope,
                               excludes: &[PackageIdent],
                               even_if_running: bool)
                               -> Result<()>
    where U: UIWriter
{
    // 1.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    let services = if even_if_running {
        // If we want to uninstall the package even if it is running, dont look up the services that
        // are running just use an empty vector.
        Vec::new()
    } else {
        supervisor_services().await?
    };
    if !services.is_empty() {
        ui.status(Status::Determining,
                  "list of running services in supervisor")?;
        for s in services.iter() {
            ui.status(Status::Found, format!("running service {}", s))?;
        }
    }

    for ident in idents {
        // 2.
        let ident = ident.as_ref();
        let pkg_install = PackageInstall::load(ident, Some(fs_root_path))?;
        let ident = pkg_install.ident();
        ui.begin(format!("Uninstalling {}", &ident))?;

        // 3.
        let deps = graph.owned_ordered_deps(&ident);

        // 4.
        match graph.count_rdeps(&ident) {
            None => {
                // package not in graph - this shouldn't happen but could be a race condition in
                // Step 2 with another hab uninstall. We can continue as what we
                // wanted (package to be removed) has already happened. We're going
                // to continue and try and delete down through the dependency tree
                // anyway
                ui.warn(format!("Tried to find dependant packages of {} but it wasn't in \
                                 graph.  Maybe another uninstall command was run at the same \
                                 time?",
                                &ident))?;
            }
            Some(0) => {
                maybe_delete(ui,
                             &fs_root_path,
                             &pkg_install,
                             execution_strategy,
                             &excludes,
                             &services)?;
                graph.remove(&ident);
            }
            Some(c) => {
                return Err(Error::CannotRemovePackage(ident.clone(), c));
            }
        }

        // 5.
        let mut count = 0;
        match scope {
            Scope::Package => {
                ui.status(Status::Skipping, "dependencies (--no-deps specified)")?;
            }
            Scope::PackageAndDependencies => {
                for p in &deps {
                    match graph.count_rdeps(&p) {
                        None => {
                            // package not in graph - this shouldn't happen but could be a race
                            // condition in Step 2 with another hab uninstall. We can
                            // continue as what we wanted (package to be removed) has already
                            // happened. We're going to continue and try
                            // and delete down through the dependency
                            // tree anyway
                            ui.warn(format!("Tried to find dependant packages of {} but it \
                                             wasn't in graph.  Maybe another uninstall command \
                                             was run at the same time?",
                                            &p))?;
                        }
                        Some(0) => {
                            let install = PackageInstall::load(&p, Some(fs_root_path))?;
                            maybe_delete(ui,
                                         &fs_root_path,
                                         &install,
                                         execution_strategy,
                                         &excludes,
                                         &services)?;

                            graph.remove(&p);
                            count += 1;
                        }
                        Some(c) => {
                            ui.status(Status::Skipping,
                                      format!("{}. It is a dependency of {} packages", &p, c))?;
                        }
                    }
                }
            }
        }
        match execution_strategy {
            ExecutionStrategy::DryRun => {
                ui.end(format!("Would uninstall {} and {} dependencies (Dry run)",
                               &ident, count))?;
            }
            ExecutionStrategy::Run => {
                ui.end(format!("Uninstall of {} and {} dependencies complete",
                               &ident, count))?;
            }
        };
    }
    Ok(())
}

/// Check if we have a launcher/supervisor running out of this habitat root.
/// If the launcher PID file exists then the supervisor is up and running
fn launcher_is_running(fs_root_path: &Path) -> bool {
    let launcher_root = hfs::launcher_root_path(Some(fs_root_path));
    let pid_file_path = launcher_root.join("PID");

    pid_file_path.is_file()
}

async fn supervisor_services() -> Result<Vec<PackageIdent>> {
    if !launcher_is_running(&*FS_ROOT) {
        return Ok(vec![]);
    }

    let cfg = config::load()?;
    let secret_key = config::ctl_secret_key(&cfg)?;
    let listen_ctl_addr = ListenCtlAddr::default();
    let msg = habitat_sup_protocol::ctl::SvcStatus::default();

    let mut out: Vec<PackageIdent> = vec![];
    let mut response = SrvClient::request(&listen_ctl_addr, &secret_key, msg).await?;
    while let Some(message_result) = response.next().await {
        let reply = message_result?;
        match reply.message_id() {
            "ServiceStatus" => {
                let m = reply.parse::<habitat_sup_protocol::types::ServiceStatus>()
                             .map_err(SrvClientError::Decode)?;
                out.push(m.ident.into());
            }
            "NetOk" => (),
            "NetErr" => {
                let err = reply.parse::<habitat_sup_protocol::net::NetErr>()
                               .map_err(SrvClientError::Decode)?;
                return Err(SrvClientError::from(err).into());
            }
            _ => {
                warn!("Unexpected status message, {:?}", reply);
            }
        }
    }
    Ok(out)
}

/// Delete a package from disk, depending upon the ExecutionStrategy supplied
///
/// Returns:
///   Ok(true) - package is deleted
///   Ok(false) - package would be deleted but it's a dry run
///   Err(_) -  IO problem deleting package from filesystem
fn maybe_delete<U>(ui: &mut U,
                   fs_root_path: &Path,
                   install: &PackageInstall,
                   strategy: ExecutionStrategy,
                   excludes: &[PackageIdent],
                   services: &[PackageIdent])
                   -> Result<bool>
    where U: UIWriter
{
    let ident = install.ident();
    let pkg_root_path = hfs::pkg_root_path(Some(fs_root_path));

    let hab = PackageIdent::from_str("core/hab")?;
    if ident.satisfies(&hab) {
        ui.status(Status::Skipping,
                  format!("{}. You can't uninstall core/hab", &ident))?;
        return Ok(false);
    }

    let is_running = services.iter().any(|i| i.satisfies(ident));
    if is_running {
        ui.status(Status::Skipping,
                  format!("{}. It is currently running in the supervisor", &ident))?;
        return Ok(false);
    }

    // The excludes list could be looser than the fully qualified idents.  E.g. if core/redis is on
    // the exclude list then we should exclude core/redis/1.1.0/20180608091936.  We use the
    // `Identifiable` trait which supplies this logic for PackageIdents
    let should_exclude = excludes.iter().any(|i| i.satisfies(ident));
    if should_exclude {
        ui.status(Status::Skipping,
                  format!("{}. It is on the exclusion list", &ident))?;
        return Ok(false);
    }

    match strategy {
        ExecutionStrategy::DryRun => {
            ui.status(Status::DryRunDeleting, &ident)?;
            Ok(false)
        }
        ExecutionStrategy::Run => {
            ui.status(Status::Deleting, &ident)?;
            let pkg_dir = install.installed_path();
            do_clean_delete(&pkg_root_path, &pkg_dir)
        }
    }
}

/// Delete empty parent directories from a given path. don't traverse above
/// the `pkg_root_path`
fn do_clean_delete(pkg_root_path: &Path, real_install_path: &Path) -> Result<bool> {
    // This match will always return Ok(Path) as the install path is always 4 levels
    // below the pkg_root_path
    match real_install_path.parent() {
        Some(real_install_base) => {
            let temp_install_path = temp_package_directory(real_install_path)?.path()
                                                                              .to_path_buf();
            fs::rename(&real_install_path, &temp_install_path)?;
            fs::remove_dir_all(&temp_install_path)?;

            for p in real_install_base.ancestors() {
                // Let's be safe and not rm below the package directories
                if p == pkg_root_path {
                    break;
                }
                match p.read_dir() {
                    Ok(contents) => {
                        // This will calculate the amount of items in the directory
                        match contents.count() {
                            0 => fs::remove_dir(&p)?,
                            _ => break,
                        }
                    }
                    Err(e) => return Err(Error::HabitatCore(herror::Error::IO(e))),
                }
            }
            Ok(true)
        }
        None => unreachable!("Install path doesn't have a parent"),
    }
}
<|endoftext|>