用Rust生成Ant-Design Table Columns | 京东云技术团队

news2024/11/17 21:42:57

经常开发表格,是不是已经被手写Ant-Design Table的Columns整烦了?

尤其是ToB项目,表格经常动不动就几十列。每次照着后端给的接口文档一个个配置,太头疼了,主要是有时还会粘错就尴尬了。

那有没有办法能自动生成columns配置呢?

当然可以。

目前后端的接口文档一般是使用Swagger来生成的,Swagger是基于OpenAPI规范的一种实现。(OpenAPI规范是一种描述RESTful API的语言无关的格式,它允许开发者定义API的操作、输入和输出参数、错误响应等信息,并提供了一种规范的方式来描述和交互API。)

那么我们只需要解析Swagger的配置就可以反向生成前端代码。

接下来我们就写个CLI工具来生成Table Columns。

平常我们实现一个CLI工具一般都是用Node,今天我们搞点不一样的,用Rust。

开始咯

swagger.json

打开后端用swagger生成的接口文档中的一个接口,一般是下面这样的,可以看到其json配置文件,如下图:
image

swagger: 2.0表明了这个文档使用的swagger版本,不同版本json配置结构会不同。

paths这里key是接口地址。

可以看到当前接口是“/api/operations/cate/rhythmTableList”。
顺着往下看,“post.responses.200.schema.originalRef”,这就是我们要找的,这个接口对应的返回值定义。

definitions拿到上面的返回值定义,就可以在“definitions”里找到对应的值。
这里是“definitions.ResponseResult«List«CateInsightRhythmListVO»».properties.data.items.originalRef”
通过他就可找到返回的实体类定义CateInsightRhythmListVO

CateInsightRhythmListVO这里就是我们生成Table Columns需要的字段定义了。

CLI

接下来制作命令行工具

起初我使用的是commander-rust,感觉用起来更符合直觉,全程采用macros定义即可。
但到发布的时候才发现,Rust依赖必须有一个确定的版本,commander-rust目前使用的是分支解析。。。
最后还是换了clap

clap的定义就要繁琐些,如下:

#[derive(Parser)]
#[command(author, version)]
#[command(about = "swagger_to - Generate code based on swagger.json")]
struct Cli {
    #[command(subcommand)]
    command: Option,
}

#[derive(Subcommand)]
enum Commands {
    /// Generate table columns for ant-design
    Columns(JSON),
}

#[derive(Args)]
struct JSON {
    /// path/to/swagger.json
    path: Option,
}


这里使用#[command(subcommand)]#[derive(Subcommand)]来定义columns子命令
使用#[derive(Args)]定义了path参数,用来让用户输入swagger.json的路径

实现columns子命令

columns命令实现的工作主要是下面几步:

  1. 读取用户输入的swagger.json

  2. 解析swager.json

  3. 生成ant-design table columns

  4. 生成对应Typescript类型定义

读取用户输入的swagger.json

这里用到了一个crate,serde_json, 他可以将swagger.json转换为对象。

let file = File::open(json).expect("File should open");
let swagger_json: Value = serde_json::from_reader(file).expect("File should be proper JSON");


解析swager.json

有了swagger_json对象,我们就可以按照OpenAPI的结构来解析它。

/// openapi.rs

pub fn parse_openapi(swagger_json: Value) -> Vec {
    let paths = swagger_json["paths"].as_object().unwrap();
    let apis = paths
        .iter()
        .map(|(path, path_value)| {
            let post = path_value["post"].as_object().unwrap();
            let responses = post["responses"].as_object().unwrap();
            let response = responses["200"].as_object().unwrap();
            let schema = response["schema"].as_object().unwrap();
            let original_ref = schema["originalRef"].as_str().unwrap();
            let data = swagger_json["definitions"][original_ref]["properties"]["data"]
                .as_object()
                .unwrap();
            let items = data["items"].as_object().unwrap();
            let original_ref = items["originalRef"].as_str().unwrap();
            let properties = swagger_json["definitions"][original_ref]["properties"]
                .as_object()
                .unwrap();
            let response = properties
                .iter()
                .map(|(key, value)| {
                    let data_type = value["type"].as_str().unwrap();
                    let description = value["description"].as_str().unwrap();
                    ResponseDataItem {
                        key: key.to_string(),
                        data_type: data_type.to_string(),
                        description: description.to_string(),
                    }
                })
                .collect();
            Api {
                path: path.to_string(),
                model_name: original_ref.to_string(),
                response: response,
            }
        })
        .collect();
    return apis;
}


