openpencil/crates/op-cli/src/figma_cli.rs
Kayshen-X 0fc6753dd9 refactor(cli,mcp,web): typed error enums for the densest stringly paths
CliError/SkillInstallError carry every op-cli failure (94 sites),
ProgramError types batch_program (14), WebCanvasError maps route
failures onto their existing HTTP statuses (23 -> 6 deliberate
boundary adapters). Display output is byte-identical to the old
strings so caller-visible text and tests are unchanged.
2026-07-26 13:20:59 +08:00

72 lines
2.4 KiB
Rust

use std::path::Path;
use serde_json::json;
use super::{flag_value, required_pos, Command, Flags};
use crate::cli_error::CliError;
pub(super) fn map_import_figma(positionals: &[String], flags: &Flags) -> Result<Command, CliError> {
let fig_path = required_pos(
positionals,
1,
"Usage: op import:figma <file.fig> [--out output.op]",
)?;
let out_path = flag_value(flags, "out").unwrap_or_else(|| figma_default_out_path(&fig_path));
Ok(Command::ImportFigma { fig_path, out_path })
}
pub(super) fn figma_default_out_path(fig_path: &str) -> String {
fig_path
.strip_suffix(".fig")
.map(|base| format!("{base}.op"))
.unwrap_or_else(|| fig_path.to_string())
}
pub(super) fn run_import_figma(fig_path: &str, out_path: &str) -> Result<String, CliError> {
let bytes =
std::fs::read(fig_path).map_err(|e| CliError::Io(format!("read {fig_path:?}: {e}")))?;
let file_name = Path::new(fig_path)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("Figma Import");
let import = op_figma::parse_fig_binary(&bytes, file_name, op_figma::FigLayoutMode::OpenPencil)
.map_err(|e| CliError::Document(format!("import {fig_path:?}: {e}")))?;
// Dedup shared image payloads into the `images` table — an
// image-heavy `.fig` references the same bitmap from many fills,
// and the inline form writes one full copy per reference.
let mut value = serde_json::to_value(&import.document)
.map_err(|e| CliError::Payload(format!("serialize {out_path:?}: {e}")))?;
jian_ops_schema::image_table::externalize_images(&mut value);
let raw = value.to_string();
std::fs::write(out_path, raw).map_err(|e| CliError::Io(format!("write {out_path:?}: {e}")))?;
let page_count = import.document.pages.as_ref().map_or(1, Vec::len);
let node_count = import
.document
.pages
.as_ref()
.map(|pages| pages.iter().map(|p| p.children.len()).sum())
.unwrap_or_else(|| import.document.children.len());
Ok(import_figma_success_json(
out_path,
page_count,
node_count,
&import.warnings,
))
}
fn import_figma_success_json(
file_path: &str,
page_count: usize,
node_count: usize,
warnings: &[String],
) -> String {
json!({
"ok": true,
"filePath": file_path,
"pageCount": page_count,
"nodeCount": node_count,
"warnings": warnings,
})
.to_string()
}