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

extern crate failure;
extern crate json;
extern crate libc;
extern crate proc_macro2;
extern crate regex;
extern crate syn;
extern crate tempfile;
#[macro_use]
extern crate lazy_static;
extern crate backtrace;
extern crate libloading;
extern crate rand;
#[macro_use]
extern crate sig;

#[macro_use]
mod errors;
mod cargo_metadata;
mod child_process;
mod code_block;
mod command_context;
mod crate_config;
mod eval_context;
#[allow(dead_code, private_no_mangle_fns)]
mod evcxr_internal_runtime;
mod idents;
mod item;
mod module;
mod runtime;
mod statement_splitter;

pub use command_context::CommandContext;
pub use errors::{CompilationError, Error};
pub use eval_context::{EvalContext, EvalContextOutputs, EvalOutputs};
pub use runtime::runtime_hook;
<commit_msg>Fix a warning<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate failure;
extern crate json;
extern crate libc;
extern crate proc_macro2;
extern crate regex;
extern crate syn;
extern crate tempfile;
#[macro_use]
extern crate lazy_static;
extern crate backtrace;
extern crate libloading;
extern crate rand;
#[macro_use]
extern crate sig;

#[macro_use]
mod errors;
mod cargo_metadata;
mod child_process;
mod code_block;
mod command_context;
mod crate_config;
mod eval_context;
#[allow(dead_code)]
mod evcxr_internal_runtime;
mod idents;
mod item;
mod module;
mod runtime;
mod statement_splitter;

pub use command_context::CommandContext;
pub use errors::{CompilationError, Error};
pub use eval_context::{EvalContext, EvalContextOutputs, EvalOutputs};
pub use runtime::runtime_hook;
<|endoftext|>


<commit_before>/// Multiclass analyses of the iris dataset build on binary logistic classifaction
///
/// Also demonstrates online training, and usage of custom feature type
extern crate csv;
extern crate vikos;
extern crate rustc_serialize;

use vikos::{Teacher, Model};
use std::default::Default;

const PATH : &'static str = "examples/data/iris.csv";

/// A custom type used to hold all features
#[derive(Clone, Default, RustcDecodable)]
struct Features{
    sepal_length : f64,
    sepal_width : f64,
    petal_length : f64,
    petal_width : f64
}

// Trait required by `vikos::model::Logistic`
impl vikos::linear_algebra::Vector for Features{

    fn dimension(&self) -> usize{
        4
    }

    fn at(&self, index : usize) -> f64{
        match index{
            0 => self.sepal_length,
            1 => self.sepal_width,
            2 => self.petal_length,
            3 => self.petal_width,
            _ => panic!("out of bounds")
        }
    }

    fn mut_at(&mut self, index : usize) -> & mut f64{
        match index{
            0 => &mut self.sepal_length,
            1 => &mut self.sepal_width,
            2 => &mut self.petal_length,
            3 => &mut self.petal_width,
            _ => panic!("out of bounds")
        }
    }
}

fn main() {

    let teacher = vikos::teacher::Nesterov{l0 : 0.0001, t: 1000.0, inertia: 0.99};
    let cost = vikos::cost::MaxLikelihood{};

    // We train three binary classifiers. One for each specis of iris.
    // Each of this classifiers will tell us the propability of an
    // observation belonging to its particular class
    let mut setosa = vikos::model::Logistic::default();
    let mut versicolor = vikos::model::Logistic::default();
    let mut virginica = vikos::model::Logistic::default();

    // Each of the classifieres has its own training state
    let mut train_setosa = teacher.new_training(&setosa);
    let mut train_versicolor = teacher.new_training(&versicolor);
    let mut train_virginica = teacher.new_training(&virginica);

    // Read iris Data
    for epoch in 0..300{
        let mut rdr = csv::Reader::from_file(PATH).expect("File is ok");
        let mut hit = 0;
        let mut miss = 0;

        for row in rdr.decode() {

            // Learn event
            let (truth, features) : (String, Features) = row.unwrap();
            teacher.teach_event(&mut train_setosa, &mut setosa, &cost, &features, truth == "setosa");
            teacher.teach_event(&mut train_versicolor, &mut versicolor, &cost, &features, truth == "versicolor");
            teacher.teach_event(&mut train_virginica, &mut virginica, &cost, &features, truth == "virginica");

            // Make prediction using current expertise
            let p_setosa = setosa.predict(&features);
            let p_versicolor = versicolor.predict(&features);
            let p_virginica = virginica.predict(&features);
            let prediction = if p_setosa > p_versicolor {
                if p_setosa > p_virginica { "setosa" } else { "virginica" }
            } else {
                if p_versicolor > p_virginica { "versicolor" } else { "virginica" }
            };

            if prediction == truth{
                hit += 1;
            } else {
                miss += 1;
            }
        }
        let accuracy = hit as f64 / (hit + miss) as f64;

        println!("epoch: {}, accuracy: {}", epoch, accuracy);
    }
}
<commit_msg>removed custom feature vector implementation in iris sample<commit_after>/// Multiclass analyses of the iris dataset build on binary logistic classifaction
///
/// Also demonstrates online training, and usage of custom feature type
extern crate csv;
extern crate vikos;
extern crate rustc_serialize;

use vikos::{Teacher, Model};
use std::default::Default;

const PATH : &'static str = "examples/data/iris.csv";

type Features = [f64;4];

fn main() {

    let teacher = vikos::teacher::Nesterov{l0 : 0.0001, t: 1000.0, inertia: 0.99};
    let cost = vikos::cost::MaxLikelihood{};

    // We train three binary classifiers. One for each specis of iris.
    // Each of this classifiers will tell us the propability of an
    // observation belonging to its particular class
    let mut setosa = vikos::model::Logistic::default();
    let mut versicolor = vikos::model::Logistic::default();
    let mut virginica = vikos::model::Logistic::default();

    // Each of the classifieres has its own training state
    let mut train_setosa = teacher.new_training(&setosa);
    let mut train_versicolor = teacher.new_training(&versicolor);
    let mut train_virginica = teacher.new_training(&virginica);

    // Read iris Data
    for epoch in 0..300{
        let mut rdr = csv::Reader::from_file(PATH).expect("File is ok");
        let mut hit = 0;
        let mut miss = 0;

        for row in rdr.decode() {

            // Learn event
            let (truth, features) : (String, Features) = row.unwrap();
            teacher.teach_event(&mut train_setosa, &mut setosa, &cost, &features, truth == "setosa");
            teacher.teach_event(&mut train_versicolor, &mut versicolor, &cost, &features, truth == "versicolor");
            teacher.teach_event(&mut train_virginica, &mut virginica, &cost, &features, truth == "virginica");

            // Make prediction using current expertise
            let p_setosa = setosa.predict(&features);
            let p_versicolor = versicolor.predict(&features);
            let p_virginica = virginica.predict(&features);
            let prediction = if p_setosa > p_versicolor {
                if p_setosa > p_virginica { "setosa" } else { "virginica" }
            } else {
                if p_versicolor > p_virginica { "versicolor" } else { "virginica" }
            };

            if prediction == truth{
                hit += 1;
            } else {
                miss += 1;
            }
        }
        let accuracy = hit as f64 / (hit + miss) as f64;

        println!("epoch: {}, accuracy: {}", epoch, accuracy);
    }
}
<|endoftext|>


<commit_before>
extern crate glutin;
extern crate glium;
extern crate image;
extern crate clock_ticks;
extern crate uil;

use std::old_io::{File, BufferedReader, BufReader};
use glium::{DisplayBuild, Surface};
use uil::layout;
use uil::rendering;
use std::old_io::timer;
use std::time::duration::Duration;