这里我写了一个parse_openapi()方法,用来将swagger.json解析成下面这种形式:

[
  {
    path: 'xxx',
    model_name: 'xxx',
    response: [
      {
        key: '字段key',
        data_type: 'number',
        description: '字段名'
      }
    ]
  }
]


对应的Rust结构定义是这样的:

pub struct ResponseDataItem {
    pub key: String,
    pub data_type: String,
    pub description: String,
}

pub struct Api {
    pub path: String,
    pub model_name: String,
    pub response: Vec<ResponseDataItem>,
}


生成ant-design table columns

有了OpenAPI对象就可以生成Table Column了,这里写了个generate_columns()方法:

/// generator.rs

pub fn generate_columns(apis: &mut Vec) -> String {
    let mut output_text = String::new();
    output_text.push_str("import type { ColumnsType } from 'antd'\n");
    output_text.push_str("import type * as Types from './types'\n");
    output_text.push_str("import * as utils from './utils'\n\n");

    for api in apis {
        let api_name = api.path.split('/').last().unwrap();
        output_text.push_str(
            &format!(
                "export const {}Columns: ColumnsType = [\n",
                api_name,
                api.model_name
            )
        );
        for data_item in api.response.clone() {
            output_text.push_str(
                &format!(
                    "  {{\n    title: '{}',\n    key: '{}',\n    dataIndex: '{}',\n    {}\n  }},\n",
                    data_item.description,
                    data_item.key,
                    data_item.key,
                    get_column_render(data_item.clone())
                )
            );
        }
        output_text.push_str("]\n");
    }

    return output_text;
}


这里主要就是采用字符串模版的形式,将OpenAPI对象遍历生成ts代码。

生成对应Typescript类型定义

Table Columns的类型使用generate_types()来生成,原理和生成columns一样,采用字符串模版:

/// generator.rs

pub fn generate_types(apis: &mut Vec) -> String {
    let mut output_text = String::new();

    for api in apis {
        let api_name = api.path.split('/').last().unwrap();
        output_text.push_str(
            &format!(
                "export type {} = {{\n",
                Some(api.model_name.clone()).unwrap_or(api_name.to_string())
            )
        );
        for data_item in api.response.clone() {
            output_text.push_str(&format!("  {}: {},\n", data_item.key, data_item.data_type));
        }
        output_text.push_str("}\n\n");
    }

    return output_text;
}


main.rs

然后我们在main.rs中分别调用上面这两个方法即可

/// main.rs

let mut apis = parse_openapi(swagger_json);
    let columns = generator::generate_columns(&mut apis);
    let mut columns_ts = File::create("columns.ts").unwrap();
    write!(columns_ts, "{}", columns).expect("Failed to write to output file");
    let types = generator::generate_types(&mut apis);
    let mut types_ts = File::create("types.ts").unwrap();
    write!(types_ts, "{}", types).expect("Failed to write to output file");


对于columns和types分别生成两个文件,columns.ts和types.ts。

!这里有一点需要注意

当时开发的时候对Rust理解不是很深,起初拿到parse_openapi返回的apis我是直接分别传给generate_columns(apis)和generate_types(apis)的。但编译的时候报错了:

image

这对于js很常见的操作竟然在Rust中报错了。原来Rust所谓不依赖运行时垃圾回收而管理变量分配引用的特点就体现在这里。
我就又回去读了遍Rust教程里的“引用和借用”那篇,算是搞懂了。这里实际上是Rust变量所有权、引用和借用的问题。读完了自然你也懂了。

看看效果

安装

