Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: add checker & support decorator parameters #4

Merged
merged 35 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5bbc54c
add checker
dengsgo Oct 15, 2023
b1a53c5
update
dengsgo Oct 22, 2023
6f62538
add _packageInfo
dengsgo Oct 22, 2023
2f98c37
add test
dengsgo Oct 22, 2023
bd85cc8
update
dengsgo Oct 22, 2023
29124ee
update
dengsgo Oct 22, 2023
0b86051
update
dengsgo Oct 22, 2023
39b235e
staff
dengsgo Oct 28, 2023
3f9ea75
add test case
dengsgo Oct 29, 2023
73a508c
add test case
dengsgo Oct 29, 2023
8e89ecd
add parseDecorAndParameters
dengsgo Oct 30, 2023
8c86c10
add TestParseDecorAndParameters
dengsgo Oct 30, 2023
5b50c2c
update case
dengsgo Nov 1, 2023
756ce96
refactor
dengsgo Nov 1, 2023
e4e0881
add decor options params builder
dengsgo Nov 1, 2023
1a31c45
add test TestCheckDecorAndGetParam
dengsgo Nov 1, 2023
84aa4c8
fix find target
dengsgo Nov 1, 2023
3324225
add 临潼
dengsgo Nov 5, 2023
61cba9a
add valid lint
dengsgo Nov 5, 2023
ccb4bcb
fix fmt
dengsgo Nov 5, 2023
51b75eb
fix&add case
dengsgo Nov 5, 2023
98fbd20
add scan comment
dengsgo Nov 5, 2023
36a7bb4
add test case argsfunc
dengsgo Nov 5, 2023
b5c3306
add v0.10.0
dengsgo Nov 5, 2023
6c33be6
update passRequiredLint
dengsgo Nov 8, 2023
5bc3a0d
add argcfun test
dengsgo Nov 9, 2023
6e0da9f
add test -cover
dengsgo Nov 9, 2023
c7f02bc
update
dengsgo Nov 9, 2023
5c5992c
update resolveLinterFromAnnotation
dengsgo Nov 9, 2023
594cbd9
update README
dengsgo Nov 9, 2023
4c5a09f
add Guide Decorators with Parameters
dengsgo Nov 9, 2023
57f40dd
fix
dengsgo Nov 9, 2023
5a7935a
fix
dengsgo Nov 9, 2023
f7bda2c
fix test
dengsgo Nov 9, 2023
1a07cb3
fix test
dengsgo Nov 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ jobs:

- name: TestFunctions
run: |
go test -v ./cmd/decorator/
go test -v ./decor/
go test -v -cover ./cmd/decorator/
go test -v -cover ./decor/

- name: Build
run: |
Expand All @@ -41,6 +41,7 @@ jobs:
cd ../datetime && go build -toolexec decorator -a && ./datetime
cd ../emptyfunc && go build -toolexec decorator -a && ./emptyfunc
cd ../genericfunc && go build -toolexec decorator -a && ./genericfunc
cd ../argsfunc && go build -toolexec decorator -a && ./argsfunc
cd ../..

- name: TestExample
Expand All @@ -50,3 +51,4 @@ jobs:
go test -v ./example/datetime/
go test -v ./example/emptyfunc/
go test -v ./example/genericfunc/
cd ./example/argsfunc/ && go test -v -toolexec decorator
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v0.10.0 beta

- Feat: support for decorators with parameters
- Feat: the `go:decor-lint` annotation is supported to constrain the behavior of the caller
- Feat: two built-in types of parameter constraints are implemented: `required`、`nonzero`
- Update: new Scanning Implementation for Decorator Functions

## v0.5.0 beta

