cubic 0.14.0

Cubic is a lightweight command line manager for virtual machines. It has a simple, daemon-less and rootless design. All Cubic virtual machines run isolated in the user context. Cubic is built on top of QEMU, KVM and cloud-init. Show all supported images: $ cubic images Create a new virtual machine instance: $ cubic create mymachine --image ubuntu:noble List all virtual machine instances: $ cubic instances Start an instance: $ cubic start <instance name> Stop an instance: $ cubic stop <instance name> Open a shell in the instance: $ cubic ssh <machine name> Copy a file from the host to the instance: $ cubic scp <path/to/host/file> <machine>:<path/to/guest/file> Copy a file from the instance to the hots: $ cubic scp <machine>:<path/to/guest/file> <path/to/host/file>
use std::cmp::max;
use std::io::{stdout, Write};
use std::sync::mpsc::{self, TryRecvError};
use std::thread::{self, JoinHandle};
use std::time;

const SPINNER_VIEW_CHARS: &[char] = &['-', '\\', '|', '/'];

pub struct SpinnerView {
    channel: mpsc::Sender<()>,
    thread: Option<JoinHandle<()>>,
}

impl SpinnerView {
    pub fn new(text: &'static str) -> Self {
        let (tx, rx) = mpsc::channel();

        let thread = thread::spawn(move || {
            let mut index = 0;
            let mut line_length = 0;
            let start = time::Instant::now();
            while let Err(TryRecvError::Empty) = rx.try_recv() {
                let text = format!(
                    "{}{}.. {} ({:.1?}s)",
                    if index > 0 { "\r" } else { "" },
                    &text,
                    SPINNER_VIEW_CHARS[index % SPINNER_VIEW_CHARS.len()],
                    start.elapsed().as_secs_f32()
                );

                line_length = max(line_length, text.len());
                print!("{text}");
                stdout().flush().ok();
                index += 1;
                thread::sleep(time::Duration::from_millis(100));
            }
            print!("\r{: ^width$}\r", "", width = line_length);
            stdout().flush().ok();
        });

        SpinnerView {
            channel: tx,
            thread: Some(thread),
        }
    }

    pub fn stop(&mut self) {
        self.channel.send(()).ok();
        self.thread.take().map(|t| t.join());
    }
}