Skip to content

Use serde to encode/decode various TOML formats #3864

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

Merged
merged 13 commits into from
Jun 9, 2024
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
Loading