Skip to content

Commit

Permalink
docs: Sprinkle more documentation around
Browse files Browse the repository at this point in the history
  • Loading branch information
CosmicHorrorDev committed Oct 25, 2024
1 parent 44e8a3f commit 07b206a
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 4 deletions.
66 changes: 66 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
//! All of the data available from parsing [App] manifest files
//!
//! This contains the definition of [`App`] and all of the types used within it
//!
//! Fundamentally an [`App`] is contained within a [`Library`], but there are a variety of helpers
//! that make locating an app easier. Namely:
//!
//! - [SteamDir::find_app()][crate::SteamDir::find_app]
//! - Searches through all of the libraries to locate an app by ID
//! - [Library::app()]
//! - Searches this specific library for an app by ID
//! - [Library::apps()]
//! - Iterates over all of the apps contained in this library
use std::{
collections::BTreeMap,
fs,
Expand All @@ -12,6 +26,9 @@ use crate::{

use serde::{Deserialize, Deserializer};

/// An [`Iterator`] over a [`Library`]'s [`App`]s
///
/// Returned from calling [`Library::apps()`]
pub struct Iter<'library> {
library: &'library Library,
app_ids: slice::Iter<'library, u32>,
Expand Down Expand Up @@ -41,6 +58,55 @@ impl Iterator for Iter<'_> {
}

/// Metadata for an installed Steam app
///
/// _See the [module level docs][self] for different ways to get an [`App`]_
///
/// All of the information contained within the `appmanifest_<APP_ID>.acf` file. For instance
///
/// ```vdf
/// "AppState"
/// {
/// "appid" "599140"
/// "installdir" "Graveyard Keeper"
/// "name" "Graveyard Keeper"
/// "LastOwner" "12312312312312312"
/// "Universe" "1"
/// "StateFlags" "6"
/// "LastUpdated" "1672176869"
/// "UpdateResult" "0"
/// "SizeOnDisk" "1805798572"
/// "buildid" "8559806"
/// "BytesToDownload" "24348080"
/// "BytesDownloaded" "0"
/// "TargetBuildID" "8559806"
/// "AutoUpdateBehavior" "1"
/// }
/// ```
///
/// gets parsed as
///
/// ```ignore
/// App {
/// app_id: 599140,
/// install_dir: "Graveyard Keeper",
/// name: Some("Graveyard Keeper"),
/// last_user: Some(12312312312312312),
/// universe: Some(Public),
/// state_flags: Some(StateFlags(6)),
/// last_updated: Some(SystemTime {
/// tv_sec: 1672176869,
/// tv_nsec: 0,
/// }),
/// update_result: Some(0),
/// size_on_disk: Some(1805798572),
/// build_id: Some(8559806),
/// bytes_to_download: Some(24348080),
/// bytes_downloaded: Some(0),
/// target_build_id: Some(8559806),
/// auto_update_behavior: Some(OnlyUpdateOnLaunch),
/// // ...
/// }
/// ```
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[cfg_attr(test, derive(serde::Serialize))]
#[non_exhaustive]
Expand Down
54 changes: 51 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,29 @@ impl SteamDir {
library::parse_library_paths(&libraryfolders_vdf)
}

/// Returns an [`Iterator`] over all the [`Library`]s believed to be part of this installation
///
/// For reasons akin to [`std::fs::read_dir()`] this method both returns a [`Result`] and
/// returns [`Result`]s for the iterator's items.
///
/// # Example
///
/// ```
/// # /*
/// let steam_dir = SteamDir::locate()?;
/// # */
/// # use steamlocate::__private_tests::prelude::*;
/// # let temp_steam_dir = expect_test_env();
/// # let steam_dir = temp_steam_dir.steam_dir();
/// let num_apps: usize = steam_dir
/// .libraries()?
/// .filter_map(Result::ok)
/// .map(|lib| lib.app_ids().len())
/// .sum();
/// println!("Wow you have {num_apps} installed!");
/// # assert_eq!(num_apps, 3);
/// # Ok::<_, TestError>(())
/// ```
pub fn libraries(&self) -> Result<library::Iter> {
let paths = self.library_paths()?;
Ok(library::Iter::new(paths))
Expand All @@ -182,6 +205,7 @@ impl SteamDir {
/// Convenient helper to look through all the libraries for a specific app
///
/// # Example
///
/// ```
/// # use steamlocate::__private_tests::prelude::*;
/// # let temp_steam_dir = expect_test_env();
Expand All @@ -206,6 +230,7 @@ impl SteamDir {
.transpose()
}

// TODO: `Iterator`ify this
pub fn compat_tool_mapping(&self) -> Result<HashMap<u32, CompatTool>> {
let config_path = self.path.join("config").join("config.vdf");
let vdf_text =
Expand Down Expand Up @@ -243,8 +268,25 @@ impl SteamDir {
shortcut::Iter::new(&self.path)
}

// TODO: rename to `from_dir()` and make consitent with similar constructors on other structs
pub fn from_dir(path: &Path) -> Result<SteamDir> {
/// Attempt to create a [`SteamDir`] from its installation directory
///
/// When possible you should prefer using [`SteamDir::locate()`]
///
/// # Example
///
/// ```
/// # use steamlocate::SteamDir;
/// # use steamlocate::__private_tests::prelude::*;
/// # let temp_steam_dir = expect_test_env();
/// # let steam_dir = temp_steam_dir.steam_dir();
/// # /*
/// let steam_dir = SteamDir::locate()?;
/// # */
/// let steam_path = steam_dir.path();
/// let still_steam_dir = SteamDir::from_dir(steam_path).expect("We just located it");
/// assert_eq!(still_steam_dir.path(), steam_path);
/// ```
pub fn from_dir(path: &Path) -> Result<Self> {
if !path.is_dir() {
return Err(Error::validation(ValidationError::missing_dir()));
}
Expand All @@ -257,8 +299,14 @@ impl SteamDir {
}

/// Attempts to locate the Steam installation directory on the system
///
/// See the [example on the struct docs][Self#example]
///
/// Uses platform specific operations to locate the Steam directory. Currently the supported
/// platforms are Windows, MacOS, and Linux while other platforms return an
/// [`LocateError::Unsupported`][error::LocateError::Unsupported]
#[cfg(feature = "locate")]
pub fn locate() -> Result<SteamDir> {
pub fn locate() -> Result<Self> {
let path = locate::locate_steam_dir()?;

Self::from_dir(&path)
Expand Down
76 changes: 76 additions & 0 deletions src/library.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! Functionality related to Steam [`Library`]s and related types
//!
//! [`Library`]s are obtained from either [`SteamDir::libraries()`][super::SteamDir::libraries],
//! [`SteamDir::find_app()`][super::SteamDir::find_app], or located manually with
//! [`Library::from_dir()`].
use std::{
fs,
path::{Path, PathBuf},
Expand Down Expand Up @@ -74,6 +80,9 @@ pub(crate) fn parse_library_paths(path: &Path) -> Result<Vec<PathBuf>> {
Ok(paths)
}

/// An [`Iterator`] over a Steam installation's [`Library`]s
///
/// Returned from calling [`SteamDir::libraries()`][super::SteamDir::libraries]
pub struct Iter {
paths: std::vec::IntoIter<PathBuf>,
}
Expand All @@ -100,13 +109,19 @@ impl ExactSizeIterator for Iter {
}
}

/// A steam library containing various installed [`App`]s
#[derive(Clone, Debug)]
pub struct Library {
path: PathBuf,
apps: Vec<u32>,
}

impl Library {
/// Attempt to create a [`Library`] directly from its installation directory
///
/// You'll typically want to use methods that handle locating the library for you like
/// [`SteamDir::libraries()`][super::SteamDir::libraries] or
/// [`SteamDir::find_app()`][super::SteamDir::find_app].
pub fn from_dir(path: &Path) -> Result<Self> {
// Read the manifest files at the library to get an up-to-date list of apps since the
// values in `libraryfolders.vdf` may be stale
Expand All @@ -131,14 +146,51 @@ impl Library {
})
}

/// Returns the path to the library's installation directory
///
/// # Example
///
/// ```
/// # use steamlocate::__private_tests::prelude::*;
/// # let temp_steam_dir = expect_test_env();
/// # let steam_dir = temp_steam_dir.steam_dir();
/// # let library = steam_dir.libraries().unwrap().next().unwrap().unwrap();
/// # /*
/// let library = /* Somehow get a library */;
/// # */
/// let path = library.path();
/// assert!(path.join("steamapps").is_dir());
/// ```
pub fn path(&self) -> &Path {
&self.path
}

/// Returns the full list of Application IDs located within this library
pub fn app_ids(&self) -> &[u32] {
&self.apps
}

/// Attempts to return the [`App`] identified by `app_id`
///
/// Returns [`None`] if the app isn't located within this library. Otherwise it attempts to
/// return metadata for the installed app
///
/// # Example
///
/// ```
/// # use steamlocate::__private_tests::prelude::*;
/// # let temp_steam_dir = expect_test_env();
/// # let steam_dir = temp_steam_dir.steam_dir();
/// # let library = steam_dir.libraries()?.next().unwrap()?;
/// const GMOD: u32 = 4_000;
/// # /*
/// let library = /* Somehow get a library */;
/// # */
/// let gmod = library.app(GMOD).expect("Of course we have gmod")?;
/// assert_eq!(gmod.app_id, GMOD);
/// assert_eq!(gmod.name.unwrap(), "Garry's Mod");
/// # Ok::<_, TestError>(())
/// ```
pub fn app(&self, app_id: u32) -> Option<Result<App>> {
self.app_ids().iter().find(|&&id| id == app_id).map(|&id| {
let manifest_path = self
Expand All @@ -149,6 +201,30 @@ impl Library {
})
}

/// Returns an [`Iterator`] over all of the [`App`]s contained in this library
///
/// # Example
///
/// ```
/// # use steamlocate::__private_tests::prelude::*;
/// # let temp_steam_dir = expect_test_env();
/// # let steam_dir = temp_steam_dir.steam_dir();
/// # let library = steam_dir.libraries()?.next().unwrap()?;
/// # /*
/// let library = /* Somehow get a library */;
/// # */
/// let total_size: u64 = library
/// .apps()
/// .filter_map(Result::ok)
/// .filter_map(|app| app.bytes_downloaded)
/// .sum();
/// println!(
/// "Library {} takes up {} bytes",
/// library.path().display(), total_size,
/// );
/// # assert_eq!(total_size, 30804429728);
/// # Ok::<_, TestError>(())
/// ```
pub fn apps(&self) -> app::Iter {
app::Iter::new(self)
}
Expand Down
8 changes: 7 additions & 1 deletion src/shortcut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use crate::{
Error, Result,
};

/// A added non-Steam game
// TODO: refactor this to remove storing the `steam_id` and instead make it a method that
// calculates on demand. That fixes some API issues and more directly represents the underlying
// data. This also means that `fn new()` can be removed
/// A non-Steam game that has been added to Steam
///
/// Information is parsed from your `userdata/<user_id>/config/shortcuts.vdf` files
#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -58,6 +61,9 @@ impl Shortcut {
}
}

/// An [`Iterator`] over a Steam installation's [`Shortcut`]s
///
/// Returned from calling [`SteamDir::shortcuts()`][super::SteamDir::shortcuts]
pub struct Iter {
dir: PathBuf,
read_dir: fs::ReadDir,
Expand Down

0 comments on commit 07b206a

Please sign in to comment.