use std::process::Command;
use crate::{
Tool, execute_command,
serde_utils::{deserialize_string, deserialize_string_vec},
};
use rmcp::ErrorData;
#[derive(Debug, ::serde::Deserialize, schemars::JsonSchema)]
pub struct RustupShowRequest {
#[serde(default)]
verbose: bool,
}
impl RustupShowRequest {
pub fn build_cmd(&self) -> Result<Command, ErrorData> {
let mut cmd = Command::new("rustup");
cmd.arg("show");
if self.verbose {
cmd.arg("--verbose");
}
Ok(cmd)
}
}
pub struct RustupShowRmcpTool;
impl Tool for RustupShowRmcpTool {
const NAME: &'static str = "rustup-show";
const TITLE: &'static str = "Show Rust toolchains";
const DESCRIPTION: &'static str = "Show the active and installed toolchains or profiles. Shows the name of the active toolchain and the version of rustc. If the active toolchain has installed support for additional compilation targets, then they are listed as well.";
type RequestArgs = RustupShowRequest;
fn call_rmcp_tool(&self, request: Self::RequestArgs) -> Result<crate::Response, ErrorData> {
execute_command(request.build_cmd()?, Self::NAME).map(Into::into)
}
}
#[derive(Debug, ::serde::Deserialize, schemars::JsonSchema)]
pub struct RustupToolchainAddRequest {
pub toolchain: String,
#[serde(default, deserialize_with = "deserialize_string")]
pub profile: Option<String>,
#[serde(default, deserialize_with = "deserialize_string_vec")]
pub components: Option<Vec<String>>,
#[serde(default, deserialize_with = "deserialize_string_vec")]
pub targets: Option<Vec<String>>,
#[serde(default)]
pub no_self_update: bool,
#[serde(default)]
pub force: bool,
#[serde(default)]
pub allow_downgrade: bool,
#[serde(default)]
pub force_non_host: bool,
}
impl RustupToolchainAddRequest {
pub fn build_cmd(&self) -> Result<Command, ErrorData> {
let mut cmd = Command::new("rustup");
cmd.arg("toolchain").arg("install").arg(&self.toolchain);
if let Some(profile) = &self.profile {
cmd.arg("--profile").arg(profile);
}
if let Some(components) = &self.components
&& !components.is_empty()
{
cmd.arg("--component").arg(components.join(","));
}
if let Some(targets) = &self.targets
&& !targets.is_empty()
{
cmd.arg("--target").arg(targets.join(","));
}
if self.no_self_update {
cmd.arg("--no-self-update");
}
if self.force {
cmd.arg("--force");
}
if self.allow_downgrade {
cmd.arg("--allow-downgrade");
}
if self.force_non_host {
cmd.arg("--force-non-host");
}
Ok(cmd)
}
}
pub struct RustupToolchainAddRmcpTool;
impl Tool for RustupToolchainAddRmcpTool {
const NAME: &'static str = "rustup-toolchain-add";
const TITLE: &'static str = "Install Rust toolchain";
const DESCRIPTION: &'static str = "Install or update the given toolchains, or by default the active toolchain. Toolchain name can be 'stable', 'nightly', or a specific version like '1.8.0'.";
type RequestArgs = RustupToolchainAddRequest;
fn call_rmcp_tool(&self, request: Self::RequestArgs) -> Result<crate::Response, ErrorData> {
execute_command(request.build_cmd()?, Self::NAME).map(Into::into)
}
}
#[derive(Debug, ::serde::Deserialize, schemars::JsonSchema)]
pub struct RustupUpdateRequest {
#[serde(default, deserialize_with = "deserialize_string")]
pub toolchain: Option<String>,
#[serde(default)]
pub no_self_update: bool,
#[serde(default)]
pub force: bool,
#[serde(default)]
pub force_non_host: bool,
}
impl RustupUpdateRequest {
pub fn build_cmd(&self) -> Result<Command, ErrorData> {
let mut cmd = Command::new("rustup");
cmd.arg("update");
if let Some(toolchain) = &self.toolchain {
cmd.arg(toolchain);
}
if self.no_self_update {
cmd.arg("--no-self-update");
}
if self.force {
cmd.arg("--force");
}
if self.force_non_host {
cmd.arg("--force-non-host");
}
Ok(cmd)
}
}
pub struct RustupUpdateRmcpTool;
impl Tool for RustupUpdateRmcpTool {
const NAME: &'static str = "rustup-update";
const TITLE: &'static str = "Update Rust toolchains";
const DESCRIPTION: &'static str = "Update Rust toolchains and rustup. With no toolchain specified, updates each of the installed toolchains from the official release channels, then updates rustup itself. If given a toolchain argument then updates that toolchain.";
type RequestArgs = RustupUpdateRequest;
fn call_rmcp_tool(&self, request: Self::RequestArgs) -> Result<crate::Response, ErrorData> {
execute_command(request.build_cmd()?, Self::NAME).map(Into::into)
}
}