Skip to content
67 changes: 45 additions & 22 deletions src/cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,21 @@ mod os {
}

use std::borrow::Cow;
use std::env;
use std::env::consts::EXE_SUFFIX;
use std::fs;
use std::io::Write;
use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
use std::process::Command;
use std::str::FromStr;
use std::{env, fmt};

use anyhow::{anyhow, Context, Result};
use cfg_if::cfg_if;
use same_file::Handle;
use serde::{Deserialize, Serialize};

use crate::currentprocess::terminalsource;
use crate::errors::RustupError;
use crate::{
cli::{
common::{self, ignorable_error, report_error, Confirm, PackageUpdate},
Expand Down Expand Up @@ -98,7 +100,8 @@ pub(crate) const NEVER_SELF_UPDATE: bool = true;
#[cfg(not(feature = "no-self-update"))]
pub(crate) const NEVER_SELF_UPDATE: bool = false;

#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum SelfUpdateMode {
Enable,
Disable,
Expand Down Expand Up @@ -1245,29 +1248,49 @@ async fn get_available_rustup_version() -> Result<String> {
let release_file = tempdir.path().join("release-stable.toml");
utils::download_file(&release_file_url, &release_file, None, &|_| ()).await?;
let release_toml_str = utils::read_file("rustup release", &release_file)?;
let release_toml: toml::Value =
toml::from_str(&release_toml_str).context("unable to parse rustup release file")?;

// Check the release file schema.
let schema = release_toml
.get("schema-version")
.ok_or_else(|| anyhow!("no schema key in rustup release file"))?
.as_str()
.ok_or_else(|| anyhow!("invalid schema key in rustup release file"))?;
if schema != "1" {
return Err(anyhow!(format!(
"unknown schema version '{schema}' in rustup release file"
)));
let release_toml = toml::from_str::<RustupManifest>(&release_toml_str)
.context("unable to parse rustup release file")?;

Ok(release_toml.version)
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
struct RustupManifest {
schema_version: SchemaVersion,
version: String,
}

#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) enum SchemaVersion {
#[serde(rename = "1")]
#[default]
V1,
}

impl SchemaVersion {
pub fn as_str(&self) -> &'static str {
match self {
Self::V1 => "1",
}
}
}

// Get the version.
let available_version = release_toml
.get("version")
.ok_or_else(|| anyhow!("no version key in rustup release file"))?
.as_str()
.ok_or_else(|| anyhow!("invalid version key in rustup release file"))?;
impl FromStr for SchemaVersion {
type Err = RustupError;

Ok(String::from(available_version))
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"1" => Ok(Self::V1),
_ => Err(RustupError::UnsupportedVersion(s.to_owned())),
}
}
}

impl fmt::Display for SchemaVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}