fn main() {

    //////////////////////////////////////////////////////////////////////////////
    // uil related code
    //
    let library = {
        let file = File::open(&Path::new("./examples/menu.markup")).unwrap();
        let reader = BufferedReader::new(file);
        uil::markup::parse(uil::StdOutErrorReporter, reader)
    };

    let styledefs = {
        let file = File::open(&Path::new("./examples/menu.deps")).unwrap();
        let reader = BufferedReader::new(file);
        uil::deps::parse(uil::StdOutErrorReporter, reader)
    };

    let stylesheet = {
        let file = File::open(&Path::new("./examples/menu.style")).unwrap();
        let reader = BufferedReader::new(file);
        uil::style::parse(uil::StdOutErrorReporter, reader, &styledefs)
    };

    let view = uil::View::new(library.get("main").unwrap(), &stylesheet);
    let mut router = uil::Router::new();

    router.add_view("main".to_string(), view);

    //////////////////////////////////////////////////////////////////////////////
    // glium related code
    //
    let display = glutin::WindowBuilder::new()
        .with_vsync()
        .build_glium()
        .unwrap();

    let (width, height) = display.get_window().unwrap().get_inner_size().unwrap();

    let image = image::load(BufReader::new(include_bytes!("./btn.png")),
        image::PNG).unwrap();

    // Use of the "use_glium" feature.
    let mut renderer = uil::backend::GliumRenderer::new(&display, image);

    //////////////////////////////////////////////////////////////////////////////
    // main loop (modified example from glium lib)
    //
    start_loop(|| {

        // Update views
        router.update(uil::Viewport { width: width as f32, height: height as f32 });

        // Render views
        router.render_views(&mut renderer);

        // polling and handling the events received by the window
        for event in display.poll_events() {
            match event {
                glutin::Event::Closed => return Action::Stop,
                _ => ()
            }
        }

        Action::Continue
    });
}

pub enum Action {
    Stop,
    Continue,
}

pub fn start_loop<F>(mut callback: F) where F: FnMut() -> Action {
    let mut accumulator = 0;
    let mut previous_clock = clock_ticks::precise_time_ns();
    loop {
        match callback() {
            Action::Stop => break,
            Action::Continue => ()
        };
        let now = clock_ticks::precise_time_ns();
        accumulator += now - previous_clock;
        previous_clock = now;
        const FIXED_TIME_STAMP: u64 = 16666667;
        while accumulator >= FIXED_TIME_STAMP {
            accumulator -= FIXED_TIME_STAMP;
            // if you have a game, update the state here
        }
        timer::sleep(Duration::nanoseconds((FIXED_TIME_STAMP - accumulator) as i64));
    }
}
<commit_msg>Updated example with latest changes.<commit_after>
extern crate glutin;
extern crate glium;
extern crate image;
extern crate clock_ticks;
extern crate uil;

use std::old_io::{File, BufferedReader, BufReader};
use glium::{DisplayBuild, Surface};
use uil::layout;
use uil::rendering;
use std::old_io::timer;
use std::time::duration::Duration;

fn main() {

    //////////////////////////////////////////////////////////////////////////////
    // uil related code
    //
    let library = {
        let file = File::open(&Path::new("./examples/menu.markup")).unwrap();
        let reader = BufferedReader::new(file);
        uil::markup::parse(uil::StdOutErrorReporter, reader)
    };

    let styledefs = {
        let file = File::open(&Path::new("./examples/menu.deps")).unwrap();
        let reader = BufferedReader::new(file);
        uil::deps::parse(uil::StdOutErrorReporter, reader)
    };

    let stylesheet = {
        let file = File::open(&Path::new("./examples/menu.style")).unwrap();
        let reader = BufferedReader::new(file);
        uil::style::parse(uil::StdOutErrorReporter, reader, &styledefs)
    };

    let mut router = uil::Router::from_library_and_stylesheet(library, &stylesheet);

    //////////////////////////////////////////////////////////////////////////////
    // glium related code
    //
    let display = glutin::WindowBuilder::new()
        .with_vsync()
        .build_glium()
        .unwrap();

    let (width, height) = display.get_window().unwrap().get_inner_size().unwrap();

    let image = image::load(BufReader::new(include_bytes!("./btn.png")),
        image::PNG).unwrap();

    // Use of the "use_glium" feature.
    let mut renderer = uil::backend::GliumRenderer::new(&display, image);

    //////////////////////////////////////////////////////////////////////////////
    // main loop (modified example from glium lib)
    //
    start_loop(|| {

        // Update views
        router.update(uil::Viewport { width: width as f32, height: height as f32 });

        // Render views
        router.render_views(&mut renderer);

        // polling and handling the events received by the window
        for event in display.poll_events() {
            match event {
                glutin::Event::Closed => return Action::Stop,
                _ => ()
            }
        }

        Action::Continue
    });
}

pub enum Action {
    Stop,
    Continue,
}

pub fn start_loop<F>(mut callback: F) where F: FnMut() -> Action {
    let mut accumulator = 0;
    let mut previous_clock = clock_ticks::precise_time_ns();
    loop {
        match callback() {
            Action::Stop => break,
            Action::Continue => ()
        };
        let now = clock_ticks::precise_time_ns();
        accumulator += now - previous_clock;
        previous_clock = now;
        const FIXED_TIME_STAMP: u64 = 16666667;
        while accumulator >= FIXED_TIME_STAMP {
            accumulator -= FIXED_TIME_STAMP;
            // if you have a game, update the state here
        }
        timer::sleep(Duration::nanoseconds((FIXED_TIME_STAMP - accumulator) as i64));
    }
}
<|endoftext|>


<commit_before>
extern crate ntpclient;
extern crate time;

use std::sync::{Arc, Mutex, Condvar};
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;
use time::{Timespec, at, Duration, precise_time_ns};

const BILLION: u64 = 1_000_000_000;

// NOTE: obsolete when Rust starts to support UDP receive timeout
struct NtpFetcher {
    server: String,
    last_sync: Arc<Mutex<(Timespec, u64)>>,
    poll: Duration,
    last_poll: u64,
}

impl NtpFetcher {
    fn new(server: String, ntp_poll: Duration) -> NtpFetcher {
        let mut ntp = NtpFetcher {
            server: server,
            last_sync: Arc::new(Mutex::new((Timespec::new(0,0), 0))),
            poll: ntp_poll,
            last_poll: 0,
        };

        ntp.consider_poll_ntp();
        ntp
    }

    fn consider_poll_ntp(&mut self) {
        if let Ok(ref lock) = self.last_sync.lock() {
            let (_, ref_time) = **lock;
            let poll_interval = if ref_time == 0 {
                // when never NTP timestamp was received, try again after 10 minutes
                Duration::minutes(10)
            } else {
                self.poll
            };

            let curr = precise_time_ns();
            let must_poll = (self.last_poll == 0) ||
                (((curr - self.last_poll) / BILLION) >= poll_interval.num_seconds() as u64);

            if must_poll {
                self.last_poll = curr;
                let sync = self.last_sync.clone();
                let server = self.server.clone();

                thread::spawn(move || {
                    println!("receiving timestamp");
                    if let Ok(ts) = ntpclient::retrieve_ntp_timestamp(&server[..]) {
                        if let Ok(ref mut lock) = sync.lock() {
                            **lock = (ts, precise_time_ns());
                        }
                    }
                });
            }
        }
    }

    fn get_timespec(&mut self) -> Option<Timespec> {
        self.consider_poll_ntp();
        match self.last_sync.lock() {
            Ok(ref lock) => {
                let (sync_ts, ref_time) = **lock;

                match ref_time {
                    0 => None,
                    _ => {
                        let ns = precise_time_ns() - ref_time;
                        Some(sync_ts + Duration::nanoseconds(ns as i64))
                    }
                }
            },
            Err(_) => None,
        }
    }
}

struct Ticker {
    rx: Receiver<Timespec>,
    tx: Sender<Timespec>,
    joiner: thread::JoinHandle<()>,
    leave_guard: Arc<(Mutex<bool>, Condvar)>,
}

