Skip to main content

systemprompt_cli/commands/core/files/ai/
show.rs

1//! `core files ai show` command.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use anyhow::{Result, anyhow};
7use clap::Args;
8use systemprompt_database::DbPool;
9use systemprompt_files::{FileRepository, TypeSpecificMetadata};
10use systemprompt_identifiers::FileId;
11use systemprompt_runtime::AppContext;
12
13use crate::CliConfig;
14use crate::commands::core::files::types::{
15    ChecksumsOutput, FileDetailOutput, FileMetadataOutput, ImageMetadataOutput,
16};
17use crate::shared::CommandOutput;
18
19#[derive(Debug, Clone, Args)]
20pub struct ShowArgs {
21    #[arg(value_name = "FILE_ID", help = "AI image file ID (UUID format)")]
22    pub file: String,
23}
24
25pub(super) async fn execute(args: ShowArgs, config: &CliConfig) -> Result<CommandOutput> {
26    let ctx = AppContext::new().await?;
27    execute_with_pool(args, ctx.db_pool(), config).await
28}
29
30pub async fn execute_with_pool(
31    args: ShowArgs,
32    pool: &DbPool,
33    _config: &CliConfig,
34) -> Result<CommandOutput> {
35    let file_id = parse_file_id(&args.file)?;
36
37    let service = FileRepository::new(pool)?;
38
39    let file = service
40        .find_by_id(&file_id)
41        .await?
42        .ok_or_else(|| anyhow!("File not found: {}", args.file))?;
43
44    if !file.ai_content {
45        return Err(anyhow!(
46            "File '{}' is not an AI-generated image. Use 'files show' for regular files.",
47            args.file
48        ));
49    }
50
51    let metadata_output = convert_metadata(&file);
52
53    let output = FileDetailOutput {
54        id: file.id(),
55        path: file.path,
56        public_url: file.public_url,
57        mime_type: file.mime_type,
58        size_bytes: file.size_bytes,
59        ai_content: file.ai_content,
60        user_id: file.user_id,
61        session_id: file.session_id,
62        trace_id: file.trace_id,
63        context_id: file.context_id,
64        metadata: metadata_output,
65        created_at: file.created_at,
66        updated_at: file.updated_at,
67    };
68
69    Ok(CommandOutput::card_value(
70        format!("AI Image: {}", args.file),
71        &output,
72    ))
73}
74
75fn parse_file_id(id: &str) -> Result<FileId> {
76    uuid::Uuid::parse_str(id).map_err(|_e| {
77        anyhow!(
78            "Invalid file ID format. Expected UUID like 'b75940ac-c50f-4d46-9fdd-ebb4970b2a7d', \
79             got '{}'",
80            id
81        )
82    })?;
83    Ok(FileId::new(id.to_owned()))
84}
85
86fn convert_metadata(file: &systemprompt_files::File) -> FileMetadataOutput {
87    let metadata = file.metadata.0.clone();
88
89    let checksums = metadata.checksums.map(|c| ChecksumsOutput {
90        md5: c.md5,
91        sha256: c.sha256,
92    });
93
94    let image = match metadata.type_specific {
95        Some(TypeSpecificMetadata::Image(img)) => Some(ImageMetadataOutput {
96            width: img.width,
97            height: img.height,
98            alt_text: img.alt_text,
99            description: img.description,
100        }),
101        _ => None,
102    };
103
104    FileMetadataOutput {
105        checksums,
106        image,
107        document: None,
108        audio: None,
109        video: None,
110    }
111}