2 use flate2
::read
::GzDecoder
;
3 use std
::collections
::HashMap
;
6 use std
::path
::{Path, PathBuf}
;
9 const DEFAULT_TARGET
: &str = "x86_64-unknown-linux-gnu";
10 const RUSTC_VERSION
: &str = include_str
!("../../../version");
12 #[derive(Debug, Hash, Eq, PartialEq, Clone)]
13 pub(crate) enum PkgType
{
27 pub(crate) fn from_component(component
: &str) -> Self {
29 "rust" => PkgType
::Rust
,
30 "rust-src" => PkgType
::RustSrc
,
31 "cargo" => PkgType
::Cargo
,
32 "rls" | "rls-preview" => PkgType
::Rls
,
33 "rust-analyzer" | "rust-analyzer-preview" => PkgType
::RustAnalyzer
,
34 "clippy" | "clippy-preview" => PkgType
::Clippy
,
35 "rustfmt" | "rustfmt-preview" => PkgType
::Rustfmt
,
36 "llvm-tools" | "llvm-tools-preview" => PkgType
::LlvmTools
,
37 "miri" | "miri-preview" => PkgType
::Miri
,
38 other
=> PkgType
::Other(other
.into()),
42 /// First part of the tarball name.
43 fn tarball_component_name(&self) -> &str {
45 PkgType
::Rust
=> "rust",
46 PkgType
::RustSrc
=> "rust-src",
47 PkgType
::Cargo
=> "cargo",
48 PkgType
::Rls
=> "rls",
49 PkgType
::RustAnalyzer
=> "rust-analyzer",
50 PkgType
::Clippy
=> "clippy",
51 PkgType
::Rustfmt
=> "rustfmt",
52 PkgType
::LlvmTools
=> "llvm-tools",
53 PkgType
::Miri
=> "miri",
54 PkgType
::Other(component
) => component
,
58 /// Whether this package has the same version as Rust itself, or has its own `version` and
59 /// `git-commit-hash` files inside the tarball.
60 fn should_use_rust_version(&self) -> bool
{
62 PkgType
::Cargo
=> false,
63 PkgType
::Rls
=> false,
64 PkgType
::RustAnalyzer
=> false,
65 PkgType
::Clippy
=> false,
66 PkgType
::Rustfmt
=> false,
67 PkgType
::LlvmTools
=> false,
68 PkgType
::Miri
=> false,
70 PkgType
::Rust
=> true,
71 PkgType
::RustSrc
=> true,
72 PkgType
::Other(_
) => true,
76 /// Whether this package is target-independent or not.
77 fn target_independent(&self) -> bool
{
78 *self == PkgType
::RustSrc
82 #[derive(Debug, Default, Clone)]
83 pub(crate) struct VersionInfo
{
84 pub(crate) version
: Option
<String
>,
85 pub(crate) git_commit
: Option
<String
>,
86 pub(crate) present
: bool
,
89 pub(crate) struct Versions
{
92 versions
: HashMap
<PkgType
, VersionInfo
>,
96 pub(crate) fn new(channel
: &str, dist_path
: &Path
) -> Result
<Self, Error
> {
97 Ok(Self { channel: channel.into(), dist_path: dist_path.into(), versions: HashMap::new() }
)
100 pub(crate) fn channel(&self) -> &str {
104 pub(crate) fn version(&mut self, mut package
: &PkgType
) -> Result
<VersionInfo
, Error
> {
105 if package
.should_use_rust_version() {
106 package
= &PkgType
::Rust
;
109 match self.versions
.get(package
) {
110 Some(version
) => Ok(version
.clone()),
112 let version_info
= self.load_version_from_tarball(package
)?
;
113 self.versions
.insert(package
.clone(), version_info
.clone());
119 fn load_version_from_tarball(&mut self, package
: &PkgType
) -> Result
<VersionInfo
, Error
> {
120 let tarball_name
= self.tarball_name(package
, DEFAULT_TARGET
)?
;
121 let tarball
= self.dist_path
.join(tarball_name
);
123 let file
= match File
::open(&tarball
) {
125 Err(err
) if err
.kind() == std
::io
::ErrorKind
::NotFound
=> {
126 // Missing tarballs do not return an error, but return empty data.
127 return Ok(VersionInfo
::default());
129 Err(err
) => return Err(err
.into()),
131 let mut tar
= Archive
::new(GzDecoder
::new(file
));
133 let mut version
= None
;
134 let mut git_commit
= None
;
135 for entry
in tar
.entries()?
{
136 let mut entry
= entry?
;
139 match entry
.path()?
.components().nth(1).and_then(|c
| c
.as_os_str().to_str()) {
140 Some("version") => dest
= &mut version
,
141 Some("git-commit-hash") => dest
= &mut git_commit
,
144 let mut buf
= String
::new();
145 entry
.read_to_string(&mut buf
)?
;
148 // Short circuit to avoid reading the whole tar file if not necessary.
149 if version
.is_some() && git_commit
.is_some() {
154 Ok(VersionInfo { version, git_commit, present: true }
)
157 pub(crate) fn disable_version(&mut self, package
: &PkgType
) {
158 match self.versions
.get_mut(package
) {
160 *version
= VersionInfo
::default();
163 self.versions
.insert(package
.clone(), VersionInfo
::default());
168 pub(crate) fn tarball_name(
172 ) -> Result
<String
, Error
> {
173 let component_name
= package
.tarball_component_name();
174 let version
= match self.channel
.as_str() {
175 "stable" => RUSTC_VERSION
.into(),
176 "beta" => "beta".into(),
177 "nightly" => "nightly".into(),
178 _
=> format
!("{}-dev", RUSTC_VERSION
),
181 if package
.target_independent() {
182 Ok(format
!("{}-{}.tar.gz", component_name
, version
))
184 Ok(format
!("{}-{}-{}.tar.gz", component_name
, version
, target
))
188 pub(crate) fn rustc_version(&self) -> &str {