- Feat: decorator support for decorating generic functions
Expand Down
134 changes: 126 additions & 8 deletions GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
- [More](#more)


`go-decorator` is a compilation chaining tool that implements the decorator feature of the Go language.
`go-decorator` is a compilation chain tool that implements the Go language decorator feature, allowing annotations to be used to apply decorators to any function.

## Requirement

Expand All @@ -38,19 +38,30 @@ $ go install github.com/dengsgo/go-decorator/cmd/decorator@latest

Run `decorator` to display the current version.

Note: Please update frequently to install the latest version. Get the best experience.
```shell
$ decorator
decorator 0.10.0 beta , https://github.com/dengsgo/go-decorator
```

Tip: Run the above installation command frequently to install the latest version for bug fixes, enhanced experience, and more new features.

## Usage

`decorator` is `go`'s compilation chaining tool, which relies on the `go` command to invoke it and compile the code.

### Adding parameters

Add the parameter `-toolexec decorator` to the `go` command.
`decorator` relies on the native `go` command to call it, just add the `-toolexec decorator` parameter to the subcommand of `go`.
For example:

For example: 'go build **-toolexec decorator**', 'go run **-toolexec decorator** main.go'.
|Native Command|Use `decorator`|
|--------|--------|
| `go build` | `go build -toolexec decorator` |
| `go run main.go` | `go run -toolexec decorator main.go` |
| `go test -v` | `go test -toolexec decorator -v` |
| `go install` | `go install -toolexec decorator` |
| `go ... -flags...` | `go ... -toolexec decorator -flags...` |

This applies to most `go` subcommands.

### Add a dependency

Expand All @@ -60,16 +71,16 @@ In your project root directory, add the `go-decorator` dependency.
$ go get -u github.com/dengsgo/go-decorator
```

### Destination functions and decorators
### Understand destination functions and decorators

> Target functions: functions that use a decorator, also known as decorated functions.
> For example, if a function A uses a decorator B to decorate itself, A is the target function.

A decorator is a concept that is often used to include decorations on a target function. When code is run to the target function, it doesn't actually execute it, but runs the decorator it uses. The actual target function logic is wrapped into the decorator and allows the decorator to control it.
Decorators are also functions. When code is run to the target function, it doesn't actually execute it, but runs the decorator it uses. The actual target function logic is wrapped into the decorator and allows the decorator to control it.

### Customizing decorators

A decorator is an ordinary `go` Top-level Function of type `func(*decor.Context)`. As long as the function satisfies this type, it is a legal decorator and can be used to decorate other functions in the project code.
A decorator is an ordinary `go` Top-level Function of type `func(*decor.Context [, ...any])`. As long as the function satisfies this type, it is a legal decorator and can be used to decorate other functions in the project code.

For example, here's a logging decorator that prints the arguments of the called function:

Expand Down Expand Up @@ -166,6 +177,113 @@ If more than one decorator is used, the decorator execution is prioritized from

The use of multiple decorators may result in less readable code and increase the cost of understanding the logic flow, especially if the decorator itself is particularly complex. This is not recommended.


### Decorator with additional parameters

As the name suggests, decorators allow for defining additional parameters in addition to the first parameter `*decor.Context`, such as:

```go
package main
import "github.com/dengsgo/go-decorator/decor"

func hit(ctx *decor.Context, msg string, count int64, repeat bool, f float64, opt string) {
// code...
}
```

The `hit` function is a legitimate decorator with optional parameters, which allows the target function to pass in the corresponding value when calling, and the hit function can obtain the parameter value of the target function.

The following parameter types are allowed:

| types | keyword|
|-----|-----|
| Integer | int,int8,int16.int32,int64,unit,unit8,unit16,unit32,unit64 |
| Float | float32,float64 |
| String | string |
| Boolean | bool |

If it exceeds the above types, it cannot be compiled.


### Using Decorators with Parameters

Use the `//go:decor function#{}` method to pass parameters to the decorator. Compared to non parametric calls, there is an additional section called `#{}`, which we refer to as the parameter field.

The parameter field starts with a `#` identifier, followed by key value pairs such as `{key: value, key1: value1}`. The key is the formal parameter name of the decorator, and the value is the String, Boolean value, or Numerical value to be passed.

For example, we need to call the `hit` decorator defined above:

```go
package main
import "github.com/dengsgo/go-decorator/decor"

func hit(ctx *decor.Context, msg string, count int64, repeat bool, f float64, opt string) {
// code...
}

//go:decor hit#{msg: "message from decor", repeat: true, count: 10, f:1}
func useArgsDecor() {}
```

The `decorator` will automatically pass the `{msg: "message from decor", repeat: true, count: 10, f: 1}` parameters to the `decorator` according to their formal parameter names during compilation.

The order of parameters in the parameter field is independent of the formal parameter order of the decorator, and you can organize the code according to your own habits.

When there is no corresponding formal parameter value in the parameter field, such as `opt` above, the corresponding type's zero value will be passed by default.

### Decorator constraints and validation

`decorator` allows the use of annotations `//go:decor-lint linter: {}` on decorators to add decorator constraints. This constraint can be used at compile time to verify whether the call to the target function is legal.

Currently, there are two built-in decorator constraints:

#### required

Validation parameters must be passed. For example:

```go
//go:decor-lint required: {msg, count, repeat, f}
func hit(ctx *decor.Context, msg string, count int64, repeat bool, f float64, opt string) {
// code...
}
```

The four parameters, `msg`, `count`, `repeat`, and `f`, require that the target function must be passed during invocation, otherwise compilation cannot pass.

Not only that, `required` also supports validation of enumerations and scopes. For example:

**Enumeration Value Restrictions**:

`//Go: decor int required: {msg: {"hello", "world", "yeah"}, count, repeat, f}`: The argument to 'msg' must be one of the three values`"hello", "world", "yeah"`.

**Scope limitations**:

`//Go: decor int required: {msg: {gte: 8, lte: 24}, count, repeat, f}`: The string length range for 'msg' is required to be between '[8,24]'.

There are currently four supported scope directives:

| 范围指令 | 说明 |
|-------|----|
| `gte` | `>=` |
| `gt` | `>` |
| `lte` | `<=` |
| `lt` | `<` |

#### nonzero

The validation parameter value cannot be zero. For example:

```go
//go:decor-lint nonzero: {msg, count, f}
func hit(ctx *decor.Context, msg string, count int64, repeat bool, f float64, opt string) {
// code...
}
```

The three parameters `msg`, `count`, and `f` require the target function to pass values that cannot be zero when called.

> You can add '//go:decor-lint' rule constraints multiple times on the decorator, which means that the target function must all meet these constraints when calling the decorator in order to compile properly.

## Context

`ctx *decor.Context` is the entry parameter of the decorator function, which is the context of the target function (i.e., the function that uses this decorator, also known as the decorated function).
Expand Down
133 changes: 125 additions & 8 deletions GUIDE.zh_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
- [更多](#更多)


`go-decorator` 是实现 Go 语言装饰器特性的编译链工具。
`go-decorator` 是实现 Go 语言装饰器特性的编译链工具, 允许使用注释就能把装饰器应用在任意函数上

## 要求

Expand All @@ -38,17 +38,30 @@ $ go install github.com/dengsgo/go-decorator/cmd/decorator@latest

运行 `decorator` 显示当前版本。

提示:请经常更新以安装最新版本。获得最佳体验。
```shell
$ decorator
decorator 0.10.0 beta , https://github.com/dengsgo/go-decorator
```

提示:经常运行上述安装命令来安装最新版本,以获得 BUG 修复、增强体验和更多的新特性。

## 使用

`decorator` 是 `go` 的编译链工具,依靠 `go` 命令来调用它运行,进行代码的编译。
`decorator` 是 `go` 的编译链工具,依靠 `go` 命令来调用它,进行代码的编译。

### 添加参数

在 `go` 命令中添加参数 `-toolexec decorator` 即可。
在 `go` 子命令中添加参数 `-toolexec decorator` 即可。

例如 'go build **-toolexec decorator**'、'go run **-toolexec decorator** main.go'.
例如:

|原生命令| 使用 `decorator` |
|--------|--------|
| `go build` | `go build -toolexec decorator` |
| `go run main.go` | `go run -toolexec decorator main.go` |
| `go test -v` | `go test -toolexec decorator -v` |
| `go install` | `go install -toolexec decorator` |
| `go ... -flags...` | `go ... -toolexec decorator -flags...` |

它适用于大多数 `go` 的子命令。

Expand All @@ -60,16 +73,16 @@ $ go install github.com/dengsgo/go-decorator/cmd/decorator@latest
$ go get -u github.com/dengsgo/go-decorator
```

### 目标函数和装饰器
### 了解目标函数和装饰器

> 目标函数:即使用了装饰器的函数,也称为被装饰的函数。
> 比如 A 函数使用了装饰器 B 来装饰自己,A 即为目标函数。

装饰器是一种概念,它通常用来对目标函数进行包含装饰。当代码运行到目标函数的时候,并不会真的执行它,而是运行它所使用的装饰器。实际的目标函数逻辑被包装到了装饰器中,并允许装饰器来控制它。
装饰器也是函数,它通常用来对目标函数进行包含装饰。当代码运行到目标函数的时候,并不会真的执行它,而是运行它所使用的装饰器。实际的目标函数逻辑被包装到了装饰器中,并允许装饰器来控制它。

### 自定义装饰器

装饰器是普通的 `go` 一级函数(Top-level Function),它的类型是 `func(*decor.Context)`. 只要函数满足这个类型,它即是合法的装饰器,可以在项目代码里来装饰其他函数。
装饰器是普通的 `go` 一级函数(Top-level Function),它的类型是 `func(*decor.Context [, ...any])`. 只要函数满足这个类型,它即是合法的装饰器,可以在项目代码里来装饰其他函数。

例如, 这里定义一个 logging 装饰器,它可以打印被调用函数的参数:

Expand Down Expand Up @@ -164,6 +177,110 @@ func datetime(timestamp int64) string {

多个装饰器的使用,可能会导致代码的可读性变差,加大逻辑流程理解成本,尤其是装饰器本身的代码又特别复杂的情况。因此并不推荐这样使用。

### 带有额外参数的装饰器

顾名思义,装饰器允许定义除了第一个参数 `*decor.Context` 外的额外参数, 如:

```go
package main
import "github.com/dengsgo/go-decorator/decor"

func hit(ctx *decor.Context, msg string, count int64, repeat bool, f float64, opt string) {
// code...
}
```

`hit` 函数即为一个合法带有可选参数的装饰器,它允许目标函数调用时传入相应值, `hit` 函数就能获取到目标函数的参数值。

以下的参数类型是被允许的:

| 类型 | 关键字|
|-----|-----|
| 整数 | int,int8,int16.int32,int64,unit,unit8,unit16,unit32,unit64 |
| 浮点数 | float32,float64 |
| 字符串 | string |
| 布尔值 | bool |

如果超出以上类型,无法通过编译。

### 使用带有参数的装饰器

使用 `//go:decor function#{}` 的方式给装饰器传参。相对于无参调用,多了 `#{}` 这一部分,这部分我们称为参数域。

参数域以 `#` 标识开始,后跟 `{key:value, key1: value1}` 这样的键值对。其中键为装饰器的形参名,值为要传递的字符串、布尔值、或者数值。

例如,我们要调用上面定义的 `hit` 装饰器:

```go
package main
import "github.com/dengsgo/go-decorator/decor"

func hit(ctx *decor.Context, msg string, count int64, repeat bool, f float64, opt string) {
// code...
}

//go:decor hit#{msg: "message from decor", repeat: true, count: 10, f:1}
func useArgsDecor() {}
```

`decorator` 在编译时就会把 `{msg: "message from decor", repeat: true, count: 10, f:1}` 参数按形参名自动对应传递到装饰器中。

参数域中的参数顺序和装饰器的形参顺序无关,你可以按自己的习惯组织代码。

当参数域中没有对应的形参值时,比如上面的 `opt` ,`decorator` 会默认传递对应类型的零值。

### 装饰器约束和验证

`decorator` 允许在装饰器上使用注释 `//go:decor-lint linter: {}` 来添加装饰器约束。这个约束可以在编译时用来验证目标函数的调用是否合法。

目前内置了两种装饰器约束:

#### required

验证参数是必须要传的。例如:

```go
//go:decor-lint required: {msg, count, repeat, f}
func hit(ctx *decor.Context, msg string, count int64, repeat bool, f float64, opt string) {
// code...
}
```

`msg, count, repeat, f` 这四个参数就要求目标函数在调用时是必须要传的,否则编译无法通过。

不仅如此,`required` 还支持验证枚举和范围。比如:

**枚举值限制**:
`//go:decor-lint required: {msg: {"hello", "world", "yeah"}, count, repeat, f}`:要求 `msg` 的参数必须是 `"hello", "world", "yeah"` 这三个值中的一个。

**范围限制**:
`//go:decor-lint required: {msg: {gte: 8, lte: 24}, count, repeat, f}`:要求 `msg` 的字符串长度范围在 `[8,24]` 之间。
目前支持的范围指令有四个:

| 范围指令 | 说明 |
|-------|----|
| `gte` | `>=` |
| `gt` | `>` |
| `lte` | `<=` |
| `lt` | `<` |


#### nonzero

验证参数值不能为零值。例如:

```go
//go:decor-lint nonzero: {msg, count, f}
func hit(ctx *decor.Context, msg string, count int64, repeat bool, f float64, opt string) {
// code...
}
```

`msg, count, f` 三个参数要求目标函数在调用时传值不能为零值。

> 可以在装饰器上多次添加 `//go:decor-lint` 规则约束,这意味着目标函数在调用装饰器时,必须全部满足这些约束才能正常编译。


## Context

`ctx *decor.Context` 是装饰器函数的入参,它是目标函数(即使用了这个装饰器的函数,也称为被装饰的函数)上下文。
Expand Down
Loading