Skip to main content

systemprompt_cli/commands/web/assets/
show.rs

1//! `web assets show` command including config references.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use anyhow::{Context, Result, anyhow};
7use chrono::{DateTime, Utc};
8use clap::Args;
9use std::fs;
10
11use crate::CliConfig;
12use crate::shared::CommandOutput;
13use systemprompt_config::ProfileBootstrap;
14
15use super::super::paths::WebPaths;
16use super::super::types::AssetDetailOutput;
17use super::asset_type::determine_asset_type;
18
19#[derive(Debug, Args)]
20pub struct ShowArgs {
21    #[arg(help = "Asset path (relative to assets directory)")]
22    pub path: String,
23}
24
25pub(super) fn execute(args: &ShowArgs, _config: &CliConfig) -> Result<CommandOutput> {
26    let profile = ProfileBootstrap::get().context("Failed to get profile")?;
27    let web_paths = WebPaths::resolve()?;
28    let assets_dir = &web_paths.assets;
29    let asset_path = assets_dir.join(&args.path);
30
31    if !asset_path.exists() {
32        return Err(anyhow!("Asset '{}' not found", args.path));
33    }
34
35    if !asset_path.is_file() {
36        return Err(anyhow!("'{}' is not a file", args.path));
37    }
38
39    let metadata = asset_path
40        .metadata()
41        .context("Failed to get file metadata")?;
42    let size_bytes = metadata.len();
43    let modified = metadata.modified().ok().map_or_else(
44        || "unknown".to_owned(),
45        |t| {
46            let datetime: DateTime<Utc> = t.into();
47            datetime.format("%Y-%m-%dT%H:%M:%SZ").to_string()
48        },
49    );
50
51    let asset_type = determine_asset_type(&asset_path, &args.path);
52    let referenced_in = find_config_references(&args.path, profile);
53
54    let output = AssetDetailOutput {
55        path: args.path.clone(),
56        absolute_path: asset_path.to_string_lossy().to_string(),
57        asset_type,
58        size_bytes,
59        modified,
60        referenced_in,
61    };
62
63    Ok(CommandOutput::card_value(
64        format!("Asset: {}", args.path),
65        &output,
66    ))
67}
68
69pub fn find_config_references(
70    asset_path: &str,
71    profile: &systemprompt_models::Profile,
72) -> Vec<String> {
73    let mut references = Vec::new();
74
75    let web_config_path = profile.paths.web_config();
76    if let Ok(content) = fs::read_to_string(&web_config_path) {
77        let search_patterns = [
78            format!("/assets/{}", asset_path),
79            format!("assets/{}", asset_path),
80            asset_path.to_owned(),
81        ];
82
83        for pattern in &search_patterns {
84            if content.contains(pattern) {
85                references.push(format!("web config: {}", web_config_path));
86                break;
87            }
88        }
89    }
90
91    let metadata_path = profile.paths.web_metadata();
92    if let Ok(content) = fs::read_to_string(&metadata_path) {
93        let search_patterns = [
94            format!("/assets/{}", asset_path),
95            format!("assets/{}", asset_path),
96            asset_path.to_owned(),
97        ];
98
99        for pattern in &search_patterns {
100            if content.contains(pattern) {
101                references.push(format!("metadata: {}", metadata_path));
102                break;
103            }
104        }
105    }
106
107    references
108}