impl Ticker {
    fn spawn(server: &str, tick_interval: Duration, ntp_poll: Duration) -> Ticker {
        let (tx, rx) = channel();
        let leave_guard = Arc::new((Mutex::new(true), Condvar::new()));
        let waiter = leave_guard.clone();
        let cloned_tx = tx.clone();
        let server = server.into();

        let joiner = thread::spawn(move || {
            let mut ntp = NtpFetcher::new(server, ntp_poll);
            let &(ref lock, ref cvar) = &*waiter;
            let mut leaver = lock.lock().ok().expect("BUG: mutex cannot claimed inside thread");

            // this loop sends the NTP synchronized timestamp to receiving end of the channel
            while *leaver {
                let (new_leaver, _) =
                    cvar.wait_timeout_ms(leaver,
                                         tick_interval.num_milliseconds() as u32).ok().expect(
                                             "BUG: unexpected error during wait");

                leaver = new_leaver;

                if *leaver {
                    if let Some(ts) = ntp.get_timespec() {
                        tx.send(ts).ok().expect("BUG: cannot send timestamp");
                    }
                }
            }
        });

        Ticker {
            rx: rx,
            tx: cloned_tx,
            joiner: joiner,
            leave_guard: leave_guard,
        }
    }

    fn stop_ticker(self) {
        let &(ref lock, ref cvar) = &*(self.leave_guard);
        let mut leaver = lock.lock().ok().expect("BUG: cannot claim mutex during stop_ticker");
        *leaver = false;
        drop(leaver);
        cvar.notify_all();
        let _ = self.joiner.join();
    }
}

fn main() {
    let ticker = Ticker::spawn("nl.pool.ntp.org", Duration::seconds(1), Duration::days(1));

    let mut teller = 0; // XXX

    loop {
        let timestamp = ticker.rx.recv().unwrap();

        println!("{}", at(timestamp).asctime());

        teller = teller + 1;
        if teller >= 5 {
            ticker.stop_ticker();
            break;
        }
    }
}
<commit_msg>Ticker: add generics<commit_after>
extern crate ntpclient;
extern crate time;

use std::sync::{Arc, Mutex, Condvar};
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;
use time::{Timespec, at, Duration, precise_time_ns};

const BILLION: u64 = 1_000_000_000;

// NOTE: obsolete when Rust starts to support UDP receive timeout
struct NtpFetcher {
    server: String,
    last_sync: Arc<Mutex<(Timespec, u64)>>,
    poll: Duration,
    last_poll: u64,
}

impl NtpFetcher {
    fn new(server: String, ntp_poll: Duration) -> NtpFetcher {
        let mut ntp = NtpFetcher {
            server: server,
            last_sync: Arc::new(Mutex::new((Timespec::new(0,0), 0))),
            poll: ntp_poll,
            last_poll: 0,
        };

        ntp.consider_poll_ntp();
        ntp
    }

    fn consider_poll_ntp(&mut self) {
        if let Ok(ref lock) = self.last_sync.lock() {
            let (_, ref_time) = **lock;
            let poll_interval = if ref_time == 0 {
                // when never NTP timestamp was received, try again after 10 minutes
                Duration::minutes(10)
            } else {
                self.poll
            };

            let curr = precise_time_ns();
            let must_poll = (self.last_poll == 0) ||
                (((curr - self.last_poll) / BILLION) >= poll_interval.num_seconds() as u64);

            if must_poll {
                self.last_poll = curr;
                let sync = self.last_sync.clone();
                let server = self.server.clone();

                thread::spawn(move || {
                    println!("receiving timestamp");
                    if let Ok(ts) = ntpclient::retrieve_ntp_timestamp(&server[..]) {
                        if let Ok(ref mut lock) = sync.lock() {
                            **lock = (ts, precise_time_ns());
                        }
                    }
                });
            }
        }
    }

    fn get_timespec(&mut self) -> Option<Timespec> {
        self.consider_poll_ntp();
        match self.last_sync.lock() {
            Ok(ref lock) => {
                let (sync_ts, ref_time) = **lock;

                match ref_time {
                    0 => None,
                    _ => {
                        let ns = precise_time_ns() - ref_time;
                        Some(sync_ts + Duration::nanoseconds(ns as i64))
                    }
                }
            },
            Err(_) => None,
        }
    }
}

struct Ticker<C> {
    rx: Receiver<(C, Option<Timespec>)>,
    tx: Sender<(C, Option<Timespec>)>,
    joiner: thread::JoinHandle<()>,
    leave_guard: Arc<(Mutex<bool>, Condvar)>,
}

impl<C> Ticker<C> where C: Send + Copy + 'static {
    fn spawn(server: &str, tick_interval: Duration, ntp_poll: Duration, event: C) -> Ticker<C> {
        let (tx, rx) = channel();
        let leave_guard = Arc::new((Mutex::new(true), Condvar::new()));
        let waiter = leave_guard.clone();
        let cloned_tx = tx.clone();
        let server = server.into();

        let joiner = thread::spawn(move || {
            let mut ntp = NtpFetcher::new(server, ntp_poll);
            let &(ref lock, ref cvar) = &*waiter;
            let mut leaver = lock.lock().ok().expect("BUG: mutex cannot claimed inside thread");

            // this loop sends the NTP synchronized timestamp to receiving end of the channel
            while *leaver {
                let (new_leaver, _) =
                    cvar.wait_timeout_ms(leaver,
                                         tick_interval.num_milliseconds() as u32).ok().expect(
                                             "BUG: unexpected error during wait");

                leaver = new_leaver;

                if *leaver {
                    if let Some(ts) = ntp.get_timespec() {
                        tx.send((event, Some(ts))).ok().expect("BUG: cannot send timestamp");
                    }
                }
            }
        });

        Ticker {
            rx: rx,
            tx: cloned_tx,
            joiner: joiner,
            leave_guard: leave_guard,
        }
    }

    fn stop_ticker(self) {
        let &(ref lock, ref cvar) = &*(self.leave_guard);
        let mut leaver = lock.lock().ok().expect("BUG: cannot claim mutex during stop_ticker");
        *leaver = false;
        drop(leaver);
        cvar.notify_all();
        let _ = self.joiner.join();
    }

    fn get_transmitter(&self) -> Sender<(C, Option<Timespec>)> {
        self.tx.clone()
    }
}

#[derive(Copy, Clone)]
enum Dummy {
    Dummy
}

fn main() {
    let ticker = Ticker::spawn("nl.pool.ntp.org", Duration::seconds(1), Duration::days(1), Dummy::Dummy);
    let _ = ticker.get_transmitter(); // suppress warning

    let mut teller = 0; // XXX

    loop {
        let (_, timestamp) = ticker.rx.recv().unwrap();

        if let Some(timestamp) = timestamp {
            println!("{}", at(timestamp).asctime());
        }

        teller = teller + 1;
        if teller >= 5 {
            ticker.stop_ticker();
            break;
        }
    }
}
<|endoftext|>


<commit_before>extern crate polydraw;

use polydraw::{Application, Renderer, Frame};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

struct Poly {
   points: Vec<Point>,
   color: RGB,
}

struct Scene {
   polys: Vec<Poly>,
}

impl Scene {
   #[inline]
   fn new() -> Self {
      Scene {
         polys: Vec::new(),
      }
   }

   #[inline]
   pub fn push(&mut self, poly: Poly) {
      self.polys.push(poly);
   }
}

struct DevRenderer {
   scene: Scene,
}

impl DevRenderer {
   fn new(scene: Scene) -> Self {
      DevRenderer {
         scene: scene,
      }
   }

   fn _render_poly(&self, frame: &mut Frame, poly: &Poly) {
      let min_y_index = _min_y_index(poly);

      println!("[{:?}] = {:?}", min_y_index, poly.points[min_y_index]);

      for point in poly.points.iter() {
         frame.put_pixel(point.x as i32, point.y as i32, &poly.color)
      }
   }
}

impl Renderer for DevRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      for poly in self.scene.polys.iter() {
         self._render_poly(frame, poly);
      }

      panic!("Bye!");
   }
}

fn _min_y_index(poly: &Poly) -> usize {
   let mut max_after = false;

   let (first, rest) = poly.points.split_first().unwrap();

   let mut min_y = first.y;
   let mut min_index: usize = 0;

   for (index, point) in rest.iter().enumerate() {
      if point.y > min_y {
         if max_after {
            return min_index;
         }
      } else {
         max_after = true;
         min_index = index + 1;
         min_y = point.y;
      }
   }

   min_index
}

