use std::collections::BTreeSet;
use crate::artifact::{ ArtifactKind, ArtifactLayout };
use crate::error::AssetError;
use crate::paths::AssetPaths;
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub enum InstallStatus
{
Installed,
Available,
}
#[ inline ]
pub fn list_available( paths : &AssetPaths, kind : ArtifactKind ) -> Result< Vec< String >, AssetError >
{
let dir = paths.source_dir( kind );
if !dir.exists()
{
return Ok( Vec::new() );
}
collect_names( &dir, kind )
}
#[ inline ]
pub fn list_installed( paths : &AssetPaths, kind : ArtifactKind ) -> Result< Vec< String >, AssetError >
{
let dir = paths.target_dir( kind );
if !dir.exists()
{
return Ok( Vec::new() );
}
collect_symlink_names( &dir, kind )
}
#[ inline ]
pub fn list_all( paths : &AssetPaths, kind : ArtifactKind ) -> Result< Vec< ( String, InstallStatus ) >, AssetError >
{
let available = list_available( paths, kind )?;
let installed = list_installed( paths, kind )?;
let inst_set : BTreeSet< _ > = installed.iter().cloned().collect();
let mut all : Vec< _ > = available.into_iter()
.map( | name |
{
let status = if inst_set.contains( &name ) { InstallStatus::Installed } else { InstallStatus::Available };
( name, status )
} )
.collect();
for name in inst_set
{
if !all.iter().any( | ( n, _ ) | *n == name )
{
all.push( ( name, InstallStatus::Installed ) );
}
}
all.sort_by( | a, b | a.0.cmp( &b.0 ) );
Ok( all )
}
fn collect_names( dir : &std::path::Path, kind : ArtifactKind ) -> Result< Vec< String >, AssetError >
{
let mut names = Vec::new();
for entry in std::fs::read_dir( dir ).map_err( AssetError::Io )?
{
let entry = entry.map_err( AssetError::Io )?;
let path = entry.path();
if let Some( name ) = artifact_name( &path, kind )
{
names.push( name );
}
}
names.sort();
Ok( names )
}
fn collect_symlink_names( dir : &std::path::Path, kind : ArtifactKind ) -> Result< Vec< String >, AssetError >
{
let mut names = Vec::new();
for entry in std::fs::read_dir( dir ).map_err( AssetError::Io )?
{
let entry = entry.map_err( AssetError::Io )?;
let path = entry.path();
let metadata = std::fs::symlink_metadata( &path ).map_err( AssetError::Io )?;
if !metadata.file_type().is_symlink()
{
continue;
}
if let Some( name ) = artifact_name( &path, kind )
{
names.push( name );
}
}
names.sort();
Ok( names )
}
fn artifact_name( path : &std::path::Path, kind : ArtifactKind ) -> Option< String >
{
match kind.layout()
{
ArtifactLayout::File =>
{
let ext = kind.file_extension()?;
let stem = path.file_stem()?.to_str()?.to_string();
if path.extension().and_then( | e | e.to_str() ) == Some( ext )
{
Some( stem )
}
else
{
None
}
}
ArtifactLayout::Directory =>
{
if path.is_dir() || std::fs::symlink_metadata( path ).is_ok_and( | m | m.file_type().is_symlink() )
{
path.file_name()?.to_str().map( str::to_string )
}
else
{
None
}
}
}
}