作者: 李大狗(李骜华)


系列简介:狗哥 Rust 学习笔记系列是大狗为对抗 Rust 陡峭的学习曲线而推出的 Rust 学习系列,具备如下原则:

  1. 循序渐进原则

按照阶梯法则(下一篇的难度是上一篇难度+1)原则进行设计,让学习 Rust 跟打游戏一样简单。

  1. 单一知识点原则

一篇文章只讲一个一个知识点,保证简单性与专注性。

  1. 实用原则

所有案例均是真实实践案例,实用性超强。

在上一篇文章里,我们开启了weid-rust-example项目,学习了如何通过diesel项目玩转SQLite数据库。今天我们依然在这个项目的基础上往前推进。

实现功能

我们将围绕结构体Struct WeId,实现create_weid_online函数:

  1. #[derive(Default)]
  2. pub struct WeId{
  3. endpoint_url: String,
  4. weid: String,
  5. }
  6. impl WeId{
  7. pub fn create_weid_online(&self) -> ... {
  8. }
  9. }

和以往我们实现过的函数不同的是,在这个函数中,我们可以遇到多种可能的错误(Error),因此,在返回值里我们就不能向过去一样,填写Result<Value, reqwest::Error>,我们需要通过一个枚举(Enum)把可能的错误打包在一起。

Let’s Go!

create_weid_online 函数拆解

在函数式编程中,我们会遵循「单一职责原则」,简单来说,就是一个函数只做一件事。因此,即使create_weid_online是简单的函数,我们依然可以将其拆分:

  1. create_weid_online —— 子函数的组合
  2. |------ call_create_weid —— 通过weid-rest-service的接口注册托管型 weid 并获得返回值
  3. |------ str_to_json —— &str 值转换为 json

子函数的实现

str_to_json函数:

  1. fn str_to_json(&self, payload: &str) -> Result<Value, serde_json::Error> {
  2. serde_json::from_str(payload)
  3. }

这个函数中使用了serde_json库,在Cargo.toml为:

  1. [dependencies]
  2. ...
  3. serde_json = { version = "1.0" }
  4. ...

call_create_weid函数:

  1. pub fn call_create_weid(&self) -> Result<String, reqwest::Error> {
  2. let mut url =self.endpoint_url.to_string();
  3. url += &"/weid/api/invoke".to_string();
  4. // ::blocking:: to block
  5. let response = reqwest::blocking::Client::new()
  6. .post(&url)
  7. .json(&serde_json::json!({
  8. "functionArg": {},
  9. "transactionArg": {},
  10. "v": "1.0.0",
  11. "functionName": "createWeId"
  12. }))
  13. .send()?
  14. .text();
  15. response
  16. }

注:在前面的文章中我们介绍了用 reqwest 调用 get 接口,这次我们调用 post 接口。

这个函数中使用了reqwest库,在Cargo.toml为:

  1. [dependencies]
  2. ...
  3. reqwest = { version = "0.10", features = ["blocking", "json"] }
  4. tokio = { version = "0.2", features = ["full"] }
  5. ...

WeId-Rest-Service 的接口说明请见:

https://weidentity.readthedocs.io/zh_CN/latest/docs/weidentity-rest-api.html

主函数的实现

以下是create_weid_online函数的源码:

  1. pub fn create_weid_online(&self) -> Result<Value, GenerateWeIdError>{
  2. let response = self.call_create_weid()?; // line1
  3. let resp = self.str_to_json(&response)?; // line2
  4. Ok(resp)
  5. }

我们可以看到,在返回值里的 Error 处,我们填的是自定义的错误类型GenerateWeidError

所以,在line1处可能发生的reqwest::Error错误,和line2处可能发生的serde_json::Error,会被汇集在GenerateWeIdError中。

聚合错误处理的实现

在这里我们使用thiserror这个库,这是目前的最佳处理方案。

Cargo.toml中引用thiserror

  1. [dependencies]
  2. ...
  3. thiserror = "1.0"
  4. ...

官方的例子是这样的:

  1. use thiserror::Error;
  2. #[derive(Error, Debug)]
  3. pub enum DataStoreError {
  4. #[error("data store disconnected")]
  5. Disconnect(#[from] io::Error),
  6. #[error("the data for key `{0}` is not available")]
  7. Redaction(String),
  8. #[error("invalid header (expected {expected:?}, found {found:?})")]
  9. InvalidHeader {
  10. expected: String,
  11. found: String,
  12. },
  13. #[error("unknown data store error")]
  14. Unknown,
  15. }

在这里我们简单使用:

  1. // 记得 enum 要写在 Struct 外面。
  2. #[derive(Error, Debug)]
  3. pub enum GenerateWeIdError {
  4. #[error("req error")]
  5. RequestError(#[from] reqwest::Error),
  6. #[error("parse error")]
  7. ParseError(#[from] serde_json::Error),
  8. }

这样,就能让GenerateWeIdError囊括这个函数中的所有可能 error 了。

【补充资料】关于 Enum 枚举:

https://kaisery.github.io/trpl-zh-cn/ch06-01-defining-an-enum.html

main函数

main函数中,我们对结构体与函数进行调用:

  1. fn main(){
  2. let weid = WeId::new("http://127.0.0.1:6001".to_string());
  3. let result = weid.create_weid_online();
  4. match result {
  5. Ok(payload) => println!("{:}", payload),
  6. Err(e) => println!("{}", e)
  7. }

执行后,打印出了我们期待的结果:

Rust 学习笔记系列| Part 6 - 图1

本项目代码见:

https://github.com/leeduckgo/weid-rust-sample

本系列代码见:

https://github.com/leeduckgo/Rust-Study