1#[cfg(target_os = "macos")]
36mod macos;
37#[cfg(all(target_family = "unix", not(target_os = "macos")))]
38mod unix;
39#[cfg(target_os = "windows")]
40mod windows;
41pub(crate) mod xdg;
42
43use std::{env, path::PathBuf};
44
45#[cfg(target_os = "macos")]
46use macos as os;
47#[cfg(all(target_family = "unix", not(target_os = "macos")))]
48use unix as os;
49#[cfg(target_os = "windows")]
50use windows as os;
51
52pub fn bin_home() -> Option<PathBuf> {
67 os::bin_home()
68}
69
70pub fn cache_home() -> Option<PathBuf> {
86 os::cache_home()
87}
88
89pub fn config_home() -> Option<PathBuf> {
105 os::config_home()
106}
107
108pub fn config_local() -> Option<PathBuf> {
127 os::config_local()
128}
129
130pub fn data_home() -> Option<PathBuf> {
146 os::data_home()
147}
148
149pub fn data_local() -> Option<PathBuf> {
168 os::data_local()
169}
170
171pub fn desktop() -> Option<PathBuf> {
186 os::desktop()
187}
188
189pub fn documents() -> Option<PathBuf> {
204 os::documents()
205}
206
207pub fn downloads() -> Option<PathBuf> {
222 os::downloads()
223}
224
225pub fn fonts() -> Option<PathBuf> {
246 os::fonts()
247}
248
249pub fn home() -> Option<PathBuf> {
262 env::home_dir()
263}
264
265pub fn music() -> Option<PathBuf> {
280 os::music()
281}
282
283pub fn pictures() -> Option<PathBuf> {
298 os::pictures()
299}
300
301pub fn preferences() -> Option<PathBuf> {
320 os::preferences()
321}
322
323pub fn publicshare() -> Option<PathBuf> {
338 os::publicshare()
339}
340
341pub fn runtime() -> Option<PathBuf> {
357 os::runtime()
358}
359
360pub fn state_home() -> Option<PathBuf> {
376 os::state_home()
377}
378
379pub fn templates() -> Option<PathBuf> {
394 os::templates()
395}
396
397pub fn videos() -> Option<PathBuf> {
413 os::videos()
414}
415
416pub fn xdg_bin_home_or(default: impl Into<PathBuf>) -> PathBuf {
432 xdg::resolve_path(xdg::BIN_HOME).unwrap_or_else(|| default.into())
433}
434
435pub fn xdg_cache_home_or(default: impl Into<PathBuf>) -> PathBuf {
451 xdg::resolve_path(xdg::CACHE_HOME).unwrap_or_else(|| default.into())
452}
453
454pub fn xdg_config_home_or(default: impl Into<PathBuf>) -> PathBuf {
470 xdg::resolve_path(xdg::CONFIG_HOME).unwrap_or_else(|| default.into())
471}
472
473pub fn xdg_data_home_or(default: impl Into<PathBuf>) -> PathBuf {
489 xdg::resolve_path(xdg::DATA_HOME).unwrap_or_else(|| default.into())
490}
491
492pub fn xdg_desktop_dir_or(default: impl Into<PathBuf>) -> PathBuf {
508 xdg::resolve_path(xdg::DESKTOP_DIR).unwrap_or_else(|| default.into())
509}
510
511pub fn xdg_documents_dir_or(default: impl Into<PathBuf>) -> PathBuf {
527 xdg::resolve_path(xdg::DOCUMENTS_DIR).unwrap_or_else(|| default.into())
528}
529
530pub fn xdg_download_dir_or(default: impl Into<PathBuf>) -> PathBuf {
546 xdg::resolve_path(xdg::DOWNLOAD_DIR).unwrap_or_else(|| default.into())
547}
548
549pub fn xdg_music_dir_or(default: impl Into<PathBuf>) -> PathBuf {
565 xdg::resolve_path(xdg::MUSIC_DIR).unwrap_or_else(|| default.into())
566}
567
568pub fn xdg_pictures_dir_or(default: impl Into<PathBuf>) -> PathBuf {
584 xdg::resolve_path(xdg::PICTURES_DIR).unwrap_or_else(|| default.into())
585}
586
587pub fn xdg_publicshare_dir_or(default: impl Into<PathBuf>) -> PathBuf {
603 xdg::resolve_path(xdg::PUBLICSHARE_DIR).unwrap_or_else(|| default.into())
604}
605
606pub fn xdg_runtime_dir_or(default: impl Into<PathBuf>) -> PathBuf {
622 xdg::resolve_path(xdg::RUNTIME_DIR).unwrap_or_else(|| default.into())
623}
624
625pub fn xdg_state_home_or(default: impl Into<PathBuf>) -> PathBuf {
641 xdg::resolve_path(xdg::STATE_HOME).unwrap_or_else(|| default.into())
642}
643
644pub fn xdg_templates_dir_or(default: impl Into<PathBuf>) -> PathBuf {
660 xdg::resolve_path(xdg::TEMPLATES_DIR).unwrap_or_else(|| default.into())
661}
662
663pub fn xdg_videos_dir_or(default: impl Into<PathBuf>) -> PathBuf {
679 xdg::resolve_path(xdg::VIDEOS_DIR).unwrap_or_else(|| default.into())
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685
686 mod bin_home {
687 use temp_env::{with_var, with_var_unset};
688
689 use super::*;
690
691 #[test]
692 fn respects_xdg_bin_home() {
693 let test_path = if cfg!(windows) { "C:\\test\\bin" } else { "/test/bin" };
694 with_var("XDG_BIN_HOME", Some(test_path), || {
695 let result = bin_home();
696 assert_eq!(result, Some(PathBuf::from(test_path)));
697 });
698 }
699
700 #[test]
701 fn ignores_relative_xdg_bin_home() {
702 with_var("XDG_BIN_HOME", Some("relative/bin"), || {
703 let result = bin_home();
704
705 if let Some(path) = result {
706 assert!(path.is_absolute());
707 }
708 });
709 }
710
711 #[test]
712 fn uses_platform_default_when_xdg_unset() {
713 with_var_unset("XDG_BIN_HOME", || {
714 let result = bin_home();
715 if let Some(bin_path) = result {
716 assert!(bin_path.is_absolute());
717
718 #[cfg(any(target_os = "linux", target_os = "macos"))]
719 assert!(bin_path.to_string_lossy().ends_with(".local/bin"));
720
721 #[cfg(target_os = "windows")]
722 assert!(bin_path.to_string_lossy().contains("Programs"));
723 }
724 });
725 }
726 }
727
728 mod cache_home {
729 use temp_env::{with_var, with_var_unset};
730
731 use super::*;
732
733 #[test]
734 fn respects_xdg_cache_home() {
735 let test_path = if cfg!(windows) {
736 "C:\\test\\cache"
737 } else {
738 "/test/cache"
739 };
740 with_var("XDG_CACHE_HOME", Some(test_path), || {
741 let result = cache_home();
742 assert_eq!(result, Some(PathBuf::from(test_path)));
743 });
744 }
745
746 #[test]
747 fn ignores_relative_xdg_cache_home() {
748 with_var("XDG_CACHE_HOME", Some("relative/cache"), || {
749 let result = cache_home();
750 if let Some(path) = result {
751 assert!(path.is_absolute());
752 }
753 });
754 }
755
756 #[test]
757 fn uses_platform_default_when_xdg_unset() {
758 with_var_unset("XDG_CACHE_HOME", || {
759 let result = cache_home();
760 if let Some(cache_path) = result {
761 assert!(cache_path.is_absolute());
762
763 #[cfg(target_os = "linux")]
764 assert!(cache_path.to_string_lossy().ends_with(".cache"));
765
766 #[cfg(target_os = "macos")]
767 assert!(cache_path.to_string_lossy().contains("Library/Caches"));
768
769 #[cfg(target_os = "windows")]
770 {
771 if let Ok(localappdata) = env::var("LOCALAPPDATA") {
772 assert_eq!(cache_path, PathBuf::from(localappdata));
773 }
774 }
775 }
776 });
777 }
778 }
779
780 mod config_home {
781 use temp_env::{with_var, with_var_unset};
782
783 use super::*;
784
785 #[test]
786 fn respects_xdg_config_home() {
787 let test_path = if cfg!(windows) {
788 "C:\\test\\config"
789 } else {
790 "/test/config"
791 };
792 with_var("XDG_CONFIG_HOME", Some(test_path), || {
793 let result = config_home();
794 assert_eq!(result, Some(PathBuf::from(test_path)));
795 });
796 }
797
798 #[test]
799 fn ignores_relative_xdg_config_home() {
800 with_var("XDG_CONFIG_HOME", Some("relative/config"), || {
801 let result = config_home();
802 if let Some(path) = result {
803 assert!(path.is_absolute());
804 }
805 });
806 }
807
808 #[test]
809 fn uses_platform_default_when_xdg_unset() {
810 with_var_unset("XDG_CONFIG_HOME", || {
811 let result = config_home();
812 if let Some(config_path) = result {
813 assert!(config_path.is_absolute());
814
815 #[cfg(target_os = "linux")]
816 assert!(config_path.to_string_lossy().ends_with(".config"));
817
818 #[cfg(target_os = "macos")]
819 assert!(config_path.to_string_lossy().contains("Library/Application Support"));
820
821 #[cfg(target_os = "windows")]
822 {
823 if let Ok(appdata) = env::var("APPDATA") {
824 assert_eq!(config_path, PathBuf::from(appdata));
825 }
826 }
827 }
828 });
829 }
830 }
831
832 mod config_local {
833 use super::*;
834
835 #[test]
836 fn uses_localappdata_on_windows() {
837 let result = config_local();
838 if let Some(config_local_path) = result {
839 assert!(config_local_path.is_absolute());
840
841 #[cfg(target_os = "windows")]
842 {
843 if let Ok(localappdata) = env::var("LOCALAPPDATA") {
844 assert_eq!(config_local_path, PathBuf::from(localappdata));
845 }
846 }
847
848 #[cfg(not(target_os = "windows"))]
849 {
850 assert_eq!(Some(config_local_path), config_home());
851 }
852 }
853 }
854
855 #[test]
856 fn matches_config_home_on_non_windows() {
857 #[cfg(not(target_os = "windows"))]
858 {
859 let config_local = config_local();
860 let config_home = config_home();
861 assert_eq!(config_local, config_home);
862 }
863 }
864 }
865
866 mod data_home {
867 use temp_env::{with_var, with_var_unset};
868
869 use super::*;
870
871 #[test]
872 fn respects_xdg_data_home() {
873 let test_path = if cfg!(windows) { "C:\\test\\data" } else { "/test/data" };
874 with_var("XDG_DATA_HOME", Some(test_path), || {
875 let result = data_home();
876 assert_eq!(result, Some(PathBuf::from(test_path)));
877 });
878 }
879
880 #[test]
881 fn ignores_relative_xdg_data_home() {
882 with_var("XDG_DATA_HOME", Some("relative/data"), || {
883 let result = data_home();
884 if let Some(path) = result {
885 assert!(path.is_absolute());
886 }
887 });
888 }
889
890 #[test]
891 fn uses_platform_default_when_xdg_unset() {
892 with_var_unset("XDG_DATA_HOME", || {
893 let result = data_home();
894 if let Some(data_path) = result {
895 assert!(data_path.is_absolute());
896
897 #[cfg(target_os = "linux")]
898 assert!(data_path.to_string_lossy().ends_with(".local/share"));
899
900 #[cfg(target_os = "macos")]
901 assert!(data_path.to_string_lossy().contains("Library/Application Support"));
902
903 #[cfg(target_os = "windows")]
904 {
905 if let Ok(appdata) = env::var("APPDATA") {
906 assert_eq!(data_path, PathBuf::from(appdata));
907 }
908 }
909 }
910 });
911 }
912 }
913
914 mod data_local {
915 use super::*;
916
917 #[test]
918 fn uses_localappdata_on_windows() {
919 let result = data_local();
920 if let Some(data_local_path) = result {
921 assert!(data_local_path.is_absolute());
922
923 #[cfg(target_os = "windows")]
924 {
925 if let Ok(localappdata) = env::var("LOCALAPPDATA") {
926 assert_eq!(data_local_path, PathBuf::from(localappdata));
927 }
928 }
929
930 #[cfg(not(target_os = "windows"))]
931 {
932 assert_eq!(Some(data_local_path), data_home());
933 }
934 }
935 }
936
937 #[test]
938 fn matches_data_home_on_non_windows() {
939 #[cfg(not(target_os = "windows"))]
940 {
941 let data_local = data_local();
942 let data_home = data_home();
943 assert_eq!(data_local, data_home);
944 }
945 }
946 }
947
948 mod desktop {
949 use temp_env::{with_var, with_var_unset};
950
951 use super::*;
952
953 #[test]
954 fn respects_xdg_desktop_dir() {
955 let test_path = if cfg!(windows) {
956 "C:\\test\\desktop"
957 } else {
958 "/test/desktop"
959 };
960 with_var("XDG_DESKTOP_DIR", Some(test_path), || {
961 let result = desktop();
962 assert_eq!(result, Some(PathBuf::from(test_path)));
963 });
964 }
965
966 #[test]
967 fn ignores_relative_xdg_desktop_dir() {
968 with_var("XDG_DESKTOP_DIR", Some("relative/desktop"), || {
969 let result = desktop();
970 if let Some(path) = result {
971 assert!(path.is_absolute());
972 }
973 });
974 }
975
976 #[test]
977 fn uses_platform_default_when_xdg_unset() {
978 with_var_unset("XDG_DESKTOP_DIR", || {
979 let result = desktop();
980 if let Some(desktop_path) = result {
981 assert!(desktop_path.is_absolute());
982 assert!(desktop_path.to_string_lossy().ends_with("Desktop"));
983 }
984 });
985 }
986 }
987
988 mod documents {
989 use temp_env::{with_var, with_var_unset};
990
991 use super::*;
992
993 #[test]
994 fn respects_xdg_documents_dir() {
995 let test_path = if cfg!(windows) {
996 "C:\\test\\documents"
997 } else {
998 "/test/documents"
999 };
1000 with_var("XDG_DOCUMENTS_DIR", Some(test_path), || {
1001 let result = documents();
1002 assert_eq!(result, Some(PathBuf::from(test_path)));
1003 });
1004 }
1005
1006 #[test]
1007 fn ignores_relative_xdg_documents_dir() {
1008 with_var("XDG_DOCUMENTS_DIR", Some("relative/documents"), || {
1009 let result = documents();
1010 if let Some(path) = result {
1011 assert!(path.is_absolute());
1012 }
1013 });
1014 }
1015
1016 #[test]
1017 fn uses_platform_default_when_xdg_unset() {
1018 with_var_unset("XDG_DOCUMENTS_DIR", || {
1019 let result = documents();
1020 if let Some(documents_path) = result {
1021 assert!(documents_path.is_absolute());
1022 assert!(documents_path.to_string_lossy().ends_with("Documents"));
1023 }
1024 });
1025 }
1026 }
1027
1028 mod downloads {
1029 use temp_env::{with_var, with_var_unset};
1030
1031 use super::*;
1032
1033 #[test]
1034 fn respects_xdg_download_dir() {
1035 let test_path = if cfg!(windows) {
1036 "C:\\test\\downloads"
1037 } else {
1038 "/test/downloads"
1039 };
1040 with_var("XDG_DOWNLOAD_DIR", Some(test_path), || {
1041 let result = downloads();
1042 assert_eq!(result, Some(PathBuf::from(test_path)));
1043 });
1044 }
1045
1046 #[test]
1047 fn ignores_relative_xdg_download_dir() {
1048 with_var("XDG_DOWNLOAD_DIR", Some("relative/downloads"), || {
1049 let result = downloads();
1050 if let Some(path) = result {
1051 assert!(path.is_absolute());
1052 }
1053 });
1054 }
1055
1056 #[test]
1057 fn uses_platform_default_when_xdg_unset() {
1058 with_var_unset("XDG_DOWNLOAD_DIR", || {
1059 let result = downloads();
1060 if let Some(downloads_path) = result {
1061 assert!(downloads_path.is_absolute());
1062 assert!(downloads_path.to_string_lossy().ends_with("Downloads"));
1063 }
1064 });
1065 }
1066 }
1067
1068 mod fonts {
1069 use super::*;
1070
1071 #[test]
1072 fn returns_platform_specific_path() {
1073 let result = fonts();
1074
1075 #[cfg(target_os = "linux")]
1076 if let Some(fonts_path) = result {
1077 assert!(fonts_path.is_absolute());
1078 assert!(fonts_path.to_string_lossy().ends_with(".local/share/fonts"));
1079 }
1080
1081 #[cfg(target_os = "macos")]
1082 if let Some(fonts_path) = result {
1083 assert!(fonts_path.is_absolute());
1084 assert!(fonts_path.to_string_lossy().ends_with("Library/Fonts"));
1085 }
1086
1087 #[cfg(target_os = "windows")]
1088 assert_eq!(result, None);
1089 }
1090
1091 #[test]
1092 fn returns_none_on_windows() {
1093 #[cfg(target_os = "windows")]
1094 {
1095 let result = fonts();
1096 assert_eq!(result, None);
1097 }
1098 }
1099
1100 #[test]
1101 fn returns_some_on_unix() {
1102 #[cfg(any(target_os = "linux", target_os = "macos"))]
1103 {
1104 let result = fonts();
1105 assert!(result.is_some());
1106 if let Some(path) = result {
1107 assert!(path.is_absolute());
1108 }
1109 }
1110 }
1111 }
1112
1113 mod home {
1114 use super::*;
1115
1116 #[test]
1117 fn returns_absolute_path_when_available() {
1118 let result = home();
1119 if let Some(home_path) = result {
1120 assert!(home_path.is_absolute());
1121 }
1122 }
1123
1124 #[test]
1125 fn delegates_to_std_env_home_dir() {
1126 let std_result = std::env::home_dir();
1127 let our_result = home();
1128 assert_eq!(std_result, our_result);
1129 }
1130 }
1131
1132 mod music {
1133 use temp_env::{with_var, with_var_unset};
1134
1135 use super::*;
1136
1137 #[test]
1138 fn respects_xdg_music_dir() {
1139 let test_path = if cfg!(windows) {
1140 "C:\\test\\music"
1141 } else {
1142 "/test/music"
1143 };
1144 with_var("XDG_MUSIC_DIR", Some(test_path), || {
1145 let result = music();
1146 assert_eq!(result, Some(PathBuf::from(test_path)));
1147 });
1148 }
1149
1150 #[test]
1151 fn ignores_relative_xdg_music_dir() {
1152 with_var("XDG_MUSIC_DIR", Some("relative/music"), || {
1153 let result = music();
1154 if let Some(path) = result {
1155 assert!(path.is_absolute());
1156 }
1157 });
1158 }
1159
1160 #[test]
1161 fn uses_platform_default_when_xdg_unset() {
1162 with_var_unset("XDG_MUSIC_DIR", || {
1163 let result = music();
1164 if let Some(music_path) = result {
1165 assert!(music_path.is_absolute());
1166 assert!(music_path.to_string_lossy().ends_with("Music"));
1167 }
1168 });
1169 }
1170 }
1171
1172 mod pictures {
1173 use temp_env::{with_var, with_var_unset};
1174
1175 use super::*;
1176
1177 #[test]
1178 fn respects_xdg_pictures_dir() {
1179 let test_path = if cfg!(windows) {
1180 "C:\\test\\pictures"
1181 } else {
1182 "/test/pictures"
1183 };
1184 with_var("XDG_PICTURES_DIR", Some(test_path), || {
1185 let result = pictures();
1186 assert_eq!(result, Some(PathBuf::from(test_path)));
1187 });
1188 }
1189
1190 #[test]
1191 fn ignores_relative_xdg_pictures_dir() {
1192 with_var("XDG_PICTURES_DIR", Some("relative/pictures"), || {
1193 let result = pictures();
1194 if let Some(path) = result {
1195 assert!(path.is_absolute());
1196 }
1197 });
1198 }
1199
1200 #[test]
1201 fn uses_platform_default_when_xdg_unset() {
1202 with_var_unset("XDG_PICTURES_DIR", || {
1203 let result = pictures();
1204 if let Some(pictures_path) = result {
1205 assert!(pictures_path.is_absolute());
1206 assert!(pictures_path.to_string_lossy().ends_with("Pictures"));
1207 }
1208 });
1209 }
1210 }
1211
1212 mod preferences {
1213 use super::*;
1214
1215 #[test]
1216 fn returns_platform_specific_path() {
1217 let result = preferences();
1218 if let Some(preferences_path) = result {
1219 assert!(preferences_path.is_absolute());
1220
1221 #[cfg(target_os = "macos")]
1222 assert!(preferences_path.to_string_lossy().ends_with("Library/Preferences"));
1223
1224 #[cfg(not(target_os = "macos"))]
1225 assert_eq!(Some(preferences_path), config_home());
1226 }
1227 }
1228
1229 #[test]
1230 fn matches_config_home_on_non_macos() {
1231 #[cfg(not(target_os = "macos"))]
1232 {
1233 let preferences = preferences();
1234 let config_home = config_home();
1235 assert_eq!(preferences, config_home);
1236 }
1237 }
1238
1239 #[test]
1240 fn uses_library_preferences_on_macos() {
1241 #[cfg(target_os = "macos")]
1242 {
1243 let result = preferences();
1244 if let Some(path) = result {
1245 assert!(path.to_string_lossy().ends_with("Library/Preferences"));
1246 }
1247 }
1248 }
1249 }
1250
1251 mod publicshare {
1252 use temp_env::{with_var, with_var_unset};
1253
1254 use super::*;
1255
1256 #[test]
1257 fn respects_xdg_publicshare_dir() {
1258 let test_path = if cfg!(windows) {
1259 "C:\\test\\public"
1260 } else {
1261 "/test/public"
1262 };
1263 with_var("XDG_PUBLICSHARE_DIR", Some(test_path), || {
1264 let result = publicshare();
1265 assert_eq!(result, Some(PathBuf::from(test_path)));
1266 });
1267 }
1268
1269 #[test]
1270 fn ignores_relative_xdg_publicshare_dir() {
1271 with_var("XDG_PUBLICSHARE_DIR", Some("relative/public"), || {
1272 let result = publicshare();
1273 if let Some(path) = result {
1274 assert!(path.is_absolute());
1275 }
1276 });
1277 }
1278
1279 #[test]
1280 fn uses_platform_default_when_xdg_unset() {
1281 with_var_unset("XDG_PUBLICSHARE_DIR", || {
1282 let result = publicshare();
1283 if let Some(public_path) = result {
1284 assert!(public_path.is_absolute());
1285
1286 #[cfg(target_os = "windows")]
1287 assert_eq!(public_path, PathBuf::from("C:\\Users\\Public"));
1288
1289 #[cfg(any(target_os = "linux", target_os = "macos"))]
1290 assert!(public_path.to_string_lossy().ends_with("Public"));
1291 }
1292 });
1293 }
1294
1295 #[test]
1296 fn uses_system_public_on_windows() {
1297 #[cfg(target_os = "windows")]
1298 {
1299 with_var_unset("XDG_PUBLICSHARE_DIR", || {
1300 let result = publicshare();
1301 assert_eq!(result, Some(PathBuf::from("C:\\Users\\Public")));
1302 });
1303 }
1304 }
1305 }
1306
1307 mod runtime {
1308 use temp_env::{with_var, with_var_unset};
1309
1310 use super::*;
1311
1312 #[test]
1313 fn respects_xdg_runtime_dir() {
1314 let test_path = if cfg!(windows) {
1315 "C:\\test\\runtime"
1316 } else {
1317 "/test/runtime"
1318 };
1319 with_var("XDG_RUNTIME_DIR", Some(test_path), || {
1320 let result = runtime();
1321 assert_eq!(result, Some(PathBuf::from(test_path)));
1322 });
1323 }
1324
1325 #[test]
1326 fn ignores_relative_xdg_runtime_dir() {
1327 with_var("XDG_RUNTIME_DIR", Some("relative/runtime"), || {
1328 let result = runtime();
1329 if let Some(path) = result {
1330 assert!(path.is_absolute());
1331 }
1332 });
1333 }
1334
1335 #[test]
1336 fn uses_platform_default_when_xdg_unset() {
1337 with_var_unset("XDG_RUNTIME_DIR", || {
1338 let result = runtime();
1339 if let Some(runtime_path) = result {
1340 assert!(runtime_path.is_absolute());
1341
1342 #[cfg(any(target_os = "linux", target_os = "macos"))]
1343 {
1344 let path_str = runtime_path.to_string_lossy();
1345 assert!(path_str.contains("tmp") || path_str.starts_with("/var/folders"));
1346 }
1347
1348 #[cfg(target_os = "windows")]
1349 {
1350 if let Ok(temp) = env::var("TEMP") {
1351 assert_eq!(runtime_path, PathBuf::from(temp));
1352 }
1353 }
1354 }
1355 });
1356 }
1357
1358 #[test]
1359 fn falls_back_to_tmp_on_unix() {
1360 #[cfg(any(target_os = "linux", target_os = "macos"))]
1361 {
1362 with_var_unset("XDG_RUNTIME_DIR", || {
1363 with_var_unset("TMPDIR", || {
1364 let result = runtime();
1365 assert_eq!(result, Some(PathBuf::from("/tmp")));
1366 });
1367 });
1368 }
1369 }
1370 }
1371
1372 mod state_home {
1373 use temp_env::{with_var, with_var_unset};
1374
1375 use super::*;
1376
1377 #[test]
1378 fn respects_xdg_state_home() {
1379 let test_path = if cfg!(windows) {
1380 "C:\\test\\state"
1381 } else {
1382 "/test/state"
1383 };
1384 with_var("XDG_STATE_HOME", Some(test_path), || {
1385 let result = state_home();
1386 assert_eq!(result, Some(PathBuf::from(test_path)));
1387 });
1388 }
1389
1390 #[test]
1391 fn ignores_relative_xdg_state_home() {
1392 with_var("XDG_STATE_HOME", Some("relative/state"), || {
1393 let result = state_home();
1394 if let Some(path) = result {
1395 assert!(path.is_absolute());
1396 }
1397 });
1398 }
1399
1400 #[test]
1401 fn uses_platform_default_when_xdg_unset() {
1402 with_var_unset("XDG_STATE_HOME", || {
1403 let result = state_home();
1404 if let Some(state_path) = result {
1405 assert!(state_path.is_absolute());
1406
1407 #[cfg(target_os = "linux")]
1408 assert!(state_path.to_string_lossy().ends_with(".local/state"));
1409
1410 #[cfg(target_os = "macos")]
1411 assert!(state_path.to_string_lossy().contains("Library/Application Support"));
1412
1413 #[cfg(target_os = "windows")]
1414 {
1415 if let Ok(localappdata) = env::var("LOCALAPPDATA") {
1416 assert_eq!(state_path, PathBuf::from(localappdata));
1417 }
1418 }
1419 }
1420 });
1421 }
1422 }
1423
1424 mod templates {
1425 use temp_env::{with_var, with_var_unset};
1426
1427 use super::*;
1428
1429 #[test]
1430 fn respects_xdg_templates_dir() {
1431 let test_path = if cfg!(windows) {
1432 "C:\\test\\templates"
1433 } else {
1434 "/test/templates"
1435 };
1436 with_var("XDG_TEMPLATES_DIR", Some(test_path), || {
1437 let result = templates();
1438 assert_eq!(result, Some(PathBuf::from(test_path)));
1439 });
1440 }
1441
1442 #[test]
1443 fn ignores_relative_xdg_templates_dir() {
1444 with_var("XDG_TEMPLATES_DIR", Some("relative/templates"), || {
1445 let result = templates();
1446 if let Some(path) = result {
1447 assert!(path.is_absolute());
1448 }
1449 });
1450 }
1451
1452 #[test]
1453 fn uses_platform_default_when_xdg_unset() {
1454 with_var_unset("XDG_TEMPLATES_DIR", || {
1455 let result = templates();
1456 if let Some(templates_path) = result {
1457 assert!(templates_path.is_absolute());
1458 assert!(templates_path.to_string_lossy().ends_with("Templates"));
1459 }
1460 });
1461 }
1462 }
1463
1464 mod videos {
1465 use temp_env::{with_var, with_var_unset};
1466
1467 use super::*;
1468
1469 #[test]
1470 fn respects_xdg_videos_dir() {
1471 let test_path = if cfg!(windows) {
1472 "C:\\test\\videos"
1473 } else {
1474 "/test/videos"
1475 };
1476 with_var("XDG_VIDEOS_DIR", Some(test_path), || {
1477 let result = videos();
1478 assert_eq!(result, Some(PathBuf::from(test_path)));
1479 });
1480 }
1481
1482 #[test]
1483 fn ignores_relative_xdg_videos_dir() {
1484 with_var("XDG_VIDEOS_DIR", Some("relative/videos"), || {
1485 let result = videos();
1486 if let Some(path) = result {
1487 assert!(path.is_absolute());
1488 }
1489 });
1490 }
1491
1492 #[test]
1493 fn uses_platform_default_when_xdg_unset() {
1494 with_var_unset("XDG_VIDEOS_DIR", || {
1495 let result = videos();
1496 if let Some(videos_path) = result {
1497 assert!(videos_path.is_absolute());
1498
1499 #[cfg(target_os = "linux")]
1500 assert!(videos_path.to_string_lossy().ends_with("Videos"));
1501
1502 #[cfg(target_os = "macos")]
1503 assert!(videos_path.to_string_lossy().ends_with("Movies"));
1504
1505 #[cfg(target_os = "windows")]
1506 assert!(videos_path.to_string_lossy().ends_with("Videos"));
1507 }
1508 });
1509 }
1510 }
1511
1512 macro_rules! xdg_or_tests {
1513 ($mod_name:ident, $fn_name:ident, $xdg_var:expr) => {
1514 mod $mod_name {
1515 use temp_env::{with_var, with_var_unset};
1516
1517 use super::*;
1518
1519 #[test]
1520 fn returns_xdg_value_when_set() {
1521 let test_path = if cfg!(windows) {
1522 "C:\\test\\xdg_or"
1523 } else {
1524 "/test/xdg_or"
1525 };
1526 with_var($xdg_var, Some(test_path), || {
1527 let result = $fn_name(PathBuf::from("/fallback"));
1528 assert_eq!(result, PathBuf::from(test_path));
1529 });
1530 }
1531
1532 #[test]
1533 fn returns_default_when_xdg_unset() {
1534 let fallback = if cfg!(windows) {
1535 "C:\\fallback"
1536 } else {
1537 "/fallback"
1538 };
1539 with_var_unset($xdg_var, || {
1540 let result = $fn_name(fallback);
1541 assert_eq!(result, PathBuf::from(fallback));
1542 });
1543 }
1544
1545 #[test]
1546 fn returns_default_when_xdg_relative() {
1547 let fallback = if cfg!(windows) {
1548 "C:\\fallback"
1549 } else {
1550 "/fallback"
1551 };
1552 with_var($xdg_var, Some("relative/path"), || {
1553 let result = $fn_name(fallback);
1554 assert_eq!(result, PathBuf::from(fallback));
1555 });
1556 }
1557 }
1558 };
1559 }
1560
1561 xdg_or_tests!(xdg_bin_home_or, xdg_bin_home_or, "XDG_BIN_HOME");
1562 xdg_or_tests!(xdg_cache_home_or, xdg_cache_home_or, "XDG_CACHE_HOME");
1563 xdg_or_tests!(xdg_config_home_or, xdg_config_home_or, "XDG_CONFIG_HOME");
1564 xdg_or_tests!(xdg_data_home_or, xdg_data_home_or, "XDG_DATA_HOME");
1565 xdg_or_tests!(xdg_desktop_dir_or, xdg_desktop_dir_or, "XDG_DESKTOP_DIR");
1566 xdg_or_tests!(xdg_documents_dir_or, xdg_documents_dir_or, "XDG_DOCUMENTS_DIR");
1567 xdg_or_tests!(xdg_download_dir_or, xdg_download_dir_or, "XDG_DOWNLOAD_DIR");
1568 xdg_or_tests!(xdg_music_dir_or, xdg_music_dir_or, "XDG_MUSIC_DIR");
1569 xdg_or_tests!(xdg_pictures_dir_or, xdg_pictures_dir_or, "XDG_PICTURES_DIR");
1570 xdg_or_tests!(xdg_publicshare_dir_or, xdg_publicshare_dir_or, "XDG_PUBLICSHARE_DIR");
1571 xdg_or_tests!(xdg_runtime_dir_or, xdg_runtime_dir_or, "XDG_RUNTIME_DIR");
1572 xdg_or_tests!(xdg_state_home_or, xdg_state_home_or, "XDG_STATE_HOME");
1573 xdg_or_tests!(xdg_templates_dir_or, xdg_templates_dir_or, "XDG_TEMPLATES_DIR");
1574 xdg_or_tests!(xdg_videos_dir_or, xdg_videos_dir_or, "XDG_VIDEOS_DIR");
1575}