Merge pull request #205 from passcod/write-to-meta-files

This commit is contained in:
Félix Saparelli 2022-07-05 21:47:31 +12:00 committed by GitHub
commit 2eed701d3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 319 additions and 4 deletions

16
Cargo.lock generated
View file

@ -143,6 +143,7 @@ dependencies = [
"flate2", "flate2",
"futures-util", "futures-util",
"guess_host_triple", "guess_host_triple",
"home",
"log", "log",
"miette", "miette",
"once_cell", "once_cell",
@ -150,6 +151,7 @@ dependencies = [
"scopeguard", "scopeguard",
"semver", "semver",
"serde", "serde",
"serde_json",
"simplelog", "simplelog",
"strum", "strum",
"strum_macros", "strum_macros",
@ -158,6 +160,7 @@ dependencies = [
"thiserror", "thiserror",
"tinytemplate", "tinytemplate",
"tokio", "tokio",
"toml",
"url", "url",
"xz2", "xz2",
"zip", "zip",
@ -595,6 +598,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "home"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.8" version = "0.2.8"
@ -1193,9 +1205,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.81" version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",

View file

@ -28,6 +28,7 @@ crates_io_api = { version = "0.8.0", default-features = false, features = ["rust
dirs = "4.0.0" dirs = "4.0.0"
flate2 = { version = "1.0.24", features = ["zlib-ng"], default-features = false } flate2 = { version = "1.0.24", features = ["zlib-ng"], default-features = false }
futures-util = { version = "0.3.21", default-features = false } futures-util = { version = "0.3.21", default-features = false }
home = "0.5.3"
log = "0.4.14" log = "0.4.14"
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] } miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
once_cell = "1.12.0" once_cell = "1.12.0"
@ -35,6 +36,7 @@ reqwest = { version = "0.11.11", features = ["rustls-tls", "stream"], default-fe
scopeguard = "1.1.0" scopeguard = "1.1.0"
semver = "1.0.10" semver = "1.0.10"
serde = { version = "1.0.136", features = ["derive"] } serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.82"
simplelog = "0.12.0" simplelog = "0.12.0"
strum = "0.24.1" strum = "0.24.1"
strum_macros = "0.24.2" strum_macros = "0.24.2"
@ -43,6 +45,7 @@ tempfile = "3.3.0"
thiserror = "1.0.31" thiserror = "1.0.31"
tinytemplate = "1.2.1" tinytemplate = "1.2.1"
tokio = { version = "1.19.1", features = ["rt-multi-thread", "process", "sync"], default-features = false } tokio = { version = "1.19.1", features = ["rt-multi-thread", "process", "sync"], default-features = false }
toml = "0.5.9"
url = "2.2.2" url = "2.2.2"
xz2 = "0.1.6" xz2 = "0.1.6"

View file

@ -6,7 +6,7 @@ use cargo_toml::Manifest;
use futures_util::stream::Stream; use futures_util::stream::Stream;
use log::debug; use log::debug;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use reqwest::{Client, ClientBuilder, Method, Response, tls}; use reqwest::{tls, Client, ClientBuilder, Method, Response};
use serde::Serialize; use serde::Serialize;
use tinytemplate::TinyTemplate; use tinytemplate::TinyTemplate;
use tokio::task::block_in_place; use tokio::task::block_in_place;

View file

@ -13,6 +13,7 @@ pub use helpers::*;
pub mod bins; pub mod bins;
pub mod fetchers; pub mod fetchers;
pub mod metafiles;
mod target; mod target;
pub use target::*; pub use target::*;

View file

@ -1,4 +1,5 @@
use std::{ use std::{
collections::BTreeSet,
ffi::OsString, ffi::OsString,
path::PathBuf, path::PathBuf,
process::{ExitCode, Termination}, process::{ExitCode, Termination},
@ -201,7 +202,10 @@ async fn entry() -> Result<()> {
// Initialize REQWESTGLOBALCONFIG // Initialize REQWESTGLOBALCONFIG
REQWESTGLOBALCONFIG REQWESTGLOBALCONFIG
.set(ReqwestConfig { secure: opts.secure, min_tls: opts.min_tls_version.map(|v| v.into()) }) .set(ReqwestConfig {
secure: opts.secure,
min_tls: opts.min_tls_version.map(|v| v.into()),
})
.unwrap(); .unwrap();
// Setup logging // Setup logging
@ -451,6 +455,14 @@ async fn install_from_package(
uithread.confirm().await?; uithread.confirm().await?;
let cvs = metafiles::CrateVersionSource {
name: opts.name,
version: package.version.parse().into_diagnostic()?,
source: metafiles::Source::Registry(
url::Url::parse("https://github.com/rust-lang/crates.io-index").unwrap(),
),
};
info!("Installing binaries..."); info!("Installing binaries...");
block_in_place(|| { block_in_place(|| {
for file in &bin_files { for file in &bin_files {
@ -472,6 +484,32 @@ async fn install_from_package(
}); });
} }
let bins: BTreeSet<String> = bin_files.iter().map(|bin| bin.base_name.clone()).collect();
{
debug!("Writing .crates.toml");
let mut c1 = metafiles::v1::CratesToml::load().unwrap_or_default();
c1.insert(cvs.clone(), bins.clone());
c1.write()?;
}
{
debug!("Writing .crates2.json");
let mut c2 = metafiles::v2::Crates2Json::load().unwrap_or_default();
c2.insert(
cvs.clone(),
metafiles::v2::CrateInfo {
version_req: Some(opts.version),
bins,
profile: "release".into(),
target: fetcher.target().to_string(),
rustc: format!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")),
..Default::default()
},
);
c2.write()?;
}
Ok(()) Ok(())
}) })
} }

5
src/metafiles.rs Normal file
View file

@ -0,0 +1,5 @@
mod cvs;
pub use cvs::*;
pub mod v1;
pub mod v2;

111
src/metafiles/cvs.rs Normal file
View file

@ -0,0 +1,111 @@
use std::{fmt, str::FromStr};
use miette::Diagnostic;
use semver::Version;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
use url::Url;
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct CrateVersionSource {
pub name: String,
pub version: Version,
pub source: Source,
}
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum Source {
Git(Url),
Path(Url),
Registry(Url),
}
impl FromStr for CrateVersionSource {
type Err = CvsParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.splitn(3, ' ').collect::<Vec<_>>()[..] {
[name, version, source] => {
let version = version.parse()?;
let source = match source
.trim_matches(&['(', ')'][..])
.splitn(2, '+')
.collect::<Vec<_>>()[..]
{
["git", url] => Source::Git(Url::parse(url)?),
["path", url] => Source::Path(Url::parse(url)?),
["registry", url] => Source::Registry(Url::parse(url)?),
[kind, arg] => {
return Err(CvsParseError::UnknownSourceType {
kind: kind.to_string(),
arg: arg.to_string(),
})
}
_ => return Err(CvsParseError::BadSource),
};
Ok(Self {
name: name.to_string(),
version,
source,
})
}
_ => Err(CvsParseError::BadFormat),
}
}
}
#[derive(Debug, Diagnostic, Error)]
pub enum CvsParseError {
#[error(transparent)]
UrlParse(#[from] url::ParseError),
#[error(transparent)]
VersionParse(#[from] semver::Error),
#[error("unknown source type {kind}+{arg}")]
UnknownSourceType { kind: String, arg: String },
#[error("bad source format")]
BadSource,
#[error("bad CVS format")]
BadFormat,
}
impl fmt::Display for CrateVersionSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
name,
version,
source,
} = &self;
write!(f, "{name} {version} ({source})")
}
}
impl fmt::Display for Source {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Source::Git(url) => write!(f, "git+{url}"),
Source::Path(url) => write!(f, "path+{url}"),
Source::Registry(url) => write!(f, "registry+{url}"),
}
}
}
impl Serialize for CrateVersionSource {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for CrateVersionSource {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Self::from_str(&String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
}
}

67
src/metafiles/v1.rs Normal file
View file

@ -0,0 +1,67 @@
use std::{
collections::{BTreeMap, BTreeSet},
fs,
path::{Path, PathBuf},
str::FromStr,
};
use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::CrateVersionSource;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct CratesToml {
v1: BTreeMap<CrateVersionSource, BTreeSet<String>>,
}
impl CratesToml {
pub fn default_path() -> Result<PathBuf, CratesTomlParseError> {
Ok(home::cargo_home()?.join(".crates.toml"))
}
pub fn load() -> Result<Self, CratesTomlParseError> {
Self::load_from_path(Self::default_path()?)
}
pub fn load_from_path(path: impl AsRef<Path>) -> Result<Self, CratesTomlParseError> {
let file = fs::read_to_string(path)?;
Self::from_str(&file)
}
pub fn insert(&mut self, cvs: CrateVersionSource, bins: BTreeSet<String>) {
self.v1.insert(cvs, bins);
}
pub fn write(&self) -> Result<(), CratesTomlParseError> {
self.write_to_path(Self::default_path()?)
}
pub fn write_to_path(&self, path: impl AsRef<Path>) -> Result<(), CratesTomlParseError> {
fs::write(path, &toml::to_vec(&self)?)?;
Ok(())
}
}
impl FromStr for CratesToml {
type Err = CratesTomlParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(toml::from_str(s)?)
}
}
#[derive(Debug, Diagnostic, Error)]
pub enum CratesTomlParseError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
TomlParse(#[from] toml::de::Error),
#[error(transparent)]
TomlWrite(#[from] toml::ser::Error),
#[error(transparent)]
CvsParse(#[from] super::CvsParseError),
}

78
src/metafiles/v2.rs Normal file
View file

@ -0,0 +1,78 @@
use std::{
collections::{BTreeMap, BTreeSet},
fs,
path::{Path, PathBuf},
};
use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::CrateVersionSource;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Crates2Json {
pub installs: BTreeMap<CrateVersionSource, CrateInfo>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct CrateInfo {
#[serde(default)]
pub version_req: Option<String>,
#[serde(default)]
pub bins: BTreeSet<String>,
#[serde(default)]
pub features: BTreeSet<String>,
#[serde(default)]
pub all_features: bool,
#[serde(default)]
pub no_default_features: bool,
pub profile: String,
pub target: String,
pub rustc: String,
}
impl Crates2Json {
pub fn default_path() -> Result<PathBuf, Crates2JsonParseError> {
Ok(home::cargo_home()?.join(".crates2.json"))
}
pub fn load() -> Result<Self, Crates2JsonParseError> {
Self::load_from_path(Self::default_path()?)
}
pub fn load_from_path(path: impl AsRef<Path>) -> Result<Self, Crates2JsonParseError> {
let file = fs::read_to_string(path)?;
Ok(serde_json::from_str(&file)?)
}
pub fn insert(&mut self, cvs: CrateVersionSource, info: CrateInfo) {
self.installs.insert(cvs, info);
}
pub fn write(&self) -> Result<(), Crates2JsonParseError> {
self.write_to_path(Self::default_path()?)
}
pub fn write_to_path(&self, path: impl AsRef<Path>) -> Result<(), Crates2JsonParseError> {
fs::write(path, &serde_json::to_vec(&self)?)?;
Ok(())
}
}
#[derive(Debug, Diagnostic, Error)]
pub enum Crates2JsonParseError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
CvsParse(#[from] super::CvsParseError),
}