1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
use colored::Colorize;
use crate::{channel::UpstreamMatch, config::Config, state::LocalState};
/// List all the available [[Channels]] presents in the upstream manifest.
pub fn list(config: &Config, state: &LocalState) -> anyhow::Result<()> {
let upstream_channels = config.upstream_manifest()?.get_channels();
crate::report::prepare_stdout_color();
let toolchains_display: Vec<String> = upstream_channels
.map(|channel| {
let channel_name = &channel.name;
// Update status is *derived*, never recorded (spec section 8.6): an installation has
// an update exactly when re-resolving its recorded intent against upstream would
// change it. A stored flag would be a second answer to a question the manifest and
// the component set already answer, and the two would drift.
let installed_indicator = match state.get(&channel.name) {
Some(installation)
if super::update::needs_update(config, installation, channel) =>
{
format!(" {}", "(update available)".yellow())
},
Some(_) => format!(" {}", "(installed)".green()),
// A channel that supersedes an installed one (spec section 11.4): updating the
// predecessor migrates to it. The counterpart lookup is the updater's, so a
// predecessor still published upstream is not shown as migrating away.
None if channel
.migrates_from
.as_ref()
.and_then(|old| state.get(old))
.is_some_and(|predecessor| {
matches!(
predecessor.as_channel().upstream_counterpart_raw(config),
Some((successor, UpstreamMatch::Migrated { .. }))
if successor.name == channel.name
)
}) =>
{
format!(" {}", "(update available)".yellow())
},
None => String::new(),
};
format!("{channel_name}{installed_indicator}")
})
.collect();
println!("{}", "Available toolchains upstream:".bold().underline());
for toolchain in toolchains_display {
println!("{toolchain}");
}
Ok(())
}