pub(crate) async fn check_rustup_update() -> Result<()> {
Expand Down
26 changes: 13 additions & 13 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use serde::Deserialize;
use thiserror::Error as ThisError;
use tokio_stream::StreamExt;

use crate::settings::MetadataVersion;
use crate::{
cli::self_update::SelfUpdateMode,
currentprocess::process,
Expand All @@ -22,7 +23,7 @@ use crate::{
fallback_settings::FallbackSettings,
install::UpdateStatus,
notifications::*,
settings::{Settings, SettingsFile, DEFAULT_METADATA_VERSION},
settings::{Settings, SettingsFile},
toolchain::{
distributable::DistributableToolchain,
names::{
Expand Down Expand Up @@ -459,20 +460,19 @@ impl Cfg {

#[cfg_attr(feature = "otel", tracing::instrument(skip_all))]
pub(crate) fn upgrade_data(&self) -> Result<()> {
let current_version = self.settings_file.with(|s| Ok(s.version.clone()))?;

if current_version == DEFAULT_METADATA_VERSION {
(self.notify_handler)(Notification::MetadataUpgradeNotNeeded(&current_version));
let current_version = self.settings_file.with(|s| Ok(s.version))?;
if current_version == MetadataVersion::default() {
(self.notify_handler)(Notification::MetadataUpgradeNotNeeded(current_version));
return Ok(());
}

(self.notify_handler)(Notification::UpgradingMetadata(
&current_version,
DEFAULT_METADATA_VERSION,
current_version,
MetadataVersion::default(),
));

match &*current_version {
"2" => {
match current_version {
MetadataVersion::V2 => {
// The toolchain installation format changed. Just delete them all.
(self.notify_handler)(Notification::UpgradeRemovesToolchains);

Expand All @@ -490,11 +490,11 @@ impl Cfg {
}

self.settings_file.with_mut(|s| {
DEFAULT_METADATA_VERSION.clone_into(&mut s.version);
s.version = MetadataVersion::default();
Ok(())
})
}
_ => Err(RustupError::UnknownMetadataVersion(current_version).into()),
MetadataVersion::V12 => unreachable!(),
}
}

Expand Down Expand Up @@ -878,8 +878,8 @@ impl Cfg {
utils::assert_is_directory(&self.rustup_dir)?;

self.settings_file.with(|s| {
(self.notify_handler)(Notification::ReadMetadataVersion(&s.version));
if s.version == DEFAULT_METADATA_VERSION {
(self.notify_handler)(Notification::ReadMetadataVersion(s.version));
if s.version == MetadataVersion::default() {
Ok(())
} else {
Err(anyhow!(
Expand Down
95 changes: 32 additions & 63 deletions src/dist/config.rs
Original file line number Diff line number Diff line change
@@ -1,87 +1,56 @@
use anyhow::{bail, Context, Result};
use std::fmt;
use std::str::FromStr;

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};

use super::manifest::Component;
use crate::errors::*;
use crate::utils::toml_utils::*;

pub(crate) const SUPPORTED_CONFIG_VERSIONS: [&str; 1] = ["1"];
pub(crate) const DEFAULT_CONFIG_VERSION: &str = "1";

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Config {
pub config_version: String,
pub config_version: ConfigVersion,
pub components: Vec<Component>,
}

impl Config {
pub(crate) fn from_toml(mut table: toml::value::Table, path: &str) -> Result<Self> {
let config_version = get_string(&mut table, "config_version", path)?;
if !SUPPORTED_CONFIG_VERSIONS.contains(&&*config_version) {
bail!(RustupError::UnsupportedVersion(config_version));
}

let components = get_array(&mut table, "components", path)?;
let components =
Self::toml_to_components(components, &format!("{}{}.", path, "components"))?;

Ok(Self {
config_version,
components,
})
}
pub(crate) fn into_toml(self) -> toml::value::Table {
let mut result = toml::value::Table::new();
result.insert(
"config_version".to_owned(),
toml::Value::String(self.config_version),
);
let components = Self::components_to_toml(self.components);
if !components.is_empty() {
result.insert("components".to_owned(), toml::Value::Array(components));
}
result
}

pub(crate) fn parse(data: &str) -> Result<Self> {
let value = toml::from_str(data).context("error parsing config")?;
Self::from_toml(value, "")
toml::from_str(data).context("error parsing config")
}

pub(crate) fn stringify(self) -> String {
self.into_toml().to_string()
pub(crate) fn stringify(&self) -> Result<String> {
Ok(toml::to_string(&self)?)
}
}

fn toml_to_components(arr: toml::value::Array, path: &str) -> Result<Vec<Component>> {
let mut result = Vec::new();
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) enum ConfigVersion {
#[serde(rename = "1")]
#[default]
V1,
}

for (i, v) in arr.into_iter().enumerate() {
if let toml::Value::Table(t) = v {
let path = format!("{path}[{i}]");
result.push(Component::from_toml(t, &path, false)?);
}
impl ConfigVersion {
pub fn as_str(&self) -> &'static str {
match self {
Self::V1 => "1",
}

Ok(result)
}
}

fn components_to_toml(components: Vec<Component>) -> toml::value::Array {
let mut result = toml::value::Array::new();
for v in components {
result.push(toml::Value::Table(v.into_toml()));
}
result
}
impl FromStr for ConfigVersion {
type Err = RustupError;

pub(crate) fn new() -> Self {
Default::default()
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"1" => Ok(Self::V1),
_ => Err(RustupError::UnsupportedVersion(s.to_owned())),
}
}
}

impl Default for Config {
fn default() -> Self {
Self {
config_version: DEFAULT_CONFIG_VERSION.to_owned(),
components: Vec::new(),
}
impl fmt::Display for ConfigVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
14 changes: 6 additions & 8 deletions src/dist/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use anyhow::{anyhow, bail, Context, Result};
use chrono::NaiveDate;
use once_cell::sync::Lazy;
use regex::Regex;
use serde::{Deserialize, Serialize};
use thiserror::Error as ThisError;

pub(crate) use crate::dist::triple::*;
Expand Down Expand Up @@ -139,7 +140,8 @@ pub struct ToolchainDesc {
pub target: TargetTriple,
}

#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Clone, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
#[serde(transparent)]
pub struct TargetTriple(String);

// Linux hosts don't indicate clib in uname, however binaries only
Expand Down Expand Up @@ -600,9 +602,11 @@ impl TryFrom<&ToolchainName> for ToolchainDesc {
}
}

#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Profile {
Minimal,
#[default]
Default,
Complete,
}
Expand Down Expand Up @@ -634,12 +638,6 @@ impl Profile {
}
}

impl Default for Profile {
fn default() -> Self {
Self::Default
}
}

impl fmt::Display for TargetTriple {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
Expand Down
Loading