fn main() {
   let mut scene = Scene::new();

   let poly_a = Poly {
      points: vec![
         Point::new(1100, 400),
         Point::new(500, 700),
         Point::new(200, 200),
      ],
      color: RGB::new(34, 78, 29),
   };

   let poly_b = Poly {
      points: vec![
         Point::new(100, 500),
         Point::new(700, 100),
         Point::new(900, 200),
         Point::new(900, 1000),
      ],
      color: RGB::new(128, 59, 89),
   };

   scene.push(poly_a);
   scene.push(poly_b);

   let mut renderer = DevRenderer::new(scene);

   Application::new()
      .renderer(&mut renderer)
      .title("Dev Rasterizer")
      .size(800, 450)
      .run();
}

<commit_msg>Same algorithm for max y of vector of points<commit_after>extern crate polydraw;

use polydraw::{Application, Renderer, Frame};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

struct Poly {
   points: Vec<Point>,
   color: RGB,
}

struct Scene {
   polys: Vec<Poly>,
}

impl Scene {
   #[inline]
   fn new() -> Self {
      Scene {
         polys: Vec::new(),
      }
   }

   #[inline]
   pub fn push(&mut self, poly: Poly) {
      self.polys.push(poly);
   }
}

struct DevRenderer {
   scene: Scene,
}

impl DevRenderer {
   fn new(scene: Scene) -> Self {
      DevRenderer {
         scene: scene,
      }
   }

   fn _render_poly(&self, frame: &mut Frame, poly: &Poly) {
      let min_y_index = _min_y_index(poly);
      let max_y_index = _max_y_index(poly);

      println!("MIN [{:?}] = {:?}", min_y_index, poly.points[min_y_index]);

      println!("MAX [{:?}] = {:?}", max_y_index, poly.points[max_y_index]);

      for point in poly.points.iter() {
         frame.put_pixel(point.x as i32, point.y as i32, &poly.color)
      }
   }
}

impl Renderer for DevRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      for poly in self.scene.polys.iter() {
         self._render_poly(frame, poly);
      }

      panic!("Bye!");
   }
}

fn _min_y_index(poly: &Poly) -> usize {
   let mut max_after = false;

   let (first, rest) = poly.points.split_first().unwrap();

   let mut min_y = first.y;
   let mut min_index: usize = 0;

   for (index, point) in rest.iter().enumerate() {
      if point.y > min_y {
         if max_after {
            return min_index;
         }
      } else {
         max_after = true;
         min_index = index + 1;
         min_y = point.y;
      }
   }

   min_index
}

fn _max_y_index(poly: &Poly) -> usize {
   let mut min_after = false;

   let (first, rest) = poly.points.split_first().unwrap();

   let mut max_y = first.y;
   let mut max_index: usize = 0;

   for (index, point) in rest.iter().enumerate() {
      if point.y < max_y {
         if min_after {
            return max_index;
         }
      } else {
         min_after = true;
         max_index = index + 1;
         max_y = point.y;
      }
   }

   max_index
}

fn main() {
   let mut scene = Scene::new();

   let poly_a = Poly {
      points: vec![
         Point::new(1100, 400),
         Point::new(500, 700),
         Point::new(200, 200),
      ],
      color: RGB::new(34, 78, 29),
   };

   let poly_b = Poly {
      points: vec![
         Point::new(100, 500),
         Point::new(700, 100),
         Point::new(900, 200),
         Point::new(900, 1000),
      ],
      color: RGB::new(128, 59, 89),
   };

   scene.push(poly_a);
   scene.push(poly_b);

   let mut renderer = DevRenderer::new(scene);

   Application::new()
      .renderer(&mut renderer)
      .title("Dev Rasterizer")
      .size(800, 450)
      .run();
}

<|endoftext|>


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

use std::env;
use std::error::Error;
use std::io::{self, Write};
use std::path::{Path, PathBuf};

use clap::{App, ArgMatches, SubCommand, AppSettings};

use mdbook::MDBook;

fn main() {
    let d_message = "-d, --dest-dir=[dest-dir]
'The output directory for your book{n}(Defaults to ./book when omitted)'";
    let dir_message = "[dir]
'A directory for your book{n}(Defaults to Current Directory when omitted)'";

    let matches = App::new("rustbook")
                    .about("Build a book with mdBook")
                    .author("Steve Klabnik <steve@steveklabnik.com>")
                    .version(&*format!("v{}", crate_version!()))
                    .setting(AppSettings::SubcommandRequired)
                    .subcommand(SubCommand::with_name("build")
                        .about("Build the book from the markdown files")
                        .arg_from_usage(d_message)
                        .arg_from_usage(dir_message))
                    .get_matches();

    // Check which subcomamnd the user ran...
    let res = match matches.subcommand() {
        ("build", Some(sub_matches)) => build(sub_matches),
        ("test", Some(sub_matches)) => test(sub_matches),
        (_, _) => unreachable!(),
    };

    if let Err(e) = res {
        writeln!(&mut io::stderr(), "An error occured:\n{}", e).ok();
        ::std::process::exit(101);
    }
}

// Build command implementation
fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
    let book = build_mdbook_struct(args);

    let mut book = match args.value_of("dest-dir") {
        Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
        None => book
    };

    try!(book.build());

    Ok(())
}

fn test(args: &ArgMatches) -> Result<(), Box<Error>> {
    let mut book = build_mdbook_struct(args);

    try!(book.test());

    Ok(())
}

fn build_mdbook_struct(args: &ArgMatches) -> mdbook::MDBook {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::new(&book_dir).read_config();

    // By default mdbook will attempt to create non-existent files referenced
    // from SUMMARY.md files. This is problematic on CI where we mount the
    // source directory as readonly. To avoid any issues, we'll disabled
    // mdbook's implicit file creation feature.
    book.create_missing = false;

    book
}

fn get_book_dir(args: &ArgMatches) -> PathBuf {
    if let Some(dir) = args.value_of("dir") {
        // Check if path is relative from current dir, or absolute...
        let p = Path::new(dir);
        if p.is_relative() {
            env::current_dir().unwrap().join(dir)
        } else {
            p.to_path_buf()
        }
    } else {
        env::current_dir().unwrap()
    }
}
<commit_msg>rustbook: remove dead test functions<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
extern crate mdbook;
#[macro_use]
extern crate clap;

use std::env;
use std::error::Error;
use std::io::{self, Write};
use std::path::{Path, PathBuf};

use clap::{App, ArgMatches, SubCommand, AppSettings};

use mdbook::MDBook;

fn main() {
    let d_message = "-d, --dest-dir=[dest-dir]
'The output directory for your book{n}(Defaults to ./book when omitted)'";
    let dir_message = "[dir]
'A directory for your book{n}(Defaults to Current Directory when omitted)'";

    let matches = App::new("rustbook")
                    .about("Build a book with mdBook")
                    .author("Steve Klabnik <steve@steveklabnik.com>")
                    .version(&*format!("v{}", crate_version!()))
                    .setting(AppSettings::SubcommandRequired)
                    .subcommand(SubCommand::with_name("build")
                        .about("Build the book from the markdown files")
                        .arg_from_usage(d_message)
                        .arg_from_usage(dir_message))
                    .get_matches();

    // Check which subcomamnd the user ran...
    let res = match matches.subcommand() {
        ("build", Some(sub_matches)) => build(sub_matches),
        (_, _) => unreachable!(),
    };

    if let Err(e) = res {
        writeln!(&mut io::stderr(), "An error occured:\n{}", e).ok();
        ::std::process::exit(101);
    }
}

// Build command implementation
fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
    let book = build_mdbook_struct(args);

    let mut book = match args.value_of("dest-dir") {
        Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
        None => book
    };

    try!(book.build());

    Ok(())
}

fn build_mdbook_struct(args: &ArgMatches) -> mdbook::MDBook {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::new(&book_dir).read_config();

    // By default mdbook will attempt to create non-existent files referenced
    // from SUMMARY.md files. This is problematic on CI where we mount the
    // source directory as readonly. To avoid any issues, we'll disabled
    // mdbook's implicit file creation feature.
    book.create_missing = false;

    book
}

