Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add CLI arguments #13

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
17 changes: 9 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion quickemu/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
[package]
name = "quickemu-rs"
version = "2.0.1"
version = "3.0.0"
edition = "2021"
license = "GPL-3.0 OR GPL-2.0"

[dependencies]
clap = { version = "4.5.31", features = ["derive"] }
env_logger = "0.11.6"
log = "0.4.25"
quickemu_core = { path = "../core" }
17 changes: 17 additions & 0 deletions quickemu/cli/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
mod display;
mod io;
mod machine;

use std::path::PathBuf;

use clap::Parser;

#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long, help = "Path to the VM's config file")]
pub vm: PathBuf,
#[clap(flatten)]
pub io: io::IoArgs,
#[clap(flatten)]
pub machine: machine::MachineArgs,
}
134 changes: 134 additions & 0 deletions quickemu/cli/src/args/display.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use std::str::FromStr;

use clap::{Parser, ValueEnum};
use quickemu_core::data::{default_spice_port, Display, DisplayType, Resolution};

#[cfg(not(target_os = "macos"))]
use quickemu_core::data::{Access, Viewer};

#[derive(Debug, Parser)]
pub(crate) struct DisplayArgs {
#[clap(long)]
display: Option<CliDisplayType>,
#[clap(long)]
braille: bool,
#[cfg(not(target_os = "macos"))]
#[clap(long, value_parser = Access::from_str)]
access: Option<Access>,
#[cfg(not(target_os = "macos"))]
#[clap(long)]
spice_port: Option<u16>,
#[cfg(not(target_os = "macos"))]
#[clap(long)]
viewer: Option<CliViewer>,
#[clap(long, value_parser = CliResolution::parse, help = "A resolution ('width'x'height') or 'fullscreen'")]
resolution: Option<Resolution>,
}

struct CliResolution;
impl CliResolution {
fn parse(input: &str) -> Result<Resolution, String> {
if input == "fullscreen" {
Ok(Resolution::FullScreen)
} else {
let (width, height) = input.split_once('x').ok_or("Resolution is not formatted as expected")?;
let width = width.parse().map_err(|e| format!("Invalid width: {e}"))?;
let height = height.parse().map_err(|e| format!("Invalid height: {e}"))?;
Ok(Resolution::Custom { width, height })
}
}
}

impl DisplayArgs {
pub(crate) fn edit_config(self, config: &mut Display) {
if let Some(display) = self.display {
config.display_type = display.into();
}

#[cfg(not(target_os = "macos"))]
if let DisplayType::Spice { access, viewer, spice_port } = &mut config.display_type {
if let Some(selected_access) = self.access {
*access = selected_access;
}
if let Some(selected_viewer) = self.viewer {
*viewer = selected_viewer.into();
}
if let Some(selected_spice_port) = self.spice_port {
*spice_port = selected_spice_port;
}
} else if self.spice_port.is_some() || self.viewer.is_some() || self.access.is_some() {
panic!("Cannot specify Spice-specific options (spice port, access, viewer) when using a non-Spice display type");
}

if self.braille {
config.braille = true;
}
if let Some(resolution) = self.resolution {
config.resolution = resolution;
}
}
}

// #[derive(Debug, Parser)]
// struct Test {
// #[clap(long, value_parser = parse_display)]
// display: DisplayArgs,
// }
//
// pub(crate) fn parse_display(s: &str) -> Result<DisplayArgs, String> {
// let args = DisplayArgs::try_parse_from
//
// Ok(args)
// }

#[derive(Debug, Clone, ValueEnum)]
enum CliDisplayType {
None,
Sdl,
Gtk,
#[cfg(not(target_os = "macos"))]
Spice,
#[cfg(not(target_os = "macos"))]
SpiceApp,
#[cfg(target_os = "macos")]
Cocoa,
}

impl From<CliDisplayType> for DisplayType {
fn from(value: CliDisplayType) -> Self {
match value {
CliDisplayType::None => DisplayType::None,
CliDisplayType::Sdl => DisplayType::Sdl,
CliDisplayType::Gtk => DisplayType::Gtk,
#[cfg(not(target_os = "macos"))]
CliDisplayType::Spice => DisplayType::Spice {
access: Access::default(),
viewer: Viewer::default(),
spice_port: default_spice_port(),
},
#[cfg(not(target_os = "macos"))]
CliDisplayType::SpiceApp => DisplayType::SpiceApp,
#[cfg(target_os = "macos")]
CliDisplayType::Cocoa => DisplayType::Cocoa,
}
}
}

#[cfg(not(target_os = "macos"))]
#[derive(Debug, Clone, ValueEnum)]
enum CliViewer {
None,
Spicy,
Remote,
}

#[cfg(not(target_os = "macos"))]
impl From<CliViewer> for Viewer {
fn from(value: CliViewer) -> Self {
match value {
CliViewer::None => Viewer::None,
CliViewer::Spicy => Viewer::Spicy,
CliViewer::Remote => Viewer::Remote,
}
}
}
Loading
Loading