您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
首页
博文
课程
大赛
工具
更多
用户中心
开发者社区
>
博文
>
用Rust生成Ant-Design Table Columns
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
用Rust生成Ant-Design Table Columns
yu****
2023-07-31
IP归属:北京
6480浏览
# 用Rust生成Ant-Design Table Columns 经常开发表格,是不是已经被手写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](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-05-04-14-484ciubWaMOPZHDNA.png) `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](https://github.com/MSDimos/commander-rust),感觉用起来更符合直觉,全程采用macros定义即可。 但到发布的时候才发现,Rust依赖必须有一个确定的版本,commander-rust目前使用的是分支解析。。。 最后还是换了[clap](https://github.com/clap-rs/clap) clap的定义就要繁琐些,如下: ```Rust #[derive(Parser)] #[command(author, version)] #[command(about = "swagger_to - Generate code based on swagger.json")] struct Cli { #[command(subcommand)] command: Option<Commands>, } #[derive(Subcommand)] enum Commands { /// Generate table columns for ant-design Columns(JSON), } #[derive(Args)] struct JSON { /// path/to/swagger.json path: Option<String>, } ``` 这里使用`#[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转换为对象。 ```Rust 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的结构来解析它。 ```Rust /// openapi.rs pub fn parse_openapi(swagger_json: Value) -> Vec<Api> { 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解析成下面这种形式: ```js [ { path: 'xxx', model_name: 'xxx', response: [ { key: '字段key', data_type: 'number', description: '字段名' } ] } ] ``` 对应的Rust结构定义是这样的: ```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()`方法: ```Rust /// generator.rs pub fn generate_columns(apis: &mut Vec<Api>) -> 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<Types.{}> = [\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一样,采用字符串模版: ```Rust /// generator.rs pub fn generate_types(apis: &mut Vec<Api>) -> 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中分别调用上面这两个方法即可 ```Rust /// 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](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-05-04-16-10zT4pFtAbuiLwL7w.png) 这对于js很常见的操作竟然在Rust中报错了。原来Rust所谓不依赖运行时垃圾回收而管理变量分配引用的特点就体现在这里。 我就又回去读了遍Rust教程里的[“引用和借用”](https://kaisery.github.io/trpl-zh-cn/ch04-02-references-and-borrowing.html)那篇,算是搞懂了。这里实际上是Rust变量所有权、引用和借用的问题。读完了自然你也懂了。 ## 看看效果 ### 安装 ```bash cargo install swagger_to ``` ### 使用 ```bash swagger_to columns path/to/swagger.json ``` 会在swagger.json所在同级目录生成三个文件: `columns.ts` ant-design table columns的定义 `types.ts` columns对应的类型定义 `utils.ts` column中render对number类型的字段添加了格式化工具 ![image](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-05-04-16-28X28cdcOuXOPC9uwD.png) ## Enjoy
上一篇:SpringCloud-Hystrix服务熔断与降级工作原理&源码
下一篇:突破传统监测模式:业务状态监控HM的新思路
yu****
文章数
3
阅读量
597
作者其他文章
01
用ChatGPT做一个Chrome扩展
用ChatGPT做了个Chrome Extension最近科技圈儿最火的话题莫过于ChatGPT了。最近又发布了GPT-4,发布会上的Demo着实吸睛。笔记本上手画个网页原型,直接生成网页。网友直呼:前端失业了!但我觉着啊,真就外行看热闹,内行看笑话。虽然ChatGPT很强大,但没有经过针对某个领域自定义化过的模型真就没啥用。。。就比如写代码这事儿,我觉着还不如GitHub的Copilot用着方便
01
用Rust生成Ant-Design Table Columns
用Rust生成Ant-Design Table Columns经常开发表格,是不是已经被手写Ant-Design Table的Columns整烦了?尤其是ToB项目,表格经常动不动就几十列。每次照着后端给的接口文档一个个配置,太头疼了,主要是有时还会粘错就尴尬了。那有没有办法能自动生成columns配置呢?当然可以。目前后端的接口文档一般是使用Swagger来生成的,Swagger是基于OpenA
01
为React Ant-Design Table增加字段设置
为React Ant-Design Table增加字段设置最近做的几个项目经常遇到这样的需求,要在表格上增加一个自定义表格字段设置的功能。就是用户可以自己控制那些列需要展示。在几个项目里都实现了一遍,每个项目的需求又都有点儿不一样,迭代了很多版,所以抽时间把这个功能封装了个组件:@silverage/table-custom,将这些差别都集成了进去,方便今后使用和维护。同时也方便需要这个功能的人来
yu****
文章数
3
阅读量
597
作者其他文章
01
用ChatGPT做一个Chrome扩展
01
为React Ant-Design Table增加字段设置
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号