用 Rust 写一个前端项目辅助工具:JSON 格式化器
文章目录
- 一、项目背景:为什么要自己做一个 JSON 格式化器?
- 二、项目结构设计(workspace:core/cli/wasm 三段式)
- 三、核心库实现(`json-core`)
- 四、CLI 工具(`json-cli`):文件/管道/重定向全支持
- 五、WASM 版本(`json-wasm`):前端直接调用
- 六、发布与开源建议
- 七、结语
 
一、项目背景:为什么要自己做一个 JSON 格式化器?
前端日常里,JSON 调试几乎无处不在:
- 接口响应排查(API 网关回包、错误定位)
- 日志/埋点数据抽样核对
- 本地配置文件(*.json,.eslintrc,tsconfig.json)格式修正
常见痛点:
- 线上/后端返回的数据体量大、带有转义字符、混杂中文;
- CLI 工具跨平台依赖重,或需要 Node 环境;
- H5 项目里希望离线使用、快速格式化,不依赖服务端。
Rust 方案的优势:
- serde_json解析和序列化稳定与高效;
- CLI 编译成单个二进制,无运行时;
- 通过 wasm-bindgen输出 WASM,浏览器直接调用,加载即用。
二、项目结构设计(workspace:core/cli/wasm 三段式)
json-toolkit/
├─ Cargo.toml                # workspace 定义
├─ json-core/                # 核心库:解析、格式化、压缩、校验
│  ├─ Cargo.toml
│  └─ src/lib.rs
├─ json-cli/                 # CLI 二进制:文件/管道/重定向
│  ├─ Cargo.toml
│  └─ src/main.rs
└─ json-wasm/                # WASM 包:给前端/NPM 使用├─ Cargo.toml└─ src/lib.rs
三、核心库实现(json-core)
 
功能目标:
- format_pretty: 美化打印(两空格缩进)
- minify: 压缩去空格
- validate: 语法校验 + 友好错误消息
- pretty_with: 自定义缩进(空格/制表符)
json-core/Cargo.toml
[package]
name = "json-core"
version = "0.1.0"
edition = "2021"
license = "MIT"
description = "Core utilities for JSON formatting/minifying/validation"
repository = "https://github.com/yourname/json-toolkit"[dependencies]
serde_json = "1.0"
thiserror = "1.0"
json-core/src/lib.rs
//! json-core: JSON 格式化/压缩/校验核心库use serde_json::{self, Value};
use thiserror::Error;#[derive(Debug, Error)]
pub enum JsonCoreError {#[error("JSON 解析失败: {0}")]Parse(String),#[error("JSON 序列化失败: {0}")]Serialize(String),
}impl From<serde_json::Error> for JsonCoreError {fn from(e: serde_json::Error) -> Self {JsonCoreError::Parse(e.to_string())}
}/// 解析字符串为 Value
pub fn parse(input: &str) -> Result<Value, JsonCoreError> {serde_json::from_str::<Value>(input).map_err(Into::into)
}/// 美化打印(两空格缩进)
pub fn format_pretty(input: &str) -> Result<String, JsonCoreError> {let value = parse(input)?;serde_json::to_string_pretty(&value).map_err(|e| JsonCoreError::Serialize(e.to_string()))
}/// 自定义美化:缩进字符和重复次数
pub fn pretty_with(input: &str, indent: &str, repeat: usize) -> Result<String, JsonCoreError> {let value = parse(input)?;let formatter = serde_json::ser::PrettyFormatter::with_indent(indent.repeat(repeat).as_bytes());let mut buf = Vec::new();let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);value.serialize(&mut ser).map_err(|e| JsonCoreError::Serialize(e.to_string()))?;String::from_utf8(buf).map_err(|e| JsonCoreError::Serialize(e.to_string()))
}/// 压缩(去空格)
pub fn minify(input: &str) -> Result<String, JsonCoreError> {let value = parse(input)?;serde_json::to_string(&value).map_err(|e| JsonCoreError::Serialize(e.to_string()))
}/// 校验,仅返回是否通过与错误信息
pub fn validate(input: &str) -> Result<(), JsonCoreError> {let _ = parse(input)?;Ok(())
}
四、CLI 工具(json-cli):文件/管道/重定向全支持
 
功能:
- --input/-i输入文件(缺省读 stdin)
- --output/-o输出文件(缺省 stdout)
- --mode [pretty|minify|validate]
- --indent " "自定义缩进(对 pretty 生效)
- 支持大文件流式读取,避免一次性占满内存
json-cli/Cargo.toml
[package]
name = "json-cli"
version = "0.1.0"
edition = "2021"[dependencies]
clap = { version = "4.4", features = ["derive"] }
json-core = { path = "../json-core" }
anyhow = "1.0"
json-cli/src/main.rs
use clap::{Parser, ValueEnum};
use std::{fs::File,io::{self, Read, Write},path::PathBuf,
};
use anyhow::{Context, Result};#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
enum Mode {Pretty,Minify,Validate,
}#[derive(Parser, Debug)]
#[command(author, version, about = "A fast JSON formatter/minifier/validator in Rust")]
struct Args {#[arg(short, long)]input: Option<PathBuf>,#[arg(short, long)]output: Option<PathBuf>,#[arg(short, long, value_enum, default_value_t = Mode::Pretty)]mode: Mode,#[arg(long, default_value = "  ")]indent: String,#[arg(long, default_value_t = 1)]repeat: usize,
}fn read_all(mut reader: impl Read) -> Result<String> {let mut buf = String::new();reader.read_to_string(&mut buf)?;Ok(buf)
}fn main() -> Result<()> {let args = Args::parse();let input_str = match args.input {Some(path) => {let file = File::open(&path).with_context(|| format!("无法打开输入文件: {}", path.display()))?;read_all(file)?}None => read_all(io::stdin())?,};let output = match args.mode {Mode::Pretty => json_core::pretty_with(&input_str, &args.indent, args.repeat)?,Mode::Minify => json_core::minify(&input_str)?,Mode::Validate => {if let Err(e) = json_core::validate(&input_str) {eprintln!(" JSON 校验失败:{e}");std::process::exit(2);}" JSON 校验通过".to_string()}};match args.output {Some(path) => {let mut f = File::create(&path).with_context(|| format!("无法创建输出文件: {}", path.display()))?;f.write_all(output.as_bytes())?;eprintln!(" 已写入: {}", path.display());}None => io::stdout().write_all(output.as_bytes())?,}Ok(())
}
五、WASM 版本(json-wasm):前端直接调用
 
json-wasm/src/lib.rs
use wasm_bindgen::prelude::*;#[wasm_bindgen]
pub fn format_pretty(input: &str) -> Result<String, JsValue> {json_core::format_pretty(input).map_err(|e| JsValue::from_str(&e.to_string()))
}#[wasm_bindgen]
pub fn minify(input: &str) -> Result<String, JsValue> {json_core::minify(input).map_err(|e| JsValue::from_str(&e.to_string()))
}#[wasm_bindgen]
pub fn validate(input: &str) -> Result<(), JsValue> {json_core::validate(input).map_err(|e| JsValue::from_str(&e.to_string()))
}
在前端 H5 中直接使用:
<script type="module">import init, { format_pretty } from "./pkg/json_wasm.js";await init();console.log(format_pretty('{"a":1,"b":[2,3]}'));
</script>
六、发布与开源建议
- CLI 工具发布到 crates.io
- WASM 版本发布到 npm,提供 .d.ts类型定义
- 结合 GitHub Actions 实现自动化版本发布
- 后续可拓展:JSON5/HJSON/YAML转换模块
七、结语
Rust 让“一个小工具”也能做到工业级稳定。
 当 CLI 与 WASM 共用一套核心逻辑时,意味着你在思考模块化复用与生态适配。
 这正是 Rust 生态的魅力所在。
 它不仅写得快,更写得长久。