cargo install swagger_to


使用

swagger_to columns path/to/swagger.json


会在swagger.json所在同级目录生成三个文件:

columns.tsant-design table columns的定义

types.tscolumns对应的类型定义

utils.tscolumn中render对number类型的字段添加了格式化工具

image

Enjoy

作者:京东零售 于弘达

来源:京东云开发者社区

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/800131.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows下安装python3教程

参考:https://blog.csdn.net/kailingr/article/details/128193083 一、安装步骤图解 准备工作&#xff1a; 进官网https://www.python.org/下载Python 安装包&#xff0c;注意&#xff1a;Python 3.9不能在Windows 7或更早版本上使用 安装&#xff1a; 1.下载完之后双击该文…

STM32 串口基础知识学习

串行/并行通信 串行通信&#xff1a;数据逐位按顺序依次传输。 并行通信&#xff1a;数据各位通过多条线同时传输。 对比 传输速率&#xff1a;串行通信较低&#xff0c;并行通信较高。抗干扰能力&#xff1a;串行通信较强&#xff0c;并行通信较弱。通信距离&#xff1a;串…

【具有非线性反馈的LTI系统识别】针对反馈非线性的LTI系统,提供非线性辨识方案(SimulinkMatlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码、Simulink仿真实现 &#x1f4a5;1 概述 本文为具有反馈非线性的LTI系统提供了一种非线性识别方案&#xff0c;这取决于输入和LTI系统输出。对于MEMS来说尤其如此&#…

uniapp:手写签名,多张图合成一张图

要实现的内容&#xff1a;手写签名&#xff0c;协议内容。点击提交后&#xff1a;生成1张图片&#xff0c;有协议内容和签署日期和签署人。 实现的效果图如下&#xff1a; 1、签名页面 <template><view class"index"><u-navbar title"电子协议…

【数据结构】实验六:队列

实验六 队列 一、实验目的与要求 1&#xff09;熟悉C/C语言&#xff08;或其他编程语言&#xff09;的集成开发环境&#xff1b; 2&#xff09;通过本实验加深对队列的理解&#xff0c;熟悉基本操作&#xff1b; 3&#xff09; 结合具体的问题分析算法时间复杂度。 二、…

Qt完成文本转换为语音播报与保存(系统内置语音引擎)(一)

一、前言 在当今数字化社会,人们对于交互式应用程序的需求越来越高。除了传统的图形用户界面,语音交互也成为了一种流行的交互方式。在这种情况下,将文本转换为语音成为了一项重要的技术,它可以为用户提供更加人性化和便捷的交互方式。在此背景下,Qt提供了QTextToSpeech类…

python测试开发面试常考题:装饰器

目录 简介 应用 第一类对象 装饰器 描述器descriptor 资料获取方法 简介 Python 装饰器是一个可调用的(函数、方法或类)&#xff0c;它获得一个函数对象 func_in 作为输入&#xff0c;并返回另一函数对象 func_out。它用于扩展函数、方法或类的行为。 装饰器模式通常用…

flask中的常用装饰器

flask中的常用装饰器 Flask 框架中提供了一些内置的装饰器&#xff0c;这些装饰器可以帮助我们更方便地开发 Web 应用。以下是一些常用的 Flask 装饰器&#xff1a; app.route()&#xff1a;这可能是 Flask 中最常用的装饰器。它用于将 URL 路由绑定到一个 Python 函数&#x…

【前端知识】React 基础巩固(三十五)——ReduxToolKit (RTK)

React 基础巩固(三十五)——ReduxToolKit (RTK) 一、RTK介绍 Redux Tool Kit &#xff08;RTK&#xff09;是官方推荐的编写Redux逻辑的方法&#xff0c;旨在成为编写Redux逻辑的标准方式&#xff0c;从而解决上面提到的问题。 RTK的核心API主要有如下几个&#xff1a; confi…

Pytorch深度学习-----神经网络的基本骨架-nn.Module的使用

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

Cadence OrCAD Capture绘制符号时缩小栅格距离的方法图文教程

🏡《总目录》   🏡《宝典目录》 目录 1,概述2,问题概述3,常规方法4,正确方法3,总结1,概述 本文简单介绍,使用Capture软件绘制原理图符号时,缩小或自定义栅格间距的方法。 2,问题概述 如下图所示,在进行原理图符号绘制时,管脚元件实体的线条等,均只能放置在栅…

佰维存储面向旗舰智能手机推出UFS3.1高速闪存

手机“性能铁三角”——SoC、运行内存、闪存决定了一款手机的用户体验和定位&#xff0c;其中存储器性能和容量对用户体验的影响越来越大。 针对旗舰智能手机&#xff0c;佰维推出了UFS3.1高速闪存&#xff0c;写入速度最高可达1800MB/s&#xff0c;是上一代通用闪存存储的4倍以…

C++ 缺省参数

1.缺省参数 一般情况下&#xff0c;函数调用时的实参个数应与形参相同&#xff0c;但为了方便地使用函数&#xff0c;C允许定义具有缺省参数的函数&#xff0c;这种函数调用时&#xff0c;实参个数可以与形参不相同。 缺省参数指在定义函数时为形参指定缺省值&#xff08;默认…

SSIS对SQL Server向Mysql数据转发表数据 (二)

1、在SQL Server数据库创建一个数据库表&#xff1a;users USE [Test1] GO/****** Object: Table [dbo].[users] Script Date: 2023/7/27 16:25:11 ******/ SET ANSI_NULLS ON GOSET QUOTED_IDENTIFIER ON GOCREATE TABLE [dbo].[users]([id] [int] IDENTITY(1,1) NOT NUL…

【数据结构】实验八:树

实验八 树 一、实验目的与要求 1&#xff09;理解树的定义&#xff1b; 2&#xff09;掌握树的存储方式及基于存储结构的基本操作实现&#xff1b; 二、 实验内容 题目一&#xff1a;采用树的双亲表示法根据输入实现以下树的存储&#xff0c;并实现输入给定结点的双亲结点…

实验二十五、压控振荡电路的测试

一、题目 研究压控振荡电路输出波形频率随输入电压幅度的变化。 二、仿真电路 仿真电路如图1所示。集成运放采用 LM324AJ&#xff0c;其电源电压为 15 V。输入直流电压采用虚拟电压源&#xff0c;锯齿波才用函数发生器产生的锯齿波输出。 三、仿真内容 &#xff08;1&…

[自然语言处理] 自然语言处理库spaCy使用指北

spaCy是一个基于Python编写的开源自然语言处理库。基于自然处理领域的最新研究&#xff0c;spaCy提供了一系列高效且易用的工具&#xff0c;用于文本预处理、文本解析、命名实体识别、词性标注、句法分析和文本分类等任务。 spaCy的官方仓库地址为&#xff1a;spaCy-github。本…

mysql的整体架构

服务层-引擎层-硬盘层 一条语句执行的整体过程: 先建立连接&#xff08;mysql -h -p 密码&#xff09;–预处理-词法分析-语法分析-优化器选择用什么索引表如何连接等-执行器 到这里都是属于server层&#xff0c;大多数功能包括视图&#xff0c;存储过程&#xff0c;触发器都是…

透明屏能在商业广告中使用吗?

透明屏是一种新型的显示技术&#xff0c;它可以将图像或文字显示在透明的玻璃或塑料材料上&#xff0c;使得观看者可以同时看到背后的物体。 这种技术在商业广告、展览、零售和家庭娱乐等领域有着广泛的应用前景。 首先&#xff0c;透明屏可以用于商业广告。传统的广告牌通常…

100个网络安全测试面试题

1、Burpsuite常用的功能是什么&#xff1f; 2、reverse_tcp和bind_tcp的区别&#xff1f; 3、拿到一个待检测的站或给你一个网站&#xff0c;你觉得应该先做什么&#xff1f; 4、你在渗透测试过程中是如何敏感信息收集的&#xff1f; 5、你平时去哪些网站进行学习、挖漏洞提交到…