fn get_book_dir(args: &ArgMatches) -> PathBuf {
    if let Some(dir) = args.value_of("dir") {
        // Check if path is relative from current dir, or absolute...
        let p = Path::new(dir);
        if p.is_relative() {
            env::current_dir().unwrap().join(dir)
        } else {
            p.to_path_buf()
        }
    } else {
        env::current_dir().unwrap()
    }
}
<|endoftext|>


<commit_before>extern crate mp4parse;

use std::env;
use std::fs::File;
use std::io::BufReader;

extern crate byteorder;
use byteorder::Error;

fn dump_file(filename: &String) {
    let file = File::open(filename).unwrap();
    let mut reader = BufReader::new(file);
    let mut context = mp4parse::MediaContext::new();
    loop {
        match mp4parse::read_box(&mut reader, &mut context) {
            Ok(_) => {},
            Err(Error::UnexpectedEOF) => { break },
            Err(e) => { panic!(e); },
        }
    }
    println!("-- result of parsing '{}' --\n{}", filename, context);
}

fn main() {
    for filename in env::args().skip(1) {
        println!("-- dump of '{}' --", filename);
        dump_file(&filename);
        println!("-- end of '{}' --", filename);
    }
}
<commit_msg>Update dump.rs to use #[derive(Debug)] box formatters.<commit_after>extern crate mp4parse;

use std::env;
use std::fs::File;
use std::io::BufReader;

extern crate byteorder;
use byteorder::Error;

fn dump_file(filename: &String) {
    let file = File::open(filename).unwrap();
    let mut reader = BufReader::new(file);
    let mut context = mp4parse::MediaContext::new();
    loop {
        match mp4parse::read_box(&mut reader, &mut context) {
            Ok(_) => {},
            Err(Error::UnexpectedEOF) => { break },
            Err(e) => { panic!(e); },
        }
    }
    println!("-- result of parsing '{}' --\n{:?}", filename, context);
}

fn main() {
    for filename in env::args().skip(1) {
        println!("-- dump of '{}' --", filename);
        dump_file(&filename);
        println!("-- end of '{}' --", filename);
    }
}
<|endoftext|>


<commit_before>use crate::{build::arg::debug_asserts::assert_arg, App, AppSettings, ArgSettings, ValueHint};
use std::cmp::Ordering;

#[derive(Eq)]
enum Flag<'a> {
    App(String, &'a str),
    Arg(String, &'a str),
}

impl PartialEq for Flag<'_> {
    fn eq(&self, other: &Flag) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}

impl PartialOrd for Flag<'_> {
    fn partial_cmp(&self, other: &Flag) -> Option<Ordering> {
        use Flag::*;

        match (self, other) {
            (App(s1, _), App(s2, _))
            | (Arg(s1, _), Arg(s2, _))
            | (App(s1, _), Arg(s2, _))
            | (Arg(s1, _), App(s2, _)) => {
                if s1 == s2 {
                    Some(Ordering::Equal)
                } else {
                    s1.partial_cmp(s2)
                }
            }
        }
    }
}

impl Ord for Flag<'_> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

pub(crate) fn assert_app(app: &App) {
    debug!("App::_debug_asserts");

    let mut short_flags = vec![];
    let mut long_flags = vec![];

    for sc in &app.subcommands {
        if let Some(s) = sc.short_flag.as_ref() {
            short_flags.push(Flag::App(format!("-{}", s), &sc.name));
        }

        for (short_alias, _) in &sc.short_flag_aliases {
            short_flags.push(Flag::App(format!("-{}", short_alias), &sc.name));
        }

        if let Some(l) = sc.long_flag.as_ref() {
            long_flags.push(Flag::App(format!("--{}", l), &sc.name));
        }

        for (long_alias, _) in &sc.long_flag_aliases {
            long_flags.push(Flag::App(format!("--{}", long_alias), &sc.name));
        }
    }

    for arg in app.args.args() {
        assert_arg(arg);

        if let Some(s) = arg.short.as_ref() {
            short_flags.push(Flag::Arg(format!("-{}", s), &*arg.name));
        }

        for (short_alias, _) in &arg.short_aliases {
            short_flags.push(Flag::Arg(format!("-{}", short_alias), arg.name));
        }

        if let Some(l) = arg.long.as_ref() {
            long_flags.push(Flag::Arg(format!("--{}", l), &*arg.name));
        }

        for (long_alias, _) in &arg.aliases {
            long_flags.push(Flag::Arg(format!("--{}", long_alias), arg.name));
        }

        // Name conflicts
        assert!(
            app.two_args_of(|x| x.id == arg.id).is_none(),
            "Argument names must be unique, but '{}' is in use by more than one argument or group",
            arg.name,
        );

        // Long conflicts
        if let Some(l) = arg.long {
            if let Some((first, second)) = app.two_args_of(|x| x.long == Some(l)) {
                panic!(
                    "Long option names must be unique for each argument, \
                        but '--{}' is in use by both '{}' and '{}'",
                    l, first.name, second.name
                )
            }
        }

        // Short conflicts
        if let Some(s) = arg.short {
            if let Some((first, second)) = app.two_args_of(|x| x.short == Some(s)) {
                panic!(
                    "Short option names must be unique for each argument, \
                        but '-{}' is in use by both '{}' and '{}'",
                    s, first.name, second.name
                )
            }
        }

        // Index conflicts
        if let Some(idx) = arg.index {
            if let Some((first, second)) =
                app.two_args_of(|x| x.is_positional() && x.index == Some(idx))
            {
                panic!(
                    "Argument '{}' has the same index as '{}' \
                    and they are both positional arguments\n\n\t \
                    Use Arg::multiple_values(true) to allow one \
                    positional argument to take multiple values",
                    first.name, second.name
                )
            }
        }

        // requires, r_if, r_unless
        for req in &arg.requires {
            assert!(
                app.id_exists(&req.1),
                "Argument or group '{:?}' specified in 'requires*' for '{}' does not exist",
                req.1,
                arg.name,
            );
        }

        for req in &arg.r_ifs {
            assert!(
                app.id_exists(&req.0),
                "Argument or group '{:?}' specified in 'required_if_eq*' for '{}' does not exist",
                req.0,
                arg.name
            );
        }

        for req in &arg.r_ifs_all {
            assert!(
                app.id_exists(&req.0),
                "Argument or group '{:?}' specified in 'required_if_eq_all' for '{}' does not exist",
                req.0,
                arg.name
            );
        }

        for req in &arg.r_unless {
            assert!(
                app.id_exists(req),
                "Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist",
                req,
                arg.name,
            );
        }

        // blacklist
        for req in &arg.blacklist {
            assert!(
                app.id_exists(req),
                "Argument or group '{:?}' specified in 'conflicts_with*' for '{}' does not exist",
                req,
                arg.name,
            );
        }

        if arg.is_set(ArgSettings::Last) {
            assert!(
                arg.long.is_none(),
                "Flags or Options cannot have last(true) set. '{}' has both a long and last(true) set.",
                arg.name
            );
            assert!(
                arg.short.is_none(),
                "Flags or Options cannot have last(true) set. '{}' has both a short and last(true) set.",
                arg.name
            );
        }

        assert!(
            !(arg.is_set(ArgSettings::Required) && arg.global),
            "Global arguments cannot be required.\n\n\t'{}' is marked as both global and required",
            arg.name
        );

        // validators
        assert!(
            arg.validator.is_none() || arg.validator_os.is_none(),
            "Argument '{}' has both `validator` and `validator_os` set which is not allowed",
            arg.name
        );

        if arg.value_hint == ValueHint::CommandWithArguments {
            assert!(
                arg.is_positional(),
                "Argument '{}' has hint CommandWithArguments and must be positional.",
                arg.name
            );

            assert!(
                app.is_set(AppSettings::TrailingVarArg),
                "Positional argument '{}' has hint CommandWithArguments, so App must have TrailingVarArg set.",
                arg.name
            );
        }
    }

    for group in &app.groups {
        // Name conflicts
        assert!(
            app.groups.iter().filter(|x| x.id == group.id).count() < 2,
            "Argument group name must be unique\n\n\t'{}' is already in use",
            group.name,
        );

        // Groups should not have naming conflicts with Args
        assert!(
            !app.args.args().any(|x| x.id == group.id),
            "Argument group name '{}' must not conflict with argument name",
            group.name,
        );

        for arg in &group.args {
            // Args listed inside groups should exist
            assert!(
                app.args.args().any(|x| x.id == *arg),
                "Argument group '{}' contains non-existent argument '{:?}'",
                group.name,
                arg
            );

            // Required groups shouldn't have args with default values
            if group.required {
                assert!(
                    app.args
                        .args ()
                        .any(|x| x.id == *arg && x.default_vals.is_empty()),
                    "Argument group '{}' is required but contains argument '{:?}' which has a default value.",
                    group.name,
                    arg
                )
            }
        }
    }

    // Conflicts between flags and subcommands

    long_flags.sort_unstable();
    short_flags.sort_unstable();

    detect_duplicate_flags(&long_flags, "long");
    detect_duplicate_flags(&short_flags, "short");

    app._panic_on_missing_help(app.g_settings.is_set(AppSettings::HelpRequired));
    assert_app_flags(app);
}

