ltk/file_chooser/
places.rsuse std::path::{ Path, PathBuf };
use crate::files;
const MEDIA: &str = "/media";
#[ derive( Clone, Copy, Debug, PartialEq, Eq ) ]
pub enum PlaceKind
{
Home,
Desktop,
Documents,
Downloads,
Music,
Pictures,
Videos,
Root,
Mount,
}
#[ derive( Clone, Debug, PartialEq, Eq ) ]
pub struct Place
{
pub kind: PlaceKind,
pub path: PathBuf,
pub name: Option<String>,
}
const USER_DIRS: [ ( PlaceKind, &str, &str ); 6 ] = [
( PlaceKind::Desktop, "DESKTOP", "Desktop" ),
( PlaceKind::Documents, "DOCUMENTS", "Documents" ),
( PlaceKind::Downloads, "DOWNLOAD", "Downloads" ),
( PlaceKind::Music, "MUSIC", "Music" ),
( PlaceKind::Pictures, "PICTURES", "Pictures" ),
( PlaceKind::Videos, "VIDEOS", "Videos" ),
];
pub fn home() -> PathBuf
{
crate::dirs::home().unwrap_or_else( || PathBuf::from( MEDIA ) )
}
pub fn standard() -> Vec<Place>
{
let home = home();
let mut out = vec![ Place { kind: PlaceKind::Home, path: home.clone(), name: None } ];
for ( kind, key, fallback ) in USER_DIRS
{
let path = user_dir( &home, key ).unwrap_or_else( || home.join( fallback ) );
if path.is_dir() { out.push( Place { kind, path, name: None } ); }
}
out.extend( roots() );
for mount in files::mounts().unwrap_or_default()
{
out.push( Place { kind: PlaceKind::Mount, path: mount.path, name: Some( mount.name ) } );
}
out
}
#[ cfg( not( windows ) ) ]
fn roots() -> Option<Place>
{
Path::new( "/" ).is_dir().then( || Place { kind: PlaceKind::Root, path: PathBuf::from( "/" ), name: None } )
}
#[ cfg( windows ) ]
fn roots() -> Vec<Place>
{
( 'C'..='Z' )
.map( |letter| ( format!( "{letter}:" ), PathBuf::from( format!( "{letter}:\\" ) ) ) )
.filter( |( _, path )| path.is_dir() )
.map( |( name, path )| Place { kind: PlaceKind::Root, path, name: Some( name ) } )
.collect()
}
fn user_dir( home: &Path, key: &str ) -> Option<PathBuf>
{
let config = crate::dirs::config_home().filter( |p| p.is_absolute() ).unwrap_or_else( || home.join( ".config" ) );
let text = std::fs::read_to_string( config.join( "user-dirs.dirs" ) ).ok()?;
parse_user_dir( &text, home, key )
}
pub( crate ) fn parse_user_dir( text: &str, home: &Path, key: &str ) -> Option<PathBuf>
{
let wanted = format!( "XDG_{key}_DIR" );
let value = text.lines()
.filter( |l| !l.trim_start().starts_with( '#' ) )
.filter_map( |l| l.split_once( '=' ) )
.find( |( k, _ )| k.trim() == wanted )?
.1.trim().trim_matches( '"' ).to_string();
let path = match value.strip_prefix( "$HOME" )
{
Some( rest ) => home.join( rest.trim_start_matches( '/' ) ),
None => PathBuf::from( value ),
};
( path != home ).then_some( path )
}