#[ allow( clippy ::std_instead_of_alloc, clippy ::std_instead_of_core ) ]
mod private
{
use crate :: *;
use std ::
{
fmt ::Formatter,
ffi ::OsString,
};
use std ::path ::Path;
use collection_tools ::collection ::HashSet;
use error ::untyped :: { Error };
use process_tools ::process :: *;
#[ derive( Debug, Default, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd ) ]
pub enum Channel
{
#[ default ]
Stable,
Nightly,
}
impl std ::fmt ::Display for Channel
{
fn fmt( &self, f: &mut Formatter< '_ > ) -> std ::fmt ::Result
{
match self
{
Self ::Stable => write!( f, "stable" ),
Self ::Nightly => write!( f, "nightly" ),
}
}
}
impl TryFrom< String > for Channel
{
type Error = error ::untyped ::Error;
fn try_from( value: String ) -> Result< Self, Self ::Error >
{
Ok( match value.as_ref()
{
"stable" => Self ::Stable,
"nightly" => Self ::Nightly,
other => error ::untyped ::bail!( "Unexpected channel value. Expected [stable, channel]. Got: `{other}`" ),
})
}
}
fn classify_toolchain( line: &str ) -> Option< Channel >
{
let name = line.split_whitespace().next().unwrap_or( "" );
if name.is_empty() { return None; }
if name.starts_with( "stable" ) { return Some( Channel ::Stable ); }
if name.starts_with( "nightly" ) { return Some( Channel ::Nightly ); }
if name.chars().next().is_some_and( | c | c.is_ascii_digit() )
&& !name.contains( "nightly" )
&& !name.contains( "beta" )
{
return Some( Channel ::Stable );
}
None
}
pub fn available_channels< P >( path: P ) -> error ::untyped ::Result< HashSet< Channel > >
where
P: AsRef< Path >,
{
let ( program, options ) = ( "rustup", [ "toolchain", "list" ] );
let report = Run ::former()
.bin_path( program )
.args( options.into_iter().map( OsString ::from ).collect :: < Vec< _ > >() )
.current_path( path.as_ref().to_path_buf() )
.run().map_err :: < Error, _ >( | report | error ::untyped ::format_err!( report.to_string() ) )?;
Ok( report.out.lines().filter_map( classify_toolchain ).collect() )
}
pub fn toolchain_name< P >( channel: Channel, path: P ) -> error ::untyped ::Result< String >
where
P: AsRef< Path >,
{
let ( program, options ) = ( "rustup", [ "toolchain", "list" ] );
let report = Run ::former()
.bin_path( program )
.args( options.into_iter().map( OsString ::from ).collect :: < Vec< _ > >() )
.current_path( path.as_ref().to_path_buf() )
.run().map_err :: < Error, _ >( | report | error ::untyped ::format_err!( report.to_string() ) )?;
let channel_str = channel.to_string();
for line in report.out.lines()
{
let name = line.split_whitespace().next().unwrap_or( "" );
if name.starts_with( &channel_str ) { return Ok( channel_str ); }
}
for line in report.out.lines()
{
let name = line.split_whitespace().next().unwrap_or( "" );
let matches = match channel
{
Channel ::Stable =>
name.chars().next().is_some_and( | c | c.is_ascii_digit() )
&& !name.contains( "nightly" )
&& !name.contains( "beta" ),
Channel ::Nightly => name.contains( "nightly" ),
};
if matches { return Ok( name.to_string() ); }
}
error ::untyped ::bail!
(
"No installed toolchain for channel `{channel_str}`. \
Try to install it with `rustup install {channel_str}`"
)
}
}
crate ::mod_interface!
{
own use Channel;
own use available_channels;
own use toolchain_name;
}