Skip to content

Commit

Permalink
Merge pull request #30 from utkarshgupta137/master
Browse files Browse the repository at this point in the history
bump windows-sys, switch to SHGetKnownFolderPath, bump to edition 2021, raise MSRV to 1.70.0
  • Loading branch information
utkarshgupta137 authored Feb 27, 2025
2 parents 6cc955a + bbf21d2 commit d8a9bb7
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 31 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
branches:
- master
tags:
- '*'
- "*"
workflow_dispatch:

jobs:
Expand Down Expand Up @@ -52,7 +52,7 @@ jobs:
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
toolchain: [nightly, stable, 1.54.0]
toolchain: [beta, stable, 1.70.0]

name: Test on ${{ matrix.os }} with Rust ${{ matrix.toolchain }}
runs-on: ${{ matrix.os }}
Expand Down
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
[package]
name = "etcetera"
version = "0.8.0"
categories = ["config"]
description = "An unopinionated library for obtaining configuration, data, cache, & other directories"
documentation = "https://docs.rs/etcetera"
edition = "2018"
edition = "2021"
homepage = "/~https://github.com/lunacookies/etcetera"
keywords = ["xdg", "dirs", "directories", "basedir", "path"]
license = "MIT OR Apache-2.0"
name = "etcetera"
readme = "README.md"
repository = "/~https://github.com/lunacookies/etcetera"
rust-version = "1.54.0"
version = "0.8.0"
rust-version = "1.70.0"
description = "An unopinionated library for obtaining configuration, data, cache, & other directories"

[dependencies]
cfg-if = "1"
home = "0.5"
home = ">=0.5,<0.5.11"

# We should keep this in sync with the `home` crate.
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.48", features = ["Win32_Foundation", "Win32_UI_Shell"] }
windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Com", "Win32_UI_Shell"] }
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@ This is a Rust library that allows you to determine the locations of configurati
Existing Rust libraries generally do not give you a choice in terms of which standards/conventions they follow.
Etcetera, on the other hand, gives you the choice.

MSRV: 1.54.0
MSRV: 1.70.0

## Conventions

Etcetera supports the following conventions:

- the [XDG base directory](https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html)
- Apple's [Standard Directories](https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html)
- Window's [Known Folder Locations](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid)
- the "Unix Single-folder Convention" i.e. everything in `~/.myapp`

## Strategies

Etcetera has 2 modes of operation: `BaseStrategy` & `AppStrategy`:

- With `BaseStrategy`, you just get the location of the respective directory. For eg. for `config_dir()`:
- XDG: `~/.config`
- Apple: `~/Library/Preferences`
Expand All @@ -34,11 +38,14 @@ Etcetera has 2 modes of operation: `BaseStrategy` & `AppStrategy`:
Note: the location of the home (~) is determined by the [`home`](https://docs.rs/home/0.5.4/home/fn.home_dir.html) crate.

### Convenience functions

Etcetera also provides convenience functions for selecting the appropriate strategy on each platform:

- `base_strategy::choose_base_strategy` & `app_strategy::choose_app_strategy`: Uses `Windows` on Windows & `XDG` everywhere else.
This is used by most CLI tools & some GUI tools on each platform.
- `base_strategy::choose_native_strategy` & `app_strategy::choose_native_strategy`: Uses `Windows` on Windows, `Apple` on macOS/iOS, & `XDG` everywhere else.
This is used by most GUI applications on each platform.

##
##

See the ![documentation](https://docs.rs/etcetera) for examples.
2 changes: 1 addition & 1 deletion src/app_strategy/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
/// This strategy follows Windows’ conventions. It seems that all Windows GUI apps, and some command-line ones follow this pattern. The specification is available [here](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
///
/// This initial example removes all the relevant environment variables to show the strategy’s use of the:
/// - (on Windows) SHGetFolderPathW API.
/// - (on Windows) SHGetKnownFolderPath API.
/// - (on non-Windows) Windows default directories.
///
/// ```
Expand Down
53 changes: 34 additions & 19 deletions src/base_strategy/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::HomeDirError;
/// This strategy follows Windows’ conventions. It seems that all Windows GUI apps, and some command-line ones follow this pattern. The specification is available [here](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
///
/// This initial example removes all the relevant environment variables to show the strategy’s use of the:
/// - (on Windows) SHGetFolderPathW API.
/// - (on Windows) SHGetKnownFolderPath API.
/// - (on non-Windows) Windows default directories.
///
/// ```
Expand Down Expand Up @@ -108,8 +108,6 @@ pub struct Windows {
home_dir: PathBuf,
}

// Ref: /~https://github.com/rust-lang/cargo/blob/home-0.5.5/crates/home/src/windows.rs
// We should keep this code in sync with the above.
impl Windows {
/// Create a new Windows BaseStrategy
pub fn new() -> Result<Self, HomeDirError> {
Expand All @@ -125,39 +123,56 @@ impl Windows {
.or_else(|| Self::dir_crt(env))
}

#[cfg(all(windows, target_vendor = "uwp"))]
// Ref: /~https://github.com/rust-lang/cargo/blob/home-0.5.11/crates/home/src/windows.rs
// We should keep this code in sync with the above.
#[cfg(all(windows, not(target_vendor = "uwp")))]
fn dir_crt(env: &'static str) -> Option<PathBuf> {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;

use windows_sys::Win32::Foundation::{MAX_PATH, S_OK};
use windows_sys::Win32::UI::Shell::{SHGetFolderPathW, CSIDL_APPDATA, CSIDL_LOCAL_APPDATA};

let csidl = match env {
"APPDATA" => CSIDL_APPDATA,
"LOCALAPPDATA" => CSIDL_LOCAL_APPDATA,
_ => return None,
use std::ptr;
use std::slice;

use windows_sys::Win32::Foundation::S_OK;
use windows_sys::Win32::System::Com::CoTaskMemFree;
use windows_sys::Win32::UI::Shell::{
FOLDERID_LocalAppData, FOLDERID_RoamingAppData, SHGetKnownFolderPath,
KF_FLAG_DONT_VERIFY,
};

extern "C" {
fn wcslen(buf: *const u16) -> usize;
}

let folder_id = match env {
"APPDATA" => FOLDERID_RoamingAppData,
"LOCALAPPDATA" => FOLDERID_LocalAppData,
_ => return None,
};

unsafe {
let mut path: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
match SHGetFolderPathW(0, csidl, 0, 0, path.as_mut_ptr()) {
let mut path = ptr::null_mut();
match SHGetKnownFolderPath(
&folder_id,
KF_FLAG_DONT_VERIFY as u32,
std::ptr::null_mut(),
&mut path,
) {
S_OK => {
let len = wcslen(path.as_ptr());
path.set_len(len);
let s = OsString::from_wide(&path);
let path_slice = slice::from_raw_parts(path, wcslen(path));
let s = OsString::from_wide(path_slice);
CoTaskMemFree(path.cast());
Some(PathBuf::from(s))
}
_ => None,
_ => {
// Free any allocated memory even on failure. A null ptr is a no-op for `CoTaskMemFree`.
CoTaskMemFree(path.cast());
None
}
}
}
}

#[cfg(not(all(windows, target_vendor = "uwp")))]
#[cfg(not(all(windows, not(target_vendor = "uwp"))))]
fn dir_crt(_env: &'static str) -> Option<PathBuf> {
None
}
Expand Down

0 comments on commit d8a9bb7

Please sign in to comment.