rework cli interface

This commit is contained in:
Robin Appelman 2025-06-21 14:07:22 +02:00
commit 182b067fad
2 changed files with 72 additions and 34 deletions

View file

@ -6,7 +6,7 @@ hashes.
## Usage
```bash
cargo r -- <app id> --depot <depot id>
depot-prefetch fetch <app id> --depot <depot id>
```
`--depot` can be specified multiple times, or left out to fetch all available
@ -15,8 +15,13 @@ depots.
You can find depot id's for an app on
[steamdb](https://steamdb.info/app/232250/depots/).
If you only care about knowing the latest manifest, you can pass `--dont-fetch`
to disable pre-fetching the manifest's data.
If you only care about knowing the latest manifest, you can use
```bash
depot-prefetch manifest <app id> --depot <depot id>
```
instead.
## Output

View file

@ -1,15 +1,29 @@
use crate::prefetch::prefetch;
use crate::product_info::{ProductInfoError, ProductInfoFetcher};
use clap::Parser;
use crate::product_info::ProductInfoFetcher;
use main_error::MainResult;
use serde::Serialize;
use crate::prefetch::prefetch;
mod product_info;
mod error;
mod prefetch;
mod product_info;
#[derive(Debug, Parser)]
struct Args {
#[command(subcommand)]
command: Command,
}
#[derive(Debug, Parser)]
enum Command {
/// Get the latest manifest and pre-fetch it
Fetch(ProductArgs),
/// Get the latest manifest without fetching
Manifest(ProductArgs),
}
#[derive(Debug, Parser)]
struct ProductArgs {
/// App to prefetch depots for
app_id: u32,
/// Branch to prefetch, defaults to "public"
@ -18,8 +32,6 @@ struct Args {
/// Only prefetch the listed depot(s)
#[clap(long)]
depot: Vec<u32>,
#[clap(long)]
dont_fetch: bool,
}
#[tokio::main]
@ -27,37 +39,58 @@ async fn main() -> MainResult {
let args = Args::parse();
let info_fetcher = ProductInfoFetcher::new().await?;
let depots = info_fetcher.fetch_depots(args.app_id).await?;
match args.command {
Command::Fetch(args) => {
let inputs = get_manifests(&info_fetcher, &args).await?;
let outputs = inputs.into_iter()
.map(|input| {
prefetch(&input).map(|hash| ManifestOutput {
app_id: input.app_id,
depot_id: input.depot_id,
manifest: input.manifest,
hash,
})
})
.collect::<Result<Vec<_>, _>>()?;
let manifests = depots.into_iter()
.filter(|(depot_id, _)| args.depot.is_empty() || args.depot.contains(depot_id))
.flat_map(|(depot_id, depot)| depot.branches.into_iter().map(move |(branch, manifest)| (depot_id, branch, manifest)))
.filter(|(_, branch, _)| &args.branch == branch)
.map(|(depot_id, _, manifest)| ManifestInput {
app_id: args.app_id,
depot_id,
manifest: manifest.gid,
});
if args.dont_fetch {
let inputs = manifests.collect::<Vec<_>>();
serde_json::to_writer_pretty(std::io::stdout(), &inputs)?;
} else {
let outputs = manifests.map(|input| {
prefetch(&input).map(|hash| ManifestOutput {
app_id: input.app_id,
depot_id: input.depot_id,
manifest: input.manifest,
hash,
})
}).collect::<Result<Vec<_>, _>>()?;
serde_json::to_writer_pretty(std::io::stdout(), &outputs)?;
serde_json::to_writer_pretty(std::io::stdout(), &outputs)?;
}
Command::Manifest(args) => {
let inputs = get_manifests(&info_fetcher, &args).await?;
serde_json::to_writer_pretty(std::io::stdout(), &inputs)?;
}
}
Ok(())
}
async fn get_manifests(
info_fetcher: &ProductInfoFetcher,
product_args: &ProductArgs,
) -> Result<Vec<ManifestInput>, ProductInfoError> {
let depots = info_fetcher.fetch_depots(product_args.app_id).await?;
Ok(depots
.into_iter()
.filter(|(depot_id, _)| {
product_args.depot.is_empty() || product_args.depot.contains(depot_id)
})
.flat_map(|(depot_id, depot)| {
depot
.branches
.into_iter()
.map(move |(branch, manifest)| (depot_id, branch, manifest))
})
.filter(|(_, branch, _)| &product_args.branch == branch)
.map(|(depot_id, _, manifest)| ManifestInput {
app_id: product_args.app_id,
depot_id,
manifest: manifest.gid,
})
.collect())
}
#[derive(Debug, Serialize)]
struct ManifestInput {
app_id: u32,
@ -71,4 +104,4 @@ struct ManifestOutput {
depot_id: u32,
manifest: u64,
hash: String,
}
}