//! `pic files ls | get | rm` — operator-facing files inspection (G2). //! //! Wraps the read-only `/api/v1/admin/apps/{id}/files*` surface. There is //! no `set`/`upload` — blob writes go through scripts (`files::create`); //! the admin surface is inspect + delete only, matching the dashboard. use std::io::Write; use std::path::Path; use anyhow::{Context, Result}; use crate::client::Client; use crate::config; use crate::output::{OutputMode, Table}; pub async fn ls(app: &str, collection: &str, limit: u32, mode: OutputMode) -> Result<()> { let creds = config::resolve()?; let client = Client::from_creds(&creds)?; let page = client.files_list(app, collection, limit).await?; let mut table = Table::new(["id", "name", "content_type", "size", "updated_at"]); for f in page.files { table.row([ f.id, f.name, f.content_type, f.size.to_string(), f.updated_at, ]); } table.print(mode); if page.next_cursor.is_some() { let _ = writeln!( std::io::stderr(), "(more results available — raise --limit to see them)" ); } Ok(()) } /// Download a file's bytes. With `--out ` writes to disk; otherwise /// streams raw bytes to stdout (pipe to a file or `xxd`). pub async fn get(app: &str, collection: &str, file_id: &str, out: Option<&Path>) -> Result<()> { let creds = config::resolve()?; let client = Client::from_creds(&creds)?; let bytes = client.files_get_bytes(app, collection, file_id).await?; match out { Some(path) => { std::fs::write(path, &bytes).with_context(|| format!("writing {}", path.display()))?; let _ = writeln!( std::io::stderr(), "Wrote {} bytes to {}", bytes.len(), path.display() ); } None => { std::io::stdout() .write_all(&bytes) .context("writing bytes to stdout")?; } } Ok(()) } pub async fn rm(app: &str, collection: &str, file_id: &str) -> Result<()> { let creds = config::resolve()?; let client = Client::from_creds(&creds)?; client.files_delete(app, collection, file_id).await?; println!("Deleted file {file_id} from {collection}"); Ok(()) }