Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Goodbye v1.0 Hello v2.0 #63

Merged
merged 33 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
130e6ee
Bump version
CosmicHorrorDev Mar 18, 2023
a58b41b
Fully switch from `steamy-vdf` (#9)
CosmicHorrorDev Mar 28, 2023
8bbfe4c
Publish v2.0.0-alpha.0
CosmicHorrorDev Mar 29, 2023
c8ff46c
Experimental switch to iterator based api (#10)
CosmicHorrorDev May 21, 2023
226816c
Custom error type (#29)
CosmicHorrorDev Jun 25, 2023
766622b
Add additional Steam search paths for Linux (#31)
ethangreen-dev Jul 9, 2023
8d9d303
impl std::error::Error for Error {} (#32)
CosmicHorrorDev Jul 16, 2023
fa0d616
Remove steamid-ng feature (keep public API stable) (#33)
CosmicHorrorDev Jul 16, 2023
4c8d142
Add Snap support for linux (#30)
Jan200101 Aug 5, 2023
a2dc7b4
Get things back to compiling and passing (#38)
CosmicHorrorDev Nov 9, 2023
97c33db
Update dependencies (#39)
CosmicHorrorDev Nov 9, 2023
e0e0aad
Switch tests to run in isolated dummy steam installations (#40)
CosmicHorrorDev Nov 15, 2023
1258479
Drastically simplify test helpers (#41)
CosmicHorrorDev Nov 19, 2023
68d1c0a
Run `cargo test` in CI (#42)
CosmicHorrorDev Nov 19, 2023
0545cb7
Placate clippy (#43)
CosmicHorrorDev Nov 24, 2023
7d03f1a
Placate clippy on more platforms (#44)
CosmicHorrorDev Nov 24, 2023
1643e44
Run CI on more platforms/channels (#45)
CosmicHorrorDev Nov 24, 2023
0c1f73e
Error cleanup (#46)
CosmicHorrorDev Nov 24, 2023
d85a079
Unfocused polish (#47)
CosmicHorrorDev Nov 25, 2023
e1b1642
Rework `locate()` failures (#49)
CosmicHorrorDev Nov 27, 2023
27ae1ba
Port compat tool (#50)
CosmicHorrorDev Nov 30, 2023
9e9b358
Expose `Library::from_dir()` and `InstallDir::library_paths()` (#51)
CosmicHorrorDev Dec 8, 2023
586da4a
Another round of refactors (#52)
CosmicHorrorDev Dec 8, 2023
809b3bc
Provide more context on installation location failure (#53)
CosmicHorrorDev Dec 9, 2023
f1cb5f5
Prepare another alpha (#54)
CosmicHorrorDev Dec 15, 2023
9cfbe7e
Docs overhaul (#55)
CosmicHorrorDev Dec 16, 2023
a096441
Prepare the v2.0 beta release (#56)
CosmicHorrorDev Dec 16, 2023
84cee45
Remove need to use `tempfile` (#57)
CosmicHorrorDev Dec 16, 2023
09bb134
Make `app.last_updated` actually optional (#59)
CosmicHorrorDev Dec 19, 2023
fe42453
Fix wasm32 in general (#60)
CosmicHorrorDev Dec 23, 2023
d9f998a
Alias `lastupdated` for `last_updated` (#61)
CosmicHorrorDev Dec 23, 2023
5db0d19
Bump version to v2.0.0-beta.2 (#62)
CosmicHorrorDev Dec 23, 2023
e198680
Temporarily ignore broken test
CosmicHorrorDev Dec 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,41 @@ env:
CARGO_TERM_COLOR: always

jobs:
check:
runs-on: ${{ matrix.os }}
validation:
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
toolchain: [stable, beta]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Clippy
- name: Install ${{ matrix.toolchain }} toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
components: clippy, rustfmt
- name: Setup cache
uses: Swatinem/rust-cache@v2
- name: Commune with clippy
run: cargo clippy --all -- -D warnings

fmt:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run test suite
run: cargo test
- name: Check docs
env:
RUSTDOCFLAGS: -Dwarnings
run: cargo doc --all --no-deps

docs:
needs: fmt
wasm:
runs-on: ubuntu-latest
env:
RUSTDOCFLAGS: -Dwarnings
steps:
- uses: actions/checkout@v3
- name: Check docs
run: cargo doc --all --no-deps
- name: Install stable toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install wasm-pack
uses: taiki-e/install-action@wasm-pack
- name: Run wasm tests (`--no-default-features`)
run: wasm-pack test --node --no-default-features
- name: Run wasm tests (`--all-features`)
run: wasm-pack test --node --all-features
42 changes: 29 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "steamlocate"
version = "1.2.1"
version = "2.0.0-beta.2"
authors = ["William Venner <[email protected]>"]
edition = "2018"
repository = "https://github.com/WilliamVenner/steamlocate-rs"
Expand All @@ -10,22 +10,38 @@ readme = "README.md"
keywords = ["steam", "vdf", "appmanifest", "directory", "steamapps"]
categories = ["os", "hardware-support", "filesystem", "accessibility"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[features]
default = []
shortcuts_extras = ["crc"]
steamid_ng = ["steamid-ng"]
default = ["locate"]
locate = ["locate_backend"]

[dependencies]
steamy-vdf = "0.2"
keyvalues-parser = "0.1"
crc = "3.0"
keyvalues-parser = "0.2"
keyvalues-serde = "0.2"
serde = { version = "1.0.0", features = ["derive"] }
keyvalues-serde = "0.1"

crc = { version = "3.0", optional = true }
# Platform-specific dependencies used for locating the steam dir
[target."cfg(target_os=\"windows\")".dependencies]
locate_backend = { package = "winreg", version = "0.51", optional = true }
[target."cfg(not(target_os=\"windows\"))".dependencies]
locate_backend = { package = "dirs", version = "5", optional = true }

[dev-dependencies]
insta = { version = "1.34.0", features = ["ron"] }
wasm-bindgen-test = "0.3.39"

[[example]]
name = "appmanifest"
required-features = ["locate"]

steamid-ng = { version = "1", optional = true }
[[example]]
name = "overview"
required-features = ["locate"]

[target.'cfg(target_os="windows")'.dependencies]
winreg = "0.11"
[target.'cfg(not(target_os="windows"))'.dependencies]
dirs = "5"
[[example]]
name = "shortcuts"
required-features = ["locate"]
162 changes: 66 additions & 96 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,127 +5,97 @@

# steamlocate

A crate which efficiently locates any Steam application on the filesystem, and/or the Steam installation itself.
A crate which efficiently locates any Steam application on the filesystem,
and/or the Steam installation itself.

This crate is best used when you do not want to depend on the Steamworks API for your program. In some cases the Steamworks API may be more appropriate to use, in which case I recommend the fantastic [steamworks](https://github.com/Thinkofname/steamworks-rs) crate. You don't need to be a Steamworks partner to get installation directory locations from the Steamworks API.
This crate is best used when you do not want to depend on the Steamworks API
for your program. In some cases the Steamworks API may be more appropriate to
use, in which case I recommend the fantastic
[steamworks](https://github.com/Thinkofname/steamworks-rs) crate. You don't
need to be a Steamworks partner to get installation directory locations from
the Steamworks API.

**This crate supports Windows, macOS and Linux.**
# Using steamlocate

## Using steamlocate
Simply add to your [Cargo.toml](https://doc.rust-lang.org/cargo/reference/manifest.html) file:
```toml
[dependencies]
steamlocate = "0.*"
```

To use [steamid-ng](#steamid-ng-support) with steamlocate, add this to your [Cargo.toml](https://doc.rust-lang.org/cargo/reference/manifest.html) file:
```toml
[dependencies]
steamid-ng = "1.*"
Simply add `steamlocate` using
[`cargo`](https://doc.rust-lang.org/cargo/getting-started/installation.html).

[dependencies.steamlocate]
version = "0.*"
features = ["steamid_ng"]
```console
$ cargo add steamlocate
```

## Caching
All functions in this crate cache their results, meaning you can call them as many times as you like and they will always return the same reference.
## Feature flags

If you need to get uncached results, simply instantiate a new [SteamDir](https://docs.rs/steamlocate/*/steamlocate/struct.SteamDir.html).
Default: `locate`

## steamid-ng Support
This crate supports [steamid-ng](https://docs.rs/steamid-ng) and can automatically convert [SteamApp::last_user](struct.SteamApp.html#structfield.last_user) to a [SteamID](https://docs.rs/steamid-ng/*/steamid_ng/struct.SteamID.html) for you.
| Feature flag | Description |
| :---: | :--- |
| `locate` | Enables automatically detecting the Steam installation on supported platforms (currently Windows, MacOS, and Linux). Unsupported platforms will return a runtime error. |

To enable this support, [use the `steamid_ng` Cargo.toml feature](#using-steamlocate).
# Examples

## Examples
## Locate the Steam installation and a specific game

#### Locate the installed Steam directory
```rust
extern crate steamlocate;
use steamlocate::SteamDir;
The `SteamDir` is going to be your entrypoint into _most_ parts of the API.
After you locate it you can access related information.

match SteamDir::locate() {
Some(steamdir) => println!("{:#?}", steamdir),
None => panic!("Couldn't locate Steam on this computer!")
}
```rust,ignore
let steam_dir = steamlocate::SteamDir::locate()?;
println!("Steam installation - {}", steam_dir.path().display());
// ^^ prints something like `Steam installation - C:\Program Files (x86)\Steam`

const GMOD_APP_ID: u32 = 4_000;
let (garrys_mod, _lib) = steam_dir
.find_app(GMOD_APP_ID)?
.expect("Of course we have G Mod");
assert_eq!(garrys_mod.name.as_ref().unwrap(), "Garry's Mod");
println!("{garrys_mod:#?}");
// ^^ prints something like vv
```
```rust
SteamDir (
path: PathBuf: "C:\\Program Files (x86)\\Steam"
)
```rust,ignore
App {
app_id: 4_000,
install_dir: "GarrysMod",
name: Some("Garry's Mod"),
universe: Some(Public),
// much much more data
}
```

#### Locate an installed Steam app by its app ID
This will locate Garry's Mod anywhere on the filesystem.
```rust
extern crate steamlocate;
use steamlocate::SteamDir;
## Get an overview of all libraries and apps on the system

let mut steamdir = SteamDir::locate().unwrap();
match steamdir.app(&4000) {
Some(app) => println!("{:#?}", app),
None => panic!("Couldn't locate Garry's Mod on this computer!")
}
```
```rust
SteamApp (
appid: u32: 4000,
path: PathBuf: "C:\\Program Files (x86)\\steamapps\\common\\GarrysMod",
vdf: <steamy_vdf::Table>,
name: Some(String: "Garry's Mod"),
last_user: Some(u64: 76561198040894045)
)
```
You can iterate over all of Steam's libraries from the steam dir. Then from each library you
can iterate over all of its apps.

#### Locate all Steam apps on this filesystem
```rust
extern crate steamlocate;
use steamlocate::{SteamDir, SteamApp};
use std::collections::HashMap;
```rust,ignore
let steam_dir = steamlocate::SteamDir::locate()?;

let mut steamdir = SteamDir::locate().unwrap();
let apps: &HashMap<u32, Option<SteamApp>> = steamdir.apps();
for library in steam_dir.libraries()? {
let library = library?;
println!("Library - {}", library.path().display());

println!("{:#?}", apps);
```
```rust
{
4000: SteamApp (
appid: u32: 4000,
path: PathBuf: "C:\\Program Files (x86)\\steamapps\\common\\GarrysMod",
vdf: <steamy_vdf::Table>,
name: Some(String: "Garry's Mod"),
last_user: Some(u64: 76561198040894045)
)
...
for app in library.apps() {
let app = app?;
println!(" App {} - {:?}", app.app_id, app.name);
}
}
```

#### Locate all Steam library folders
```rust
extern crate steamlocate;
use steamlocate::{SteamDir, LibraryFolders};
use std::{vec, path::PathBuf};

let mut steamdir: SteamDir = SteamDir::locate().unwrap();
let libraryfolders: &LibraryFolders = steamdir.libraryfolders();
let paths: &Vec<PathBuf> = &libraryfolders.paths;

println!("{:#?}", paths);
```
```rust
{
"C:\\Program Files (x86)\\Steam\\steamapps",
"D:\\Steam\\steamapps",
"E:\\Steam\\steamapps",
"F:\\Steam\\steamapps",
...
}
On my laptop this prints

```text
Library - /home/wintermute/.local/share/Steam
App 1628350 - Steam Linux Runtime 3.0 (sniper)
App 1493710 - Proton Experimental
App 4000 - Garry's Mod
Library - /home/wintermute/temp steam lib
App 391540 - Undertale
App 1714040 - Super Auto Pets
App 2348590 - Proton 8.0
```

## Contribution

Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the MIT license,
shall be dual licensed as above, without any additional terms or conditions.
shall be licensed as above, without any additional terms or conditions.
19 changes: 19 additions & 0 deletions examples/appmanifest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::{env, process::exit};

use steamlocate::SteamDir;

fn main() {
let args: Vec<_> = env::args().collect();
if args.len() != 2 || args[1].parse::<u32>().is_err() {
eprintln!("Usage: cargo run --example appmanifest -- <STEAM_APP_ID>");
exit(1);
}
let app_id: u32 = args[1].parse().expect("<STEAM_APP_ID> should be a u32");

let steam_dir = SteamDir::locate().unwrap();
match steam_dir.find_app(app_id) {
Ok(Some((app, _library))) => println!("Found app - {:#?}", app),
Ok(None) => println!("No app found for {}", app_id),
Err(err) => println!("Failed reading app: {err}"),
}
}
26 changes: 26 additions & 0 deletions examples/overview.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use steamlocate::SteamDir;

fn main() {
let steamdir = SteamDir::locate().unwrap();
println!("Steam Dir - {:?}", steamdir.path());

// TODO: use `anyhow` to make error handling here simpler
for maybe_library in steamdir.libraries().unwrap() {
match maybe_library {
Err(err) => eprintln!("Failed reading library: {err}"),
Ok(library) => {
println!(" Library - {:?}", library.path());
for app in library.apps() {
match app {
Ok(app) => println!(
" App {} - {}",
app.app_id,
app.name.as_deref().unwrap_or("<no-name>")
),
Err(err) => println!(" Failed reading app: {err}"),
}
}
}
}
}
}
9 changes: 7 additions & 2 deletions examples/shortcuts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

fn main() {
let mut steamdir = steamlocate::SteamDir::locate().unwrap();
let shortcuts = steamdir.shortcuts();
println!("Shortcuts - {:#?}", shortcuts);
println!("Shortcuts:");
for maybe_shortcut in steamdir.shortcuts().unwrap() {
match maybe_shortcut {
Ok(shortcut) => println!(" - {} {}", shortcut.app_id, shortcut.app_name),
Err(err) => println!("Failed reading potential shortcut: {err}"),
}
}
}
Loading