fn detect_duplicate_flags(flags: &[Flag], short_or_long: &str) {
    use Flag::*;

    for (one, two) in find_duplicates(flags) {
        match (one, two) {
            (App(flag, one), App(_, another)) if one != another => panic!(
                "the '{}' {} flag is specified for both '{}' and '{}' subcommands",
                flag, short_or_long, one, another
            ),

            (Arg(flag, one), Arg(_, another)) if one != another => panic!(
                "{} option names must be unique, but '{}' is in use by both '{}' and '{}'",
                short_or_long, flag, one, another
            ),

            (Arg(flag, arg), App(_, sub)) | (App(flag, sub), Arg(_, arg)) => panic!(
                "the '{}' {} flag for the '{}' argument conflicts with the short flag \
                     for '{}' subcommand",
                flag, short_or_long, arg, sub
            ),

            _ => {}
        }
    }
}

/// Find duplicates in a sorted array.
///
/// The algorithm is simple: the array is sorted, duplicates
/// must be placed next to each other, we can check only adjacent elements.
fn find_duplicates<T: PartialEq>(slice: &[T]) -> impl Iterator<Item = (&T, &T)> {
    slice.windows(2).filter_map(|w| {
        if w[0] == w[1] {
            Some((&w[0], &w[1]))
        } else {
            None
        }
    })
}

fn assert_app_flags(app: &App) {
    use AppSettings::*;

    macro_rules! checker {
        ($a:ident requires $($b:ident)|+) => {
            if app.is_set($a) {
                let mut s = String::new();

                $(
                    if !app.is_set($b) {
                        s.push_str(&format!("\nAppSettings::{} is required when AppSettings::{} is set.\n", std::stringify!($b), std::stringify!($a)));
                    }
                )+

                if !s.is_empty() {
                    panic!("{}", s)
                }
            }
        }
    }

    checker!(AllowInvalidUtf8ForExternalSubcommands requires AllowExternalSubcommands);
}
<commit_msg>imp: adds debug_asserts against generating meaningless flags<commit_after>use crate::{
    build::arg::{debug_asserts::assert_arg, ArgProvider},
    util::Id,
    App, AppSettings, ArgSettings, ValueHint,
};
use std::cmp::Ordering;

#[derive(Eq)]
enum Flag<'a> {
    App(String, &'a str),
    Arg(String, &'a str),
}

impl PartialEq for Flag<'_> {
    fn eq(&self, other: &Flag) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}

impl PartialOrd for Flag<'_> {
    fn partial_cmp(&self, other: &Flag) -> Option<Ordering> {
        use Flag::*;

        match (self, other) {
            (App(s1, _), App(s2, _))
            | (Arg(s1, _), Arg(s2, _))
            | (App(s1, _), Arg(s2, _))
            | (Arg(s1, _), App(s2, _)) => {
                if s1 == s2 {
                    Some(Ordering::Equal)
                } else {
                    s1.partial_cmp(s2)
                }
            }
        }
    }
}

impl Ord for Flag<'_> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

pub(crate) fn assert_app(app: &App) {
    debug!("App::_debug_asserts");

    let mut short_flags = vec![];
    let mut long_flags = vec![];

    // Invalid version flag settings
    if app.version.is_none() && app.long_version.is_none() {
        // PropagateVersion is meaningless if there is no version
        assert!(
            !app.settings.is_set(AppSettings::PropagateVersion),
            "No version information via App::version or App::long_version to propagate"
        );

        // Used `App::mut_arg("version", ..) but did not provide any version information to display
        let has_mutated_version = app
            .args
            .args()
            .any(|x| x.id == Id::version_hash() && x.provider == ArgProvider::GeneratedMutated);

        if has_mutated_version {
            assert!(app.settings.is_set(AppSettings::NoAutoVersion),
                "Used App::mut_arg(\"version\", ..) without providing App::version, App::long_version or using AppSettings::NoAutoVersion");
        }
    }

    for sc in &app.subcommands {
        if let Some(s) = sc.short_flag.as_ref() {
            short_flags.push(Flag::App(format!("-{}", s), &sc.name));
        }

        for (short_alias, _) in &sc.short_flag_aliases {
            short_flags.push(Flag::App(format!("-{}", short_alias), &sc.name));
        }

        if let Some(l) = sc.long_flag.as_ref() {
            long_flags.push(Flag::App(format!("--{}", l), &sc.name));
        }

        for (long_alias, _) in &sc.long_flag_aliases {
            long_flags.push(Flag::App(format!("--{}", long_alias), &sc.name));
        }
    }

    for arg in app.args.args() {
        assert_arg(arg);

        if let Some(s) = arg.short.as_ref() {
            short_flags.push(Flag::Arg(format!("-{}", s), &*arg.name));
        }

        for (short_alias, _) in &arg.short_aliases {
            short_flags.push(Flag::Arg(format!("-{}", short_alias), arg.name));
        }

        if let Some(l) = arg.long.as_ref() {
            long_flags.push(Flag::Arg(format!("--{}", l), &*arg.name));
        }

        for (long_alias, _) in &arg.aliases {
            long_flags.push(Flag::Arg(format!("--{}", long_alias), arg.name));
        }

        // Name conflicts
        assert!(
            app.two_args_of(|x| x.id == arg.id).is_none(),
            "Argument names must be unique, but '{}' is in use by more than one argument or group",
            arg.name,
        );

        // Long conflicts
        if let Some(l) = arg.long {
            if let Some((first, second)) = app.two_args_of(|x| x.long == Some(l)) {
                panic!(
                    "Long option names must be unique for each argument, \
                        but '--{}' is in use by both '{}' and '{}'",
                    l, first.name, second.name
                )
            }
        }

        // Short conflicts
        if let Some(s) = arg.short {
            if let Some((first, second)) = app.two_args_of(|x| x.short == Some(s)) {
                panic!(
                    "Short option names must be unique for each argument, \
                        but '-{}' is in use by both '{}' and '{}'",
                    s, first.name, second.name
                )
            }
        }

        // Index conflicts
        if let Some(idx) = arg.index {
            if let Some((first, second)) =
                app.two_args_of(|x| x.is_positional() && x.index == Some(idx))
            {
                panic!(
                    "Argument '{}' has the same index as '{}' \
                    and they are both positional arguments\n\n\t \
                    Use Arg::multiple_values(true) to allow one \
                    positional argument to take multiple values",
                    first.name, second.name
                )
            }
        }

        // requires, r_if, r_unless
        for req in &arg.requires {
            assert!(
                app.id_exists(&req.1),
                "Argument or group '{:?}' specified in 'requires*' for '{}' does not exist",
                req.1,
                arg.name,
            );
        }

        for req in &arg.r_ifs {
            assert!(
                app.id_exists(&req.0),
                "Argument or group '{:?}' specified in 'required_if_eq*' for '{}' does not exist",
                req.0,
                arg.name
            );
        }

        for req in &arg.r_ifs_all {
            assert!(
                app.id_exists(&req.0),
                "Argument or group '{:?}' specified in 'required_if_eq_all' for '{}' does not exist",
                req.0,
                arg.name
            );
        }

        for req in &arg.r_unless {
            assert!(
                app.id_exists(req),
                "Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist",
                req,
                arg.name,
            );
        }

        // blacklist
        for req in &arg.blacklist {
            assert!(
                app.id_exists(req),
                "Argument or group '{:?}' specified in 'conflicts_with*' for '{}' does not exist",
                req,
                arg.name,
            );
        }

        if arg.is_set(ArgSettings::Last) {
            assert!(
                arg.long.is_none(),
                "Flags or Options cannot have last(true) set. '{}' has both a long and last(true) set.",
                arg.name
            );
            assert!(
                arg.short.is_none(),
                "Flags or Options cannot have last(true) set. '{}' has both a short and last(true) set.",
                arg.name
            );
        }

        assert!(
            !(arg.is_set(ArgSettings::Required) && arg.global),
            "Global arguments cannot be required.\n\n\t'{}' is marked as both global and required",
            arg.name
        );

        // validators
        assert!(
            arg.validator.is_none() || arg.validator_os.is_none(),
            "Argument '{}' has both `validator` and `validator_os` set which is not allowed",
            arg.name
        );

        if arg.value_hint == ValueHint::CommandWithArguments {
            assert!(
                arg.is_positional(),
                "Argument '{}' has hint CommandWithArguments and must be positional.",
                arg.name
            );

            assert!(
                app.is_set(AppSettings::TrailingVarArg),
                "Positional argument '{}' has hint CommandWithArguments, so App must have TrailingVarArg set.",
                arg.name
            );
        }
    }

    for group in &app.groups {
        // Name conflicts
        assert!(
            app.groups.iter().filter(|x| x.id == group.id).count() < 2,
            "Argument group name must be unique\n\n\t'{}' is already in use",
            group.name,
        );

        // Groups should not have naming conflicts with Args
        assert!(
            !app.args.args().any(|x| x.id == group.id),
            "Argument group name '{}' must not conflict with argument name",
            group.name,
        );

        for arg in &group.args {
            // Args listed inside groups should exist
            assert!(
                app.args.args().any(|x| x.id == *arg),
                "Argument group '{}' contains non-existent argument '{:?}'",
                group.name,
                arg
            );

            // Required groups shouldn't have args with default values
            if group.required {
                assert!(
                    app.args
                        .args ()
                        .any(|x| x.id == *arg && x.default_vals.is_empty()),
                    "Argument group '{}' is required but contains argument '{:?}' which has a default value.",
                    group.name,
                    arg
                )
            }
        }
    }

    // Conflicts between flags and subcommands

    long_flags.sort_unstable();
    short_flags.sort_unstable();

    detect_duplicate_flags(&long_flags, "long");
    detect_duplicate_flags(&short_flags, "short");

    app._panic_on_missing_help(app.g_settings.is_set(AppSettings::HelpRequired));
    assert_app_flags(app);
}

