diff --git a/Cargo.lock b/Cargo.lock index 07bec3b..e570a51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,7 +179,9 @@ dependencies = [ "directories", "indicatif", "mimalloc", + "phf", "serde", + "thiserror", "toml", ] @@ -332,6 +334,48 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "portable-atomic" version = "1.4.3" @@ -356,6 +400,21 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + [[package]] name = "redox_syscall" version = "0.2.16" @@ -433,6 +492,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "strsim" version = "0.10.0" @@ -441,9 +506,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.35" +version = "2.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59bf04c28bee9043ed9ea1e41afc0552288d3aba9c6efdd78903b802926f4879" +checksum = "91e02e55d62894af2a08aca894c6577281f76769ba47c94d5756bec8ac6e7373" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index ca2ab3e..7ebe922 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,9 @@ dialoguer = "0.10.4" directories = "5.0.1" indicatif = "0.17.6" mimalloc = { version = "0.1.39", optional = true } +phf = { version = "0.11.2", features = ["macros"] } serde = { version = "1.0.188", features = ["derive"] } +thiserror = "1.0.48" toml = { version = "0.8.0", features = ["display", "parse"] } [features] diff --git a/justfile b/justfile index 1f5f0a7..03b5240 100644 --- a/justfile +++ b/justfile @@ -51,4 +51,4 @@ msrv: --mount type=bind,source=$HOME/.cargo/registry,target=/usr/local/cargo/registry \ -w /project \ rust:latest \ - bash -c 'cargo install cargo-msrv --version 0.16.0-beta.14 --profile=dev && cargo msrv -- cargo check --verbose --locked' + bash -c 'cargo install cargo-msrv --version 0.15.1 --profile=dev && cargo msrv -- cargo check --verbose --locked' diff --git a/src/cli.rs b/src/cli.rs index 1ed82b0..2412010 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,91 +1,25 @@ use clap::{Parser, Subcommand}; -use std::ffi::OsString; #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] #[command(propagate_version = true)] pub struct Args { #[command(subcommand)] - subcommand: ArgCommand, + pub subcommand: ArgCommand, + // TODO: Top level config stuff, like --config=$PATH } #[derive(Debug, Subcommand)] pub enum ArgCommand { - /// Deals with virtual system bases - Img { - #[command(subcommand)] - subcommand: ArgImgCommand, - }, - /// List virtual systems and their statuses - List, - /// Runs a virtual system - Create { - /// Temporary system that must be removed on exit. - /// Can use multiple of these at a time. - #[arg(short, long)] - temp: bool, - /// Do not remove on exit - #[arg(short, long)] - no_remove: bool, - /// Do not attach STDIO - #[arg(short, long)] - detach: bool, - /// Name to be given to the virtual system - name: String, - /// Base of the virtual system - base: String, - /// Command to exec, otherwise base command will be used - #[arg(last = true)] - cmd: Option, - }, - /// Runs a command in the virtual system - Exec { - /// Do not attach STDIO - #[arg(short, long)] - detach: bool, - /// Name of the virtual system - name: String, - /// Command to exec, otherwise base command will be used - #[arg(last = true)] - cmd: Option, - }, - /// Start a virtual system - Start { - /// Name of the virtual system - name: String, - }, - /// Stop a virtual system - Stop { - /// Name of the virtual system - name: String, - }, -} - -#[derive(Debug, Subcommand)] -pub enum ArgImgCommand { - /// Create a new image + /// (TODO) Persist Create, - /// Download an image definition from a url - Download, - /// Edit an image - Edit, // TODO: This should allow people to open stuff like the config and Dockerfile in VSCODE - /// Bundle an image for use in - Bundle, - /// List images - List, - /// Get info about an image - Info, - /// (Re)Builds base image, removing the old image and any containers that use it - Build { - /// Do not rebuild image if it already exists - #[arg(short, long)] - no_rebuild: bool, - /// Use build cache if possible - #[arg(short, long)] - use_cache: bool, - /// TODO - base_name: String, + /// (TODO) Temp + Run { + /// Id of the dvs + id: String, }, + /// (TODO) Edit and manage dvs' + Img, } #[test] diff --git a/src/driver/docker.rs b/src/driver/docker.rs index 8b13789..c9ea169 100644 --- a/src/driver/docker.rs +++ b/src/driver/docker.rs @@ -1 +1,14 @@ +use crate::driver::Driver; +pub struct DockerDriver {} +impl DockerDriver { + pub fn new() -> Self { + Self {} + } +} +impl Driver for DockerDriver { + // TODO: Support --platform + fn pull_image(&self, image: &str) -> String { + format!("docker pull {image}") + } +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs index e44e69e..2b07dd6 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1,2 +1,15 @@ +use crate::driver::docker::DockerDriver; + mod docker; mod podman; + +pub trait Driver { + fn pull_image(&self, image: &str) -> String; +} + +pub fn get_bundled(item: &str) -> Option> { + match item { + "bundled-docker" => Some(Box::new(DockerDriver::new())), + _ => None, + } +} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..69040c8 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,8 @@ +use thiserror::Error; + +#[allow(dead_code)] // TODO: Remove +#[derive(Debug, Error)] +pub enum CommandError { + #[error("The child process exited with code `{0}`")] + ExitCode(i32), +} diff --git a/src/image/bundled/rust.toml b/src/image/bundled/rust.toml new file mode 100644 index 0000000..6341084 --- /dev/null +++ b/src/image/bundled/rust.toml @@ -0,0 +1,30 @@ +[info] +id = "rust" # dvs id + +[container] +workspace = "/project" # -w +mount_pwd = "/project" # --mount type=bind,source="$(pwd)",target=/project +mounts = ["$HOME/.cargo/registry:/usr/local/cargo/registry"] # --mount type=bind,source=$HOME/.cargo/registry,target=/usr/local/cargo/registry +env = ["CARGO_TARGET_DIR=/target"] # -e CARGO_TARGET_DIR=/target + +[image] +default = "latest" +versions = [ + "latest", + "alpine", + "bookworm", + "bullseye", + "buster", + "slim", + "slim-bookworm", + "slim-bullseye", + "slim-buster", +] +pull = "rust" # Applies to all image unless overridden +pull_updates = true +entrypoint = "/bin/sh" +cmd = "-c /bin/bash" + +[image.alpine] +entrypoint = "/bin/sh" +cmd = "-c /bin/sh" diff --git a/src/image/mod.rs b/src/image/mod.rs new file mode 100644 index 0000000..3d20813 --- /dev/null +++ b/src/image/mod.rs @@ -0,0 +1,9 @@ +use phf::phf_map; + +static BUNDLED_MAP: phf::Map<&'static str, &'static str> = phf_map! { + "rust" => include_str!("./bundled/rust.toml") +}; + +pub fn get_bundled(item: &str) -> Option<&&'static str> { + BUNDLED_MAP.get(item) +} diff --git a/src/main.rs b/src/main.rs index 7c5fdd7..029f2d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,74 @@ +use crate::cli::ArgCommand; use clap::Parser; +use std::{ffi::OsString, process::Command}; +use toml::Table; mod cli; mod driver; +mod errors; +mod image; + +#[cfg(target_family = "wasm")] +compile_error!("Wasm is not a supported target!"); fn main() { - let _args = cli::Args::parse(); + let args = cli::Args::parse(); #[cfg(debug_assertions)] - dbg!(_args); + dbg!(&args); + + // TODO: Get config here! + let driver = driver::get_bundled("bundled-docker").unwrap(); + + match &args.subcommand { + ArgCommand::Run { id } => { + let image = image::get_bundled(id).unwrap(); + let image = image.parse::().unwrap(); + + let pull_img = image["image"]["pull"].as_str().unwrap(); + let pull_img_default = image["image"]["default"].as_str().unwrap(); + let pull_img = if !pull_img.contains(':') { + format!("{pull_img}:{pull_img_default}") + } + else { + pull_img.to_string() + }; + + dbg!(&pull_img); + + let cmd = driver.pull_image(&pull_img); + run_cmd(&cmd, None).expect("FFF"); + } + _ => todo!(), + } +} + +fn run_cmd(cmd: &str, end: Option) -> Result<(), String> { + #[cfg(target_family = "unix")] + let mut command = Command::new("sh"); + #[cfg(target_family = "unix")] + command.arg("-c"); + + #[cfg(target_family = "windows")] + let mut command = Command::new("cmd"); + #[cfg(target_family = "windows")] + command.arg("/C"); + + command.arg(cmd); + if let Some(str) = end { + command.arg(str); + } + + dbg!(&command); + + // let out = command.status().expect("exec failed"); + + let child = command.spawn().expect("Could not spawn child process."); + let _out = child.wait_with_output(); + + // match command.spawn().and_then(|mut child| child.wait()) { + // Ok(status) => std::process::exit(status.code().unwrap_or(1)), + // Err(error) => die!("fatal: {}", error), + // }; - println!("Hello, world!"); + Ok(()) }