用 Rust 写一个前端项目辅助工具:JSON 格式化器

文章目录

    • [一、项目背景:为什么要自己做一个 JSON 格式化器?](#一、项目背景:为什么要自己做一个 JSON 格式化器?)
    • [二、项目结构设计(workspace:core/cli/wasm 三段式)](#二、项目结构设计(workspace:core/cli/wasm 三段式))
    • 三、核心库实现(`json-core`)
    • [四、CLI 工具(`json-cli`):文件/管道/重定向全支持](#四、CLI 工具(json-cli):文件/管道/重定向全支持)
    • [五、WASM 版本(`json-wasm`):前端直接调用](#五、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

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

rust 复制代码
//! 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

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

rust 复制代码
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

rust 复制代码
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 中直接使用:

html 复制代码
<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 生态的魅力所在。
它不仅写得快,更写得长久。

相关推荐
焦糖小布丁2 小时前
加http和https访问的网站不同?
前端
weixin_497845542 小时前
Windows系统Rust安装慢的问题
开发语言·后端·rust
季春二九3 小时前
Edge 卸载工具 | 版本号1.0 | 专为彻底卸载Microsoft Edge设计
前端·microsoft·edge·edge 卸载工具
雨过天晴而后无语3 小时前
HTML中JS监听输入框值的即时变化
前端·javascript·html
座山雕~3 小时前
html 和css基础常用的标签和样式(2)-css
前端·css·html
一勺菠萝丶3 小时前
为什么 HTTP 能访问,但 HTTPS 却打不开?——Nginx SSL 端口配置详解
前端
4Forsee3 小时前
【Android】消息机制
android·java·前端
不爱说话郭德纲3 小时前
UniappX不会运行到鸿蒙?超超超保姆级鸿蒙开发生成证书以及配置证书步骤
前端·uni-app·harmonyos
jackzhuoa3 小时前
Rust API 设计的零成本抽象原则:从语言基石到工程实践
算法·rust