fn detect_duplicate_flags(flags: &[Flag], short_or_long: &str) {
    use Flag::*;

    for (one, two) in find_duplicates(flags) {
        match (one, two) {
            (App(flag, one), App(_, another)) if one != another => panic!(
                "the '{}' {} flag is specified for both '{}' and '{}' subcommands",
                flag, short_or_long, one, another
            ),

            (Arg(flag, one), Arg(_, another)) if one != another => panic!(
                "{} option names must be unique, but '{}' is in use by both '{}' and '{}'",
                short_or_long, flag, one, another
            ),

            (Arg(flag, arg), App(_, sub)) | (App(flag, sub), Arg(_, arg)) => panic!(
                "the '{}' {} flag for the '{}' argument conflicts with the short flag \
                     for '{}' subcommand",
                flag, short_or_long, arg, sub
            ),

            _ => {}
        }
    }
}

/// Find duplicates in a sorted array.
///
/// The algorithm is simple: the array is sorted, duplicates
/// must be placed next to each other, we can check only adjacent elements.
fn find_duplicates<T: PartialEq>(slice: &[T]) -> impl Iterator<Item = (&T, &T)> {
    slice.windows(2).filter_map(|w| {
        if w[0] == w[1] {
            Some((&w[0], &w[1]))
        } else {
            None
        }
    })
}

fn assert_app_flags(app: &App) {
    use AppSettings::*;

    macro_rules! checker {
        ($a:ident requires $($b:ident)|+) => {
            if app.is_set($a) {
                let mut s = String::new();

                $(
                    if !app.is_set($b) {
                        s.push_str(&format!("\nAppSettings::{} is required when AppSettings::{} is set.\n", std::stringify!($b), std::stringify!($a)));
                    }
                )+

                if !s.is_empty() {
                    panic!("{}", s)
                }
            }
        }
    }

    checker!(AllowInvalidUtf8ForExternalSubcommands requires AllowExternalSubcommands);
}
<|endoftext|>


<commit_before>use super::{ExecutionStrategy,
            Scope};
use crate::{command::pkg::list,
            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_loaded: bool)
                          -> Result<()>
    where U: UIWriter
{
    uninstall_many(ui,
                   &[ident],
                   fs_root_path,
                   execution_strategy,
                   scope,
                   excludes,
                   even_if_loaded).await
}

/// Uninstall all but the `number_latest_to_keep` packages.
///
/// Returns the number of packages that were uninstalled
#[allow(clippy::too_many_arguments)]
pub async fn uninstall_all_but_latest<U>(ui: &mut U,
                                         ident: impl AsRef<PackageIdent>,
                                         number_latest_to_keep: usize,
                                         fs_root_path: &Path,
                                         execution_strategy: ExecutionStrategy,
                                         scope: Scope,
                                         excludes: &[PackageIdent],
                                         even_if_loaded: bool)
                                         -> Result<usize>
    where U: UIWriter
{
    let ident = ident.as_ref().clone();
    let mut idents = list::package_list(&ident.into())?;
    if number_latest_to_keep >= idents.len() {
        return Ok(0);
    }
    // Reverse sort the idents so the latest occur first in the list
    idents.sort_unstable_by(|a, b| b.by_parts_cmp(a));
    let to_uninstall = &idents[number_latest_to_keep..];
    uninstall_many(ui,
                   to_uninstall,
                   fs_root_path,
                   execution_strategy,
                   scope,
                   excludes,
                   even_if_loaded).await?;
    Ok(to_uninstall.len())
}

/// 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_loaded` indictates that the package should be uninstalled even if it is loaded by the
/// supervisor. This only applies to packages specified in `idents`. It does not apply to their
/// dependencies.
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_loaded: bool)
                               -> Result<()>
    where U: UIWriter
{
    // 1.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    let loaded_services = supervisor_services().await?;
    if !loaded_services.is_empty() {
        ui.status(Status::Determining, "list of loaded services in supervisor")?;
        for s in loaded_services.iter() {
            ui.status(Status::Found, format!("loaded service {}", s))?;
        }
    }
    let uninstall_if_loaded = if even_if_loaded {
        UninstallMode::Force
    } else {
        UninstallMode::SkipIfLoaded(&loaded_services)
    };
    // Never uninstall a dependency if it is loaded
    let dependency_uninstall_if_loaded = UninstallMode::SkipIfLoaded(&loaded_services);

    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,
                             uninstall_if_loaded)?;
                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,
                                         dependency_uninstall_if_loaded)?;

                            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)
}

#[derive(Clone, Copy)]
enum UninstallMode<'a> {
    Force,
    SkipIfLoaded(&'a [PackageIdent]),
}

impl UninstallMode<'_> {
    fn should_skip(&self, ident: &PackageIdent) -> bool {
        if let Self::SkipIfLoaded(services) = self {
            services.iter().any(|i| i.satisfies(ident))
        } else {
            false
        }
    }
}

/// 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],
                   uninstall_if_loaded: UninstallMode)
                   -> 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);
    }

    if uninstall_if_loaded.should_skip(ident) {
        ui.status(Status::Skipping,
                  format!("{}. It is currently loaded by 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 message when no packages need to be uninstalled<commit_after>use super::{ExecutionStrategy,
            Scope};
use crate::{command::pkg::list,
            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_loaded: bool)
                          -> Result<()>
    where U: UIWriter
{
    uninstall_many(ui,
                   &[ident],
                   fs_root_path,
                   execution_strategy,
                   scope,
                   excludes,
                   even_if_loaded).await
}

/// Uninstall all but the `number_latest_to_keep` packages.
///
/// Returns the number of packages that were uninstalled
#[allow(clippy::too_many_arguments)]
pub async fn uninstall_all_but_latest<U>(ui: &mut U,
                                         ident: impl AsRef<PackageIdent>,
                                         number_latest_to_keep: usize,
                                         fs_root_path: &Path,
                                         execution_strategy: ExecutionStrategy,
                                         scope: Scope,
                                         excludes: &[PackageIdent],
                                         even_if_loaded: bool)
                                         -> Result<usize>
    where U: UIWriter
{
    let ident = ident.as_ref();
    let mut idents = list::package_list(&ident.clone().into())?;
    if number_latest_to_keep >= idents.len() {
        ui.begin(format!("Uninstalling {}", ident))?;
        ui.status(Status::Skipping,
                  format!("Only {} packages installed", idents.len()))?;
        ui.end(format!("Uninstall of {} complete", ident))?;
        return Ok(0);
    }
    // Reverse sort the idents so the latest occur first in the list
    idents.sort_unstable_by(|a, b| b.by_parts_cmp(a));
    let to_uninstall = &idents[number_latest_to_keep..];
    uninstall_many(ui,
                   to_uninstall,
                   fs_root_path,
                   execution_strategy,
                   scope,
                   excludes,
                   even_if_loaded).await?;
    Ok(to_uninstall.len())
}

/// 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_loaded` indictates that the package should be uninstalled even if it is loaded by the
/// supervisor. This only applies to packages specified in `idents`. It does not apply to their
/// dependencies.
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_loaded: bool)
                               -> Result<()>
    where U: UIWriter
{
    // 1.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    let loaded_services = supervisor_services().await?;
    if !loaded_services.is_empty() {
        ui.status(Status::Determining, "list of loaded services in supervisor")?;
        for s in loaded_services.iter() {
            ui.status(Status::Found, format!("loaded service {}", s))?;
        }
    }
    let uninstall_if_loaded = if even_if_loaded {
        UninstallMode::Force
    } else {
        UninstallMode::SkipIfLoaded(&loaded_services)
    };
    // Never uninstall a dependency if it is loaded
    let dependency_uninstall_if_loaded = UninstallMode::SkipIfLoaded(&loaded_services);

    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,
                             uninstall_if_loaded)?;
                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,
                                         dependency_uninstall_if_loaded)?;

                            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)
}

#[derive(Clone, Copy)]
enum UninstallMode<'a> {
    Force,
    SkipIfLoaded(&'a [PackageIdent]),
}

impl UninstallMode<'_> {
    fn should_skip(&self, ident: &PackageIdent) -> bool {
        if let Self::SkipIfLoaded(services) = self {
            services.iter().any(|i| i.satisfies(ident))
        } else {
            false
        }
    }
}

/// 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],
                   uninstall_if_loaded: UninstallMode)
                   -> 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);
    }

    if uninstall_if_loaded.should_skip(ident) {
        ui.status(Status::Skipping,
                  format!("{}. It is currently loaded by 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|>


<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

// Non-standard properties that Gecko uses for XUL elements.
<% data.new_style_struct("XUL", inherited=False) %>

${helpers.single_keyword("-moz-box-align", "stretch start center baseline end",
                         products="gecko", gecko_ffi_name="mBoxAlign",
                         gecko_enum_prefix="StyleBoxAlign",
                         gecko_inexhaustive=True,
                         animation_type="none",
                         alias="-webkit-box-align",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)")}

${helpers.single_keyword("-moz-box-direction", "normal reverse",
                         products="gecko", gecko_ffi_name="mBoxDirection",
                         gecko_enum_prefix="StyleBoxDirection",
                         animation_type="none",
                         alias="-webkit-box-direction",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)")}

${helpers.predefined_type("-moz-box-flex", "Number", "0.0", "parse_non_negative",
                          products="gecko", gecko_ffi_name="mBoxFlex",
                          animation_type="none",
                          alias="-webkit-box-flex",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)")}

${helpers.single_keyword("-moz-box-orient", "horizontal vertical",
                         products="gecko", gecko_ffi_name="mBoxOrient",
                         gecko_enum_prefix="StyleBoxOrient",
                         animation_type="none",
                         alias="-webkit-box-orient",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)")}

${helpers.single_keyword("-moz-box-pack", "start center end justify",
                         products="gecko", gecko_ffi_name="mBoxPack",
                         gecko_enum_prefix="StyleBoxPack",
                         animation_type="none",
                         alias="-webkit-box-pack",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)")}

${helpers.single_keyword("-moz-stack-sizing", "stretch-to-fit ignore",
                         products="gecko", gecko_ffi_name="mStretchStack",
                         gecko_constant_prefix="NS_STYLE_STACK_SIZING",
                         animation_type="none",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)")}

${helpers.predefined_type("-moz-box-ordinal-group", "Integer", "0",
                          parse_method="parse_non_negative",
                          products="gecko",
                          alias="-webkit-box-ordinal-group",
                          gecko_ffi_name="mBoxOrdinal",
                          animation_type="none",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)")}
<commit_msg>Auto merge of #16537 - KuoE0:add-inline-axis-and-block-axis-for-moz-box-orient, r=KuoE0<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

// Non-standard properties that Gecko uses for XUL elements.
<% data.new_style_struct("XUL", inherited=False) %>

${helpers.single_keyword("-moz-box-align", "stretch start center baseline end",
                         products="gecko", gecko_ffi_name="mBoxAlign",
                         gecko_enum_prefix="StyleBoxAlign",
                         gecko_inexhaustive=True,
                         animation_type="none",
                         alias="-webkit-box-align",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)")}

${helpers.single_keyword("-moz-box-direction", "normal reverse",
                         products="gecko", gecko_ffi_name="mBoxDirection",
                         gecko_enum_prefix="StyleBoxDirection",
                         animation_type="none",
                         alias="-webkit-box-direction",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)")}

${helpers.predefined_type("-moz-box-flex", "Number", "0.0", "parse_non_negative",
                          products="gecko", gecko_ffi_name="mBoxFlex",
                          animation_type="none",
                          alias="-webkit-box-flex",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)")}

${helpers.single_keyword("-moz-box-orient", "horizontal vertical",
                         products="gecko", gecko_ffi_name="mBoxOrient",
                         extra_gecko_aliases="inline-axis=horizontal block-axis=vertical",
                         gecko_enum_prefix="StyleBoxOrient",
                         animation_type="none",
                         alias="-webkit-box-orient",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)")}

${helpers.single_keyword("-moz-box-pack", "start center end justify",
                         products="gecko", gecko_ffi_name="mBoxPack",
                         gecko_enum_prefix="StyleBoxPack",
                         animation_type="none",
                         alias="-webkit-box-pack",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)")}

${helpers.single_keyword("-moz-stack-sizing", "stretch-to-fit ignore",
                         products="gecko", gecko_ffi_name="mStretchStack",
                         gecko_constant_prefix="NS_STYLE_STACK_SIZING",
                         animation_type="none",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)")}

${helpers.predefined_type("-moz-box-ordinal-group", "Integer", "0",
                          parse_method="parse_non_negative",
                          products="gecko",
                          alias="-webkit-box-ordinal-group",
                          gecko_ffi_name="mBoxOrdinal",
                          animation_type="none",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)")}
<|endoftext|>