From 3a78fd393d3c99d726860d7e35398e4cb237f7f7 Mon Sep 17 00:00:00 2001 From: CaptainNEO Date: Thu, 10 Mar 2022 21:01:51 +0800 Subject: [PATCH] almost finished.. --- .gitignore | 2 + README.md | 136 +- .../drf_example/animals/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 198 bytes .../animals/__pycache__/admin.cpython-310.pyc | Bin 0 -> 239 bytes .../animals/__pycache__/apps.cpython-310.pyc | Bin 0 -> 479 bytes .../__pycache__/models.cpython-310.pyc | Bin 0 -> 1252 bytes .../__pycache__/serializers.cpython-310.pyc | Bin 0 -> 669 bytes .../animals/__pycache__/views.cpython-310.pyc | Bin 0 -> 584 bytes .../drf_example/drf_example/animals/admin.py | 3 + .../drf_example/drf_example/animals/apps.py | 6 + .../animals/migrations/0001_initial.py | 28 + .../animals/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-310.pyc | Bin 0 -> 1128 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 209 bytes .../drf_example/drf_example/animals/models.py | 27 + .../drf_example/animals/serializers.py | 8 + .../drf_example/drf_example/animals/tests.py | 3 + .../drf_example/drf_example/animals/views.py | 8 + .../drf_example/drf_example/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 202 bytes .../__pycache__/settings.cpython-310.pyc | Bin 0 -> 2382 bytes .../__pycache__/urls.cpython-310.pyc | Bin 0 -> 1578 bytes .../__pycache__/wsgi.cpython-310.pyc | Bin 0 -> 613 bytes .../drf_example/drf_example/asgi.py | 16 + .../drf_example/drf_example/settings.py | 125 ++ .../drf_example/drf_example/urls.py | 44 + .../drf_example/drf_example/wsgi.py | 16 + python/drf_example/drf_example/manage.py | 22 + .../drf_example/openapi-schema.yml | 176 ++ .../drf_example/templates/swagger.html | 31 + python/drf_example/poetry.lock | 149 ++ python/drf_example/pyproject.toml | 18 + python/fastapi_example/main.py | 26 +- rust/poem/.gitignore | 1 + rust/poem/Cargo.lock | 1425 +++++++++++++++++ rust/poem/Cargo.toml | 18 + rust/poem/src/main.rs | 149 ++ 38 files changed, 2362 insertions(+), 75 deletions(-) create mode 100644 .gitignore create mode 100644 python/drf_example/drf_example/animals/__init__.py create mode 100644 python/drf_example/drf_example/animals/__pycache__/__init__.cpython-310.pyc create mode 100644 python/drf_example/drf_example/animals/__pycache__/admin.cpython-310.pyc create mode 100644 python/drf_example/drf_example/animals/__pycache__/apps.cpython-310.pyc create mode 100644 python/drf_example/drf_example/animals/__pycache__/models.cpython-310.pyc create mode 100644 python/drf_example/drf_example/animals/__pycache__/serializers.cpython-310.pyc create mode 100644 python/drf_example/drf_example/animals/__pycache__/views.cpython-310.pyc create mode 100644 python/drf_example/drf_example/animals/admin.py create mode 100644 python/drf_example/drf_example/animals/apps.py create mode 100644 python/drf_example/drf_example/animals/migrations/0001_initial.py create mode 100644 python/drf_example/drf_example/animals/migrations/__init__.py create mode 100644 python/drf_example/drf_example/animals/migrations/__pycache__/0001_initial.cpython-310.pyc create mode 100644 python/drf_example/drf_example/animals/migrations/__pycache__/__init__.cpython-310.pyc create mode 100644 python/drf_example/drf_example/animals/models.py create mode 100644 python/drf_example/drf_example/animals/serializers.py create mode 100644 python/drf_example/drf_example/animals/tests.py create mode 100644 python/drf_example/drf_example/animals/views.py create mode 100644 python/drf_example/drf_example/drf_example/__init__.py create mode 100644 python/drf_example/drf_example/drf_example/__pycache__/__init__.cpython-310.pyc create mode 100644 python/drf_example/drf_example/drf_example/__pycache__/settings.cpython-310.pyc create mode 100644 python/drf_example/drf_example/drf_example/__pycache__/urls.cpython-310.pyc create mode 100644 python/drf_example/drf_example/drf_example/__pycache__/wsgi.cpython-310.pyc create mode 100644 python/drf_example/drf_example/drf_example/asgi.py create mode 100644 python/drf_example/drf_example/drf_example/settings.py create mode 100644 python/drf_example/drf_example/drf_example/urls.py create mode 100644 python/drf_example/drf_example/drf_example/wsgi.py create mode 100755 python/drf_example/drf_example/manage.py create mode 100644 python/drf_example/drf_example/openapi-schema.yml create mode 100644 python/drf_example/drf_example/templates/swagger.html create mode 100644 python/drf_example/poetry.lock create mode 100644 python/drf_example/pyproject.toml create mode 100644 rust/poem/.gitignore create mode 100644 rust/poem/Cargo.lock create mode 100644 rust/poem/Cargo.toml create mode 100644 rust/poem/src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c074f6a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv +db.sqlite3 diff --git a/README.md b/README.md index d51d043..480b0af 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ # OpenAPI3 Framworks Compare 本文会横向对比几种支持OpenAPI文档生成的工具/框架。 - OpenAPI Specification(下文简称OAS)定义了一个标准的、语言无关的 RESTful API 接口规范,它可以同时允许开发人员和操作系统查看并理解某个服务的功能,而无需访问源代码,文档或网络流量检查(既方便人类学习和阅读,也方便机器阅读)。正确定义 OAS 后,开发者可以使用最少的实现逻辑来理解远程服务并与之交互。 此外,文档生成工具可以使用 OpenAPI 规范来生成 API 文档,代码生成工具可以生成各种编程语言下的服务端和客户端代码,测试代码和其他用例。OpenAPI官方提供了各种语言的服务端和客户端的代码生成工具,比较著名的如[OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator),当然也有很多优秀的第三方开发者开发的工具。 @@ -47,46 +46,43 @@ OpenAPI Specification(下文简称OAS)定义了一个标准的、语言无 ## 2. 参赛选手介绍 -| 语言 | 框架 | Web功能完备 | OpenAPI版本 | Github Stars | 当前版本 | first release date | -|--------|--------------------------------------------------------------------------|-------------|-------------|--------------------------------------------------------------------------------|------------------------------------------------------------------------------------|--------------------| -| Go | [goa](https://github.com/goadesign/goa) | 是 | 3 | ![stars](https://img.shields.io/github/stars/goadesign/goa.svg) | ![Release](https://img.shields.io/github/tag/goadesign/goa.svg) | 2016-08-03 | -| Go | [swag](https://github.com/swaggo/swag) | 否 | 2 | ![stars](https://img.shields.io/github/stars/swaggo/swag.svg) | ![Release](https://img.shields.io/github/release/swaggo/swag.svg) | 2017-11-30 | -| Go | [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) | 是 | 2 | ![stars](https://img.shields.io/github/stars/grpc-ecosystem/grpc-gateway.svg) | ![Release](https://img.shields.io/github/release/grpc-ecosystem/grpc-gateway.svg) | 2016-07-11 | -| Go | [fizz](https://github.com/wI2L/fizz) | 是 | 3 | ![stars](https://img.shields.io/github/stars/wI2L/fizz.svg) | ![Release](https://img.shields.io/github/release/wI2L/fizz.svg) | 2019-11-06 | -| Python | [Django REST framework](https://github.com/encode/django-rest-framework) | 是 | 3 | ![stars](https://img.shields.io/github/stars/encode/django-rest-framework.svg) | ![Release](https://img.shields.io/github/release/encode/django-rest-framework.svg) | 2011-02-22 | -| Python | [FastAPI](https://github.com/tiangolo/fastapi) | 是 | 3 | ![stars](https://img.shields.io/github/stars/tiangolo/fastapi.svg) | ![Release](https://img.shields.io/github/release/tiangolo/fastapi.svg) | 2018-12-16 | -| Rust | [Poem](https://github.com/poem-web/poem) | 是 | 3 | ![stars](https://img.shields.io/github/stars/poem-web/poem.svg) | ![Release](https://img.shields.io/github/tag/poem-web/poem.svg) | 2021-10-14 | +| 语言 | 框架 | Web功能完备 | 使用OpenAPI版本 | Github Stars | Contributors | Commit Activity | 当前版本 | first release date | +|--------|--------------------------------------------------------------------------|-------------|-----------------|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|--------------------| +| Go | [goa](https://github.com/goadesign/goa) | 是 | 3 | ![stars](https://img.shields.io/github/stars/goadesign/goa.svg) | ![contributors](https://img.shields.io/github/contributors/goadesign/goa.svg) | ![commit](https://img.shields.io/github/commit-activity/y/goadesign/goa.svg) | ![Release](https://img.shields.io/github/tag/goadesign/goa.svg) | 2016-08-03 | +| Go | [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) | 是 | 2 | ![stars](https://img.shields.io/github/stars/grpc-ecosystem/grpc-gateway.svg) | ![contributors](https://img.shields.io/github/contributors/grpc-ecosystem/grpc-gateway.svg) | ![commit](https://img.shields.io/github/commit-activity/y/grpc-ecosystem/grpc-gateway.svg) | ![Release](https://img.shields.io/github/release/grpc-ecosystem/grpc-gateway.svg) | 2016-07-11 | +| Go | [swag](https://github.com/swaggo/swag) | 否 | 2 | ![stars](https://img.shields.io/github/stars/swaggo/swag.svg) | ![contributors](https://img.shields.io/github/contributors/swaggo/swag.svg) | ![commit](https://img.shields.io/github/commit-activity/y/swaggo/swag.svg) | ![Release](https://img.shields.io/github/release/swaggo/swag.svg) | 2017-11-30 | +| Go | [fizz](https://github.com/wI2L/fizz) | 是 | 3 | ![stars](https://img.shields.io/github/stars/wI2L/fizz.svg) | ![contributors](https://img.shields.io/github/contributors/wI2L/fizz.svg) | ![commit](https://img.shields.io/github/commit-activity/y/wI2L/fizz.svg) | ![Release](https://img.shields.io/github/release/wI2L/fizz.svg) | 2019-11-06 | +| Python | [Django REST framework](https://github.com/encode/django-rest-framework) | 是 | 3 | ![stars](https://img.shields.io/github/stars/encode/django-rest-framework.svg) | ![contributors](https://img.shields.io/github/contributors/encode/django-rest-framework.svg) | ![commit](https://img.shields.io/github/commit-activity/y/encode/django-rest-framework.svg) | ![Release](https://img.shields.io/github/release/encode/django-rest-framework.svg) | 2011-02-22 | +| Python | [FastAPI](https://github.com/tiangolo/fastapi) | 是 | 3 | ![stars](https://img.shields.io/github/stars/tiangolo/fastapi.svg) | ![contributors](https://img.shields.io/github/contributors/tiangolo/fastapi.svg) | ![commit](https://img.shields.io/github/commit-activity/y/tiangolo/fastapi.svg) | ![Release](https://img.shields.io/github/release/tiangolo/fastapi.svg) | 2018-12-16 | +| Rust | [Poem](https://github.com/poem-web/poem) | 是 | 3 | ![stars](https://img.shields.io/github/stars/poem-web/poem.svg) | ![contributors](https://img.shields.io/github/contributors/poem-web/poem.svg) | ![commit](https://img.shields.io/github/commit-activity/y/poem-web/poem.svg) | ![Release](https://img.shields.io/github/tag/poem-web/poem.svg) | 2021-10-14 | ## 3. 详细对比 所有框架均会从以下几个方面进行讨论 +> 所有评价均为个人意见,欢迎不同声音:D 1. 开发过程 2. OAS的实现方式: 探究这些工具是如何从代码生成对应sepc文件的,并会导航一些关键代码,方便读者进行深入探究。 3. 优点:该框架的一些特色或者相比其他框架有优势的地方 4. 缺点:该框架的一些劣势,或者不使用的理由 -> 所有评价均为个人意见,欢迎不同声音:D +以及下文中所涉及项目的完整代码均保存在本仓库中。 + ### 1. Goa #### 1. 开发过程 -1. 安装goa提供的代码生成工具 +1. 安装goa提供的代码生成工具,创建一个design文件夹,用于描述API ```bash go install goa.design/goa/v3/cmd/goa@v3 - ``` - -2. 创建一个design文件夹,用于描述API - - ```bash mkdir -p goa_example/design ``` -3. 使用goa提供的dsl描述API [desigin.go](go/goa_example/design/design.go) +2. 使用goa提供的dsl描述API [desigin.go](go/goa_example/design/design.go) -4. 生成代码模板 +3. 生成代码模板 ```bash goa gen goa_example/design @@ -94,7 +90,7 @@ OpenAPI Specification(下文简称OAS)定义了一个标准的、语言无 该步骤会生成gen文件夹中的所有文件,包括design文件中定义的所有接口信息、Model描述、验证方式、Protobuf描述(如果有)等所有相关信息的Go描述 -5. (optional) 生成实现的一个example +4. (optional) 生成实现的一个example ```bash goa example goa_example/design @@ -102,7 +98,7 @@ OpenAPI Specification(下文简称OAS)定义了一个标准的、语言无 该步骤会生成cmd文件夹,包括http server和 grpc server的启动代码 和项目根目录/{service_name}.go的文件,其中包含了各方法的默认实现(fmt.Println()) -6. 完成代码实现 +5. 完成代码实现 接下来只需要修改各方法中的具体实现为真实业务逻辑即可[service1.go](go/goa_example/service1.go) @@ -422,7 +418,7 @@ Django REST Framework(以下简称DRF)是著名开源组织[encode](https://www. #### 1. 开发过程 -创建python虚拟环境、Django项目初始化相关的流程由于比较冗长,涉及到的上下文信息比较多,这里就不再赘述,感兴趣可以去看[Django官方文档](https://docs.djangoproject.com/en/4.0/)和[Django REST Framework官方文档](https://www.django-rest-framework.org),我们现在只关注他是怎么描述Schema的。 +创建python虚拟环境、Django项目初始化相关的流程和上下文信息较多,这里就不再赘述,感兴趣可以去看[Django官方文档](https://docs.djangoproject.com/en/4.0/)和[Django REST Framework官方文档](https://www.django-rest-framework.org),我们现在只关注他是怎么描述Schema的。 在DRF中,描述数据model的结构叫做Serializer。官方提供了非常多的内置的序列化方法,包括各种可接受的数据类型,然后用户在定义Serializer的时候使用kwargs为各个参数字段添加约束信息。serializer.Serializer类本身提供了 request → serializer → response 数据流的转化能力。用户只需要继承这些能力即可。例如 @@ -438,25 +434,41 @@ class SnippetSerializer(serializers.Serializer): language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python') ``` -也提供了直接继承Django Model的能力 +也提供了直接继承Django Model的能力,下面是一个完整的Student CURD的示例 ```python -class Snippet(models.Model): - created = models.DateTimeField(auto_now_add=True) - title = models.CharField(max_length=100, blank=True, default='') - code = models.TextField() - linenos = models.BooleanField(default=False) - language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100) - style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100) +# models.py +class Student(models.Model): + class YearInSchool(models.TextChoices): + FRESHMAN = "FR", "Freshman" + SOPHOMORE = "SO", "Sophomore" + JUNIOR = "JR", "Junior" + SENIOR = "SR", "Senior" + GRADUATE = "GR", "Graduate" + name = models.CharField(max_length=100, help_text="学生姓名") + year_in_school = models.CharField( + max_length=2, + choices=YearInSchool.choices, + default=YearInSchool.FRESHMAN, + help_text="该学生所在的学年", + ) + date_of_birth = models.DateField(help_text="该学生的生日") + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) class Meta: - ordering = ['created'] + ordering = ["created_at"] - -class SnippetSerializer(serializers.ModelSerializer): +# serializers.py +class StudentSerializer(serializers.ModelSerializer): class Meta: - model = Snippet + model = Student fields = "__all__" + +# views.py +class StudentView(viewsets.ModelViewSet): + serializer_class = StudentSerializer + queryset = Student.objects.all() ``` #### 2. OAS的实现方式 @@ -486,37 +498,7 @@ class SnippetSerializer(serializers.ModelSerializer): ##### 1. 编写业务逻辑main.py -```python -from typing import Optional - -from fastapi import FastAPI -from pydantic import BaseModel, Field - -app = FastAPI() - - -class Item(BaseModel): - id: Optional[int] = None - name: str = Field(description="The name of the item", regex="^[a-zA-Z0-9]*$") - price: float - is_offer: Optional[bool] = None - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int, q: Optional[str] = None): - return {"item_id": item_id, "q": q} - - -@app.post("/items") -def create_item(item: Item) -> Item: - return item - -``` +查看[main.py](./python/fastapi_example/main.py) ##### 2. 使用uvicorn启动服务 @@ -526,33 +508,45 @@ uvicorn main:app #### 2. OAS的实现方式 -通过Python提供的类型注解功能,程序在启动的时候,就能够得到所有入参内容、和返回结果,包括他们的类型信息。开发体验更像是直接写好处理逻辑的函数,然后框架把这些函数按照一定的规则用HTTP的形式实现。 +通过Python提供的类型注解功能,程序在启动的时候,在装饰器的地方就能够得到所装饰的函数的所有入参内容、和返回结果,以此为依据生成加上路由信息,和装饰器内提供的其他参数信息,一起生成OpenAPI中所需的内容。 +包括他们的类型信息。开发体验更像是直接写好处理逻辑的函数,然后框架把这些函数按照一定的规则用HTTP的形式实现。 #### 3. 优点 1. 所见即所得,开发接口的时候非常直观,效率很高,而且提供了十分丰富的其他功能使得参数可以复用。 -2. 设计清晰易懂,用户层面不需要知道太多细节,不强制添加对应的类型注解(当然对应的也就无法得到足够的文档信息) +2. 设计清晰易懂,用户层面不需要知道太多细节,用户可以按需要添加对应的类型注解。 3. 可以直接利用python语言提供的强大生态资源。 #### 4. 缺点 -1. 除了语言与公司技术栈不太相符外,找不到明显的缺点。 +1. 与公司技术栈不太匹配。 ### 7. Poem +该框架是当前Rust语言下百花齐放的各种异步Web框架中的第一个支持OpenAPI的框架。虽然发布时间不久,但是已经提供了比较完善的支持。 + #### 1. 开发过程 +这里采用官方用例提供的一个模拟用户CURD的例子 +查看代码[main.rs](./rust/poem/src/main.rs) + #### 2. OAS的实现方式 -WIP +1. 字段属性、路由信息采用poem-openapi-derive库提供的宏来描述 +2. 函数的入参和返回用于判断接口的Request和Response,这里有点类似FastAPI,只不过python用的类型注解来判断参数类型,rust使用的是泛型。 +3. 编译的时候,这些宏在全部展开,配合[Doc Comment](https://doc.rust-lang.org/rust-by-example/meta/doc.html)可以得到OpenAPI所需的全部信息。 + #### 3. 优点 -WIP +1. Rust语言提供零成本抽象能力、零GC、保证内存安全等特性使其性能非常优异。而且语言为用户提供了非常丰富的抽象能力、和特性。 +2. 框架本身提供的宏非常简洁。Go1.18宏正式启用后,可以调研是否可以借鉴思想。 + #### 4. 缺点 -WIP +1. Rust的学习成本比起Go语言要高,市场上相关人才比较少。 +2. 框架太年轻了,需要经过时间和开发者的考验。 ## 4. FAQ diff --git a/python/drf_example/drf_example/animals/__init__.py b/python/drf_example/drf_example/animals/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/drf_example/drf_example/animals/__pycache__/__init__.cpython-310.pyc b/python/drf_example/drf_example/animals/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a822ce960d3f4c3e2a62c787b850b59715cee1ae GIT binary patch literal 198 zcmd1j<>g`k0-$k9_jzaTX)u^`h}H?1f!w>-ZnyI40lKer&UC{@3pvLquv zPd}w7Ek3m(F}ENm6@{IcmzkTGQ>-5!pP83g5+AQuP~Xm)4WjcAVKWjuhbl~=HGVxdY*{AZg1Z zm_0^2G=Ir5kkxZFBY4gUK}HUVMW@wtO@O8s#}xLr$-K6{EvGj9@=C literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/animals/__pycache__/apps.cpython-310.pyc b/python/drf_example/drf_example/animals/__pycache__/apps.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e90b9d9c168f2de890e63ecc0bad35e6a5c7d02e GIT binary patch literal 479 zcmY*VyH3L}6t&|NLLUq2io}|s@xY2uLj{PXNJT7JEVp(_z_E>;2+9gyz+dn^Y*`r) zzrcjuRH9tz9^c3D@%2@MK^Hjt`iSRm?)>!0wuLSkJGX}p7+_F<5@ryB3%~+~UI9ZK zzMw2%;Wdam-;M=h*zBcRPn67y<-%9C4Z39P+zuUZkU<9AZI~ezWFbRNC2R(%EJ`YD zEAf25?xT1^Zx#egP|yd{F=Z literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/animals/__pycache__/models.cpython-310.pyc b/python/drf_example/drf_example/animals/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..710075f62637ff82b3d4c177a40a3d9c3970f19f GIT binary patch literal 1252 zcmZuxy>Amq6rWkI*K22OP=wIws%y6(E}%pxQE)<#VxR1!V4BgyZ(?uQ-PxPn70Fdd z&`?A|0tpG7bQSkeoUWna|Dc){8OXnYD0s7*K=>GIp5OPpdG9x?y4?=J6~7Mmyapj} ztZ;t0D6HbnuOR^fG$sjUl#)#X4m2JTXh?eCJZP{6YFucdrimICHBIm)By62vBnheU z5(P5Or&c__C0~-rDh?gyY>b(| zp=CZ#L>fBAnT%L_qO!S4l$O@v_LzCM%QRBjN)wbO@=V&q;7u){6onjyzVSIvMIt#j z9nP`hGPZoc`ER9&Pc8n&crdxOy*9*iM!((~ZI8wSJYjdZHL|$})xW5}&xV{`s`Bd^ZH^x@z6`kem%c zHV#~%cFxBRYgUpgy@M8RA8@+IE^DK<11<26DO*B5fHriH@1F8Y$oCK#0dx@=-3Jc4 z06j#-1=E@oCCIcm0X(s^RWAPf>)q2o7k@ulJU+%bb`o*OW0}s1xoOX3obf^)7AKB% z{|U+a|6kM*Zy()X{PEAb=MPa_ym}eBx{GGhLV!e5nVS~KeNo1R?%B}g^Pw1oN4|ab zBBYgd2XPvRvQRu#_qYH+**}xNYuabz+MH)i13Ig)i;zsaL*gG_sUF7!qu zV=%5oZ8dd$5lz}h!g@#eh#k$8$R@pY#xcPK82;dLIKXpaETWDz9PanKm4Vbf#8dufD+24i0<0 io?unHX>Qx^M-}j+^NfGJey%s{SaRFffGjsJ`=0=&wLCrm literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/animals/__pycache__/serializers.cpython-310.pyc b/python/drf_example/drf_example/animals/__pycache__/serializers.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21573691abfa7e5116da33cd9e51ba00a692fff0 GIT binary patch literal 669 zcmZuuy-wsX5Vqqa8((OYLq5XbI&;Q{1O39*ynCzjuF@k2A8&>k1bEX3= zOi+qkaCXFWsG}#QBj8uzqsSv2Pgy#6!#gbHe#L<;lqnv-x;Ng}2Ge$~p=vMa%%TZQ z_R!rX0i|0hk3lZ9K=#nD|EHL~vwtC@W*ynj1u_tMs5uT9AdPs~_(C&2 zfmZo}l*$+>Cu!^xDJxZilzv&tvet7$@leW}xibBZC8>Obn_zrAE5K;?%O?BHzwIIA0|R8wqk&8#YKYkTdsF6y#T7P98Py{xND+nIzr zRW=4b#i}YwWn8wP@wmD7A-y5S=8hm?Ay2qn#c+~_c0fZV1WN=|{us26MpTOs$Q`{c zVLy0RAwgCxxK`4zK-1a)5BRuyyz7-%J0T@L{-(S7T^FD1^os7%Z*}7i*^qCDe|N*4 AUH||9 literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/animals/__pycache__/views.cpython-310.pyc b/python/drf_example/drf_example/animals/__pycache__/views.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bd1eb6b08d070ca2e80682a1d1e67005de9a60e GIT binary patch literal 584 zcmY*Wy-pl45VrT%z;Yo?ioC%VutmBkiil7YQE-tsk;08J=Q*(#dv~$-AV-@#NK)pN z+@wGhyh19*yNI8WW;`?Ana|(1?e_L2K|B3dp55Yk6N=}SV6l(x-eXWiQB4fxloA!G zSVt!2@sKAvF)2?e`9f5xvR_1HK$qFinCB{=k&@lvJSnMXmo;2D=-nM18?R^mN>rpz9HVU-0)0@ zMj8;pZwg^rwbCIkgt%BqJ>J;B*6+B-mx#HR&e=4y^Y&~B^WOPbYVFy@3hWwr{3UjR z*F*Tfs7`R$RSmS&f$Vx&H`C8wtK);?Plr|8K_k2R-R{ClbJg1KZg<|APFkqCb${MA zm9h%~Kcwk2yvSu!8>wA2NVnImZH9=!T?mXMbV7&Tv}Ar_!Sy0wKmf5pF~9Z1$&D;C plmPU|jTc9u2ujVMA2vOa8nahn3;QtY_A&fA$1oJ6pap#s-vfNKnKS?Z literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/animals/admin.py b/python/drf_example/drf_example/animals/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/python/drf_example/drf_example/animals/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/python/drf_example/drf_example/animals/apps.py b/python/drf_example/drf_example/animals/apps.py new file mode 100644 index 0000000..c41249c --- /dev/null +++ b/python/drf_example/drf_example/animals/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AnimalsConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "animals" diff --git a/python/drf_example/drf_example/animals/migrations/0001_initial.py b/python/drf_example/drf_example/animals/migrations/0001_initial.py new file mode 100644 index 0000000..43baaad --- /dev/null +++ b/python/drf_example/drf_example/animals/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# Generated by Django 4.0.3 on 2022-03-10 10:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Student', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='学生姓名', max_length=100)), + ('year_in_school', models.CharField(choices=[('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate')], default='FR', help_text='该学生所在的学年', max_length=2)), + ('date_of_birth', models.DateField(help_text='该学生的生日')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'ordering': ['created_at'], + }, + ), + ] diff --git a/python/drf_example/drf_example/animals/migrations/__init__.py b/python/drf_example/drf_example/animals/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/drf_example/drf_example/animals/migrations/__pycache__/0001_initial.cpython-310.pyc b/python/drf_example/drf_example/animals/migrations/__pycache__/0001_initial.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea80b69562dd65b3fd98871ecab321fa6527fd0d GIT binary patch literal 1128 zcmY*YOOG5i5VqaDGu`vpY{C*2a^V!I4@qy}icrFaT_sXB(oVR{#qv&hdc5hj8+SL^ z;e-^{=xQVO!3WX$N3S`WHj)RPhL%DI%@hN2YBm#;vgbnB{!YP9; z+G2%wFK8Mtty0_^M`Wo z;!0X2a`mZngHNPAHkIUB6w-xWHUlhq`P)y=zWHJK^ViES7iWM55N@?-kmT8S~c*T^G}j49hrJ4}Wy;w{abyB_D*cug)*A8`J0BXUnY zqOQ`a#>a63kfqf4suLwE>uYrVz4kTl`IfbTw+uc|*)4qMeV@$5doy9z>TSH`N*#Lj zfhyLpXG*#5{$Gi`fl*rV{51Ut+oDRfH0howYoYYvhxgN?y`x)qQaqzYsouRdwW4@r z?8EBX#1y5lGA-xz%;*&Cl*?05l)3z$E;ROCUZt=8T)MlvdjtEWE%SOgxBfGCa8+-k zh)BqAhKz2rExJQD+sU4HV)i2ZH+s?TKE&sa;Y8@nT!-V9oR*K)`KqRs_6|mM(R(Yt O-={v2&$z`d2mb+!D_$4? literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/animals/migrations/__pycache__/__init__.cpython-310.pyc b/python/drf_example/drf_example/animals/migrations/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..011c0158886d2b29c91e738200b040542be960a7 GIT binary patch literal 209 zcmY+8u?oU45QbCfAVL@4qC>sGRYWNcE)^=y;k2m+n_NgD+DGzbd;nM9!pUS(Ke+Gz z@1~X^cJTf literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/animals/models.py b/python/drf_example/drf_example/animals/models.py new file mode 100644 index 0000000..4bec762 --- /dev/null +++ b/python/drf_example/drf_example/animals/models.py @@ -0,0 +1,27 @@ +from django.db import models + +# Create your models here. + + +class YearInSchool(models.TextChoices): + FRESHMAN = "FR", "Freshman" + SOPHOMORE = "SO", "Sophomore" + JUNIOR = "JR", "Junior" + SENIOR = "SR", "Senior" + GRADUATE = "GR", "Graduate" + + +class Student(models.Model): + name = models.CharField(max_length=100, help_text="学生姓名") + year_in_school = models.CharField( + max_length=2, + choices=YearInSchool.choices, + default=YearInSchool.FRESHMAN, + help_text="该学生所在的学年", + ) + date_of_birth = models.DateField(help_text="该学生的生日") + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ["created_at"] diff --git a/python/drf_example/drf_example/animals/serializers.py b/python/drf_example/drf_example/animals/serializers.py new file mode 100644 index 0000000..a0114eb --- /dev/null +++ b/python/drf_example/drf_example/animals/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers +from .models import Student + + +class StudentSerializer(serializers.ModelSerializer): + class Meta: + model = Student + fields = "__all__" diff --git a/python/drf_example/drf_example/animals/tests.py b/python/drf_example/drf_example/animals/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/python/drf_example/drf_example/animals/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/python/drf_example/drf_example/animals/views.py b/python/drf_example/drf_example/animals/views.py new file mode 100644 index 0000000..50e0fe8 --- /dev/null +++ b/python/drf_example/drf_example/animals/views.py @@ -0,0 +1,8 @@ +from rest_framework import viewsets +from .serializers import StudentSerializer +from .models import Student + + +class StudentView(viewsets.ModelViewSet): + serializer_class = StudentSerializer + queryset = Student.objects.all() diff --git a/python/drf_example/drf_example/drf_example/__init__.py b/python/drf_example/drf_example/drf_example/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/drf_example/drf_example/drf_example/__pycache__/__init__.cpython-310.pyc b/python/drf_example/drf_example/drf_example/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d063984f245e8fca220af3c9fd514dfae09241b GIT binary patch literal 202 zcmd1j<>g`kf@fznlR)%i5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_+erR!OQL%nr zYQDa6VnIn_W}cs`zkZN&kfW!5enDzpVnL>{Zdy@dZh3xDcCl`9er`cxQL27HWl2VU wo_O^gbQe4jdOVaW%QnT1>G>OqBWhXzHi^f06&oL${XOl1-O0~V~GS1!nYX-0}1}wkU&u4;m;rkTpltO(JvGQg$|X+F^)5E zLSMmw0)Pq&BjQlyIB8`J&xTkUj66^B2xU?@IDIKQKoAI%SbAl9BTWBKtQZfb0{srh z%vJeR+_Bj(*_^OI0*akcjMrWE@WIXDL;I%VA6kz-sk-<3^sC{${ioG++j91+ul8>z zgnjJqeRTgYaE70=J4UsXTpq*7P8hJ5^yQq?WOa6R1QFL}7Bd$dFm@DSnp~UNq?l5m z`Rs-d8@3A`H)cH;9E_742Q`;sOqn%^Z66U16|JOiP`$t!fqarPySIh=NGjd`8&diai)@QCwr&vssdO5|_1GJZQzCI>p@w>+gm zc@%pzQ6@+XgDydaavvaYfR1G%*K2!020Mtrg{V|+sDI~7}kkTJUdg!i?Kx* zCr{3c)FPn4k>A9D%Z8`^U!T{1tel-mj;8=m&kN8V`8X!dS%i5UtdFQU*E4EK5xx&V zK}zz$1QwX|LxeplZxR<27H$#jp?39JTC^-^^_GBj<4o0cpqYeLyNbfM6D zh0{U=Z6;u5X;am;?6lLeuGPAlVLjKrOXus_W^X&KsLf_)SF2mkI=Z2!OO3W}z!EG# ziRfv$)u`8-+OFEw(#3A4V_3azv({;Er3;4E+G(nWrl-rh`gS9;Z#HUbR!!+bT{YA! zte#$1d&V_8D#nreHyr*3Ojty9+ku+h>ivjZhl>OsPddym^` zA)6T{WNrX*EsLJ6)U_?O*EB3Z?pRw5ty%wxi$4Sw^5IWk$;yW+0S=X#9Wk2(ZLOnp zYhAUWgb@zxh-|E9Z941HbRBwR#*`8rv0)e}Q#$z9`SJIt937>lFX5y{9)1Ep4mkP^ z3?jF@E_%cstWIGY)RI)!nn% z%n51Fd57!;#0&5+z97Mg2jH?uKy}-Z2BekNmRnWT#D53>m0Z{;lA;D*`RT4gE zK&r#=;i{Y&bj`6g=>3=+F-B-fa$X6k4+)t|!3n?2XjQew4gU{Yf15HUu#qADipef_ zr_;SQuchFyM@q`!v#F7%QFvTXrS{J-F}4x)Z776Zf;2#4OVkaVuP~NH|Bh&ke@scPZm}8F{sD7_WoY8+V3J0Xvdv_!e_TJ+KWY z6^fGu=uFL)z^P>dO@k*I(DPC$`q+uHd^IeZc z1tqQW{luHz~alX+#@yAED}R46=(?$tq-;lN~B zI&N!ZaLD{j;s`tVo1G&|ueN@sJ4urGxCUiOUt z&VP+@Lxqxo*&NX_=d@5w56ix_eF<~AD)gzj4rJYHs#NzSEbx{i_vKKSl3h literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/drf_example/__pycache__/wsgi.cpython-310.pyc b/python/drf_example/drf_example/drf_example/__pycache__/wsgi.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..073cfe5f3e014dd7343065f1aaffdfec36532e53 GIT binary patch literal 613 zcmZ`%!EO^V5cO`-rYscoP;vG(5?NP*6GDip4N(vzQqu}4myNrgO&q))%XX8d*M2~M z0gikOKd>B;koW~ocoQM*g^@h=cs%po%oq<36B6Xt_wMyWM99zDXi7U6Z^LpYK?yP? zA`-ERrcu~iDlS{oRzx6Lw!iF&w%Gd`2di~Yru!nEknaB7kL1&2Fr+!^Le6P{mI_;B zaLY@hfEtTS$bFh5Lr>w>paVz!0_ZM?E5)zFGwvv-C5jsbJq0UJbj7XQx->6A(AkU| zqh!v#L_M2@eD4BMi595T0kqsYDC&@0I$B5t)Q7Tz28iZOw?OU07wIsW66W640X zm+1*x;MyY-U=(gj(4MW`T(abMXbBm0qw07LKCADt294_3`TNt+V4O|*mzTrQV3K_t zpI?3G-yJtPkUH>Q>bdK7s!o;#8d+Aw=&GX~nKTbIq;+Z=2e7IGdL=FDDhYuC3Vx$} z^|*ohjo5miTlGh>D+ksw4am;8@m%Urf6Ok4FG4QOuUi(X-Qxkmfh^+L&pTVQ76 k7pPf-=YP*NwbX2?gV($Hya}I(xSB$n96WjwKaG?4F9EZ|E&u=k literal 0 HcmV?d00001 diff --git a/python/drf_example/drf_example/drf_example/asgi.py b/python/drf_example/drf_example/drf_example/asgi.py new file mode 100644 index 0000000..34e0edc --- /dev/null +++ b/python/drf_example/drf_example/drf_example/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for drf_example project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'drf_example.settings') + +application = get_asgi_application() diff --git a/python/drf_example/drf_example/drf_example/settings.py b/python/drf_example/drf_example/drf_example/settings.py new file mode 100644 index 0000000..187a5a8 --- /dev/null +++ b/python/drf_example/drf_example/drf_example/settings.py @@ -0,0 +1,125 @@ +""" +Django settings for drf_example project. + +Generated by 'django-admin startproject' using Django 4.0.3. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "django-insecure-gt64$h6a$cmx_7&)g2bs=h1q@)-a_cq)vq(zit%mj#2xnch9t*" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "rest_framework", + "animals", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +ROOT_URLCONF = "drf_example.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +WSGI_APPLICATION = "drf_example.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/python/drf_example/drf_example/drf_example/urls.py b/python/drf_example/drf_example/drf_example/urls.py new file mode 100644 index 0000000..5e5e720 --- /dev/null +++ b/python/drf_example/drf_example/drf_example/urls.py @@ -0,0 +1,44 @@ +"""drf_example URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include +from django.views.generic import TemplateView +from rest_framework import routers +from rest_framework.schemas import get_schema_view +from animals.views import StudentView + +router = routers.DefaultRouter() +router.register("student", StudentView) + +urlpatterns = [ + path("", include(router.urls)), + path("admin/", admin.site.urls), + path( + "swagger/", + TemplateView.as_view( + template_name="swagger.html", + extra_context={"schema_url": "openapi-schema"}, + ), + name="swagger-ui", + ), + path( + "openapi", + get_schema_view( + title="Your Project", description="API for all things …", version="1.0.0" + ), + name="openapi-schema", + ), +] diff --git a/python/drf_example/drf_example/drf_example/wsgi.py b/python/drf_example/drf_example/drf_example/wsgi.py new file mode 100644 index 0000000..e6591fb --- /dev/null +++ b/python/drf_example/drf_example/drf_example/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for drf_example project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'drf_example.settings') + +application = get_wsgi_application() diff --git a/python/drf_example/drf_example/manage.py b/python/drf_example/drf_example/manage.py new file mode 100755 index 0000000..3fd76b6 --- /dev/null +++ b/python/drf_example/drf_example/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'drf_example.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/python/drf_example/drf_example/openapi-schema.yml b/python/drf_example/drf_example/openapi-schema.yml new file mode 100644 index 0000000..56ab697 --- /dev/null +++ b/python/drf_example/drf_example/openapi-schema.yml @@ -0,0 +1,176 @@ +openapi: 3.0.2 +info: + title: '' + version: '' +paths: + /student/: + get: + operationId: listStudents + description: '' + parameters: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Student' + description: '' + tags: + - student + post: + operationId: createStudent + description: '' + parameters: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Student' + multipart/form-data: + schema: + $ref: '#/components/schemas/Student' + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + description: '' + tags: + - student + /student/{id}/: + get: + operationId: retrieveStudent + description: '' + parameters: + - name: id + in: path + required: true + description: A unique integer value identifying this student. + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + description: '' + tags: + - student + put: + operationId: updateStudent + description: '' + parameters: + - name: id + in: path + required: true + description: A unique integer value identifying this student. + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Student' + multipart/form-data: + schema: + $ref: '#/components/schemas/Student' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + description: '' + tags: + - student + patch: + operationId: partialUpdateStudent + description: '' + parameters: + - name: id + in: path + required: true + description: A unique integer value identifying this student. + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Student' + multipart/form-data: + schema: + $ref: '#/components/schemas/Student' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + description: '' + tags: + - student + delete: + operationId: destroyStudent + description: '' + parameters: + - name: id + in: path + required: true + description: A unique integer value identifying this student. + schema: + type: string + responses: + '204': + description: '' + tags: + - student +components: + schemas: + Student: + type: object + properties: + id: + type: integer + readOnly: true + name: + type: string + description: "\u5B66\u751F\u59D3\u540D" + maxLength: 100 + year_in_school: + enum: + - FR + - SO + - JR + - SR + - GR + type: string + description: "\u8BE5\u5B66\u751F\u6240\u5728\u7684\u5B66\u5E74" + date_of_birth: + type: string + format: date + description: "\u8BE5\u5B66\u751F\u7684\u751F\u65E5" + created_at: + type: string + format: date-time + readOnly: true + updated_at: + type: string + format: date-time + readOnly: true + required: + - name + - date_of_birth diff --git a/python/drf_example/drf_example/templates/swagger.html b/python/drf_example/drf_example/templates/swagger.html new file mode 100644 index 0000000..cbe47f1 --- /dev/null +++ b/python/drf_example/drf_example/templates/swagger.html @@ -0,0 +1,31 @@ + + + + + Swagger + + + + + + +
+ + + + + diff --git a/python/drf_example/poetry.lock b/python/drf_example/poetry.lock new file mode 100644 index 0000000..76f6bbf --- /dev/null +++ b/python/drf_example/poetry.lock @@ -0,0 +1,149 @@ +[[package]] +name = "asgiref" +version = "3.5.0" +description = "ASGI specs, helper code, and adapters" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] + +[[package]] +name = "django" +version = "4.0.3" +description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +asgiref = ">=3.4.1,<4" +sqlparse = ">=0.2.2" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "djangorestframework" +version = "3.13.1" +description = "Web APIs for Django, made easy." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +django = ">=2.2" +pytz = "*" + +[[package]] +name = "pytz" +version = "2021.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "sqlparse" +version = "0.4.2" +description = "A non-validating SQL parser." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "tzdata" +version = "2021.5" +description = "Provider of IANA time zone data" +category = "main" +optional = false +python-versions = ">=2" + +[[package]] +name = "uritemplate" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" +category = "main" +optional = false +python-versions = ">=3.6" + +[metadata] +lock-version = "1.1" +python-versions = "^3.10" +content-hash = "e74b47a6538a1b28c9531278ec4708c89167a7cd1e406f17c9f0cc029c15d115" + +[metadata.files] +asgiref = [ + {file = "asgiref-3.5.0-py3-none-any.whl", hash = "sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"}, + {file = "asgiref-3.5.0.tar.gz", hash = "sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0"}, +] +django = [ + {file = "Django-4.0.3-py3-none-any.whl", hash = "sha256:1239218849e922033a35d2a2f777cb8bee18bd725416744074f455f34ff50d0c"}, + {file = "Django-4.0.3.tar.gz", hash = "sha256:77ff2e7050e3324c9b67e29b6707754566f58514112a9ac73310f60cd5261930"}, +] +djangorestframework = [ + {file = "djangorestframework-3.13.1-py3-none-any.whl", hash = "sha256:24c4bf58ed7e85d1fe4ba250ab2da926d263cd57d64b03e8dcef0ac683f8b1aa"}, + {file = "djangorestframework-3.13.1.tar.gz", hash = "sha256:0c33407ce23acc68eca2a6e46424b008c9c02eceb8cf18581921d0092bc1f2ee"}, +] +pytz = [ + {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, + {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, +] +pyyaml = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +sqlparse = [ + {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"}, + {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, +] +tzdata = [ + {file = "tzdata-2021.5-py2.py3-none-any.whl", hash = "sha256:3eee491e22ebfe1e5cfcc97a4137cd70f092ce59144d81f8924a844de05ba8f5"}, + {file = "tzdata-2021.5.tar.gz", hash = "sha256:68dbe41afd01b867894bbdfd54fa03f468cfa4f0086bfb4adcd8de8f24f3ee21"}, +] +uritemplate = [ + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, +] diff --git a/python/drf_example/pyproject.toml b/python/drf_example/pyproject.toml new file mode 100644 index 0000000..7f94b92 --- /dev/null +++ b/python/drf_example/pyproject.toml @@ -0,0 +1,18 @@ +[tool.poetry] +name = "drf_example" +version = "0.1.0" +description = "" +authors = ["CaptainNEO "] + +[tool.poetry.dependencies] +python = "^3.10" +Django = "^4.0.3" +djangorestframework = "^3.13.1" +PyYAML = "^6.0" +uritemplate = "^4.1.1" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/python/fastapi_example/main.py b/python/fastapi_example/main.py index 76093c4..697eae0 100644 --- a/python/fastapi_example/main.py +++ b/python/fastapi_example/main.py @@ -13,16 +13,34 @@ class Item(BaseModel): is_offer: Optional[bool] = None +db = { + 1: Item(id=1, name="Foo", price=19.99), + 2: Item(id=2, name="Bar", price=29.99), + 3: Item(id=3, name="Baz", price=39.99), +} + + @app.get("/") -def read_root(): - return {"Hello": "World"} +def get_items( + limit: int = 10, + offset: int = Field(0, description="db offset"), + q: Optional[str] = None, +): + print(limit, offset, q) + return [ + {"item": db[k], "price": db[k].price} + for k in sorted(db.keys())[offset : offset + limit] + ] @app.get("/items/{item_id}") -def read_item(item_id: int, q: Optional[str] = None): - return {"item_id": item_id, "q": q} +def read_item(item_id: int): + return db[item_id] @app.post("/items") def create_item(item: Item) -> Item: + pid = list(db.keys())[-1] + 1 + item.id = pid + db[pid] = item return item diff --git a/rust/poem/.gitignore b/rust/poem/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/rust/poem/.gitignore @@ -0,0 +1 @@ +/target diff --git a/rust/poem/Cargo.lock b/rust/poem/Cargo.lock new file mode 100644 index 0000000..52a7a91 --- /dev/null +++ b/rust/poem/Cargo.lock @@ -0,0 +1,1425 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "async-trait" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.44", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" +dependencies = [ + "aes-gcm", + "base64", + "hkdf", + "hmac", + "percent-encoding", + "rand", + "sha2", + "subtle", + "time 0.3.7", + "version_check", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "darling" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "email_address" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8684b7c9cb4857dfa1e5b9629ef584ba618c9b93bae60f58cb23f4f271d0468e" +dependencies = [ + "serde", +] + +[[package]] +name = "encoding_rs" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "h2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util 0.6.9", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "headers" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +dependencies = [ + "base64", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha-1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "multer" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f8f35e687561d5c1667590911e6698a8cb714a134a7505718a182e7bc9d3836" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin", + "tokio", + "version_check", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poem" +version = "1.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e1991d85570faf4c15a966a7a2c72ec1ff796481b2f32331ec213198452b86" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "cookie", + "futures-util", + "headers", + "http", + "hyper", + "mime", + "multer", + "parking_lot", + "percent-encoding", + "pin-project-lite", + "poem-derive", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "tempfile", + "thiserror", + "time 0.3.7", + "tokio", + "tokio-stream", + "tokio-util 0.7.0", + "tracing", +] + +[[package]] +name = "poem-derive" +version = "1.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba20c6a260c44a840c49dbd01f7a2b066122ce9f4a405c5e6d9a0dcefc7f11c7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "poem-openapi" +version = "1.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effcb95682aa4c315ea34c6d6f3e742c1e0263f52116e79ea7e5166b9b20b8ef" +dependencies = [ + "base64", + "bytes", + "derive_more", + "email_address", + "futures-util", + "mime", + "num-traits", + "poem", + "poem-openapi-derive", + "regex", + "serde", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "poem-openapi-derive" +version = "1.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52daa219842c0e616c015b5b60618e50c95e67ac2d9aff8d604aa42d74e2b67e" +dependencies = [ + "Inflector", + "darling", + "http", + "indexmap", + "mime", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "syn", + "thiserror", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "poem", + "poem-openapi", + "slab", + "tokio", + "tracing-subscriber", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" + +[[package]] +name = "tokio" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +dependencies = [ + "lazy_static", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" +dependencies = [ + "ansi_term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" diff --git a/rust/poem/Cargo.toml b/rust/poem/Cargo.toml new file mode 100644 index 0000000..eb31b5a --- /dev/null +++ b/rust/poem/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +poem = "1.3.13" +poem-openapi = { version = "1.3.13", features = [ + "email", + "swagger-ui", + "rapidoc", + "redoc", +] } +tokio = { version = "1.17.0", features = ["macros", "rt-multi-thread"] } +tracing-subscriber = "0.3.9" +slab = "0.4.5" diff --git a/rust/poem/src/main.rs b/rust/poem/src/main.rs new file mode 100644 index 0000000..b94ffed --- /dev/null +++ b/rust/poem/src/main.rs @@ -0,0 +1,149 @@ +use poem::{listener::TcpListener, Route, Server}; +use poem_openapi::{ + param::Path, + payload::Json, + types::{Email, Password}, + ApiResponse, Object, OpenApi, OpenApiService, Tags, +}; +use slab::Slab; +use tokio::sync::Mutex; + +#[derive(Tags)] +enum ApiTags { + /// Operations about user + User, +} + +/// Create user schema +#[derive(Debug, Object, Clone, Eq, PartialEq)] +struct User { + /// Id + #[oai(read_only)] + id: i64, + /// Name + #[oai(validator(max_length = 64))] + name: String, + /// Password + #[oai(validator(max_length = 32))] + password: Password, + email: Email, +} + +/// Update user schema +#[derive(Debug, Object, Clone, Eq, PartialEq)] +struct UpdateUser { + /// Name + name: Option, + /// Password + password: Option, +} + +#[derive(ApiResponse)] +enum CreateUserResponse { + /// Returns when the user is successfully created. + #[oai(status = 200)] + Ok(Json), +} + +#[derive(ApiResponse)] +enum FindUserResponse { + /// Return the specified user. + #[oai(status = 200)] + Ok(Json), + /// Return when the specified user is not found. + #[oai(status = 404)] + NotFound, +} + +#[derive(ApiResponse)] +enum DeleteUserResponse { + /// Returns when the user is successfully deleted. + #[oai(status = 200)] + Ok, + /// Return when the specified user is not found. + #[oai(status = 404)] + NotFound, +} + +#[derive(ApiResponse)] +enum UpdateUserResponse { + /// Returns when the user is successfully updated. + #[oai(status = 200)] + Ok, + /// Return when the specified user is not found. + #[oai(status = 404)] + NotFound, +} + +#[derive(Default)] +struct Api { + users: Mutex>, +} + +#[OpenApi] +impl Api { + /// Create a new user + #[oai(path = "/users", method = "post", tag = "ApiTags::User")] + async fn create_user(&self, user: Json) -> CreateUserResponse { + let mut users = self.users.lock().await; + let id = users.insert(user.0) as i64; + CreateUserResponse::Ok(Json(id)) + } + + /// Find user by id + #[oai(path = "/users/:user_id", method = "get", tag = "ApiTags::User")] + async fn find_user(&self, user_id: Path) -> FindUserResponse { + let users = self.users.lock().await; + match users.get(user_id.0 as usize) { + Some(user) => FindUserResponse::Ok(Json(user.clone())), + None => FindUserResponse::NotFound, + } + } + + /// Delete user by id + #[oai(path = "/users/:user_id", method = "delete", tag = "ApiTags::User")] + async fn delete_user(&self, user_id: Path) -> DeleteUserResponse { + let mut users = self.users.lock().await; + let user_id = user_id.0 as usize; + if users.contains(user_id) { + users.remove(user_id); + DeleteUserResponse::Ok + } else { + DeleteUserResponse::NotFound + } + } + + /// Update user by id + #[oai(path = "/users/:user_id", method = "put", tag = "ApiTags::User")] + async fn put_user(&self, user_id: Path, update: Json) -> UpdateUserResponse { + let mut users = self.users.lock().await; + match users.get_mut(user_id.0 as usize) { + Some(user) => { + if let Some(name) = update.0.name { + user.name = name; + } + if let Some(password) = update.0.password { + user.password = password; + } + UpdateUserResponse::Ok + } + None => UpdateUserResponse::NotFound, + } + } +} + +#[tokio::main] +async fn main() -> Result<(), std::io::Error> { + if std::env::var_os("RUST_LOG").is_none() { + std::env::set_var("RUST_LOG", "poem=debug"); + } + tracing_subscriber::fmt::init(); + + let api_service = + OpenApiService::new(Api::default(), "Users", "1.0").server("http://localhost:3000/api"); + let ui = api_service.rapidoc(); + + Server::new(TcpListener::bind("127.0.0.1:3000")) + .run(Route::new().nest("/api", api_service).nest("/", ui)) + .await +}