diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..38b9fe4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +*.a +*.so +*.exe +*.pyc +*.log +*.dll +*.out +*.key +_test +*.test +*.cert +*.exe~ +go.work +.Trash-* +.DS_Store +go.work.sum +main.out.bin +*gitignore*secrets* +*gitignore*outputs* +ehthumbs.db +Thumbs.db +.gitkeep +.vscode/ +*.dylib +vendor/ +.idea/ +*.err +*.swp +_obj +tmp/ +bin/ +tags +*.DB +*.db +*.o +*~ diff --git a/README.md b/README.md index ab38304..8915a4d 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,142 @@ -# must -must means assert means require. while the assert/require are using in testcase. must is using in main code. +# `must` – A Simple, Yet Powerful Assertion Library for Go -这个包的作用就是断言,比如该为真的地方返回假,或者该没有err的地方返回错误,就直接崩掉就行。 +### Why settle for less when you can be sure? -## 安装 -``` +In Go, errors are part of the game, but sometimes you want to enforce guarantees that your code must meet—without the need for extensive checks or verbose error-handling. That's where `must` comes in: a lightweight package that ensures certain conditions *must* be true for your program to continue, panic-free. If the condition fails, you get an immediate and clear failure, saving time debugging or testing edge cases. + +## README +[中文说明](README.zh.md) + +## Install + +Quickly integrate `must` into your Go project by running: + +```bash go get github.com/yyle88/must ``` -## 简单样例 -``` -res, err := run() -must.Done(err) //假如出现错误-就崩掉 -must.Nice(res) //假如结果为空-就崩掉 -``` +## Core Philosophy +The premise behind `must` is simple: **assert the unassertable**. + +In many cases, Go developers use error handling to validate that conditions are met, but what if you don’t want to write multiple checks for simple conditions? What if certain things in your codebase *must* happen, and anything less should halt the program? + +That’s exactly what `must` does. It brings you immediate feedback if something goes wrong with minimal boilerplate, ensuring you catch bugs early in the development cycle. + +## Features + +- **Enforce Certain Conditions**: Assert that a value or state must meet specific criteria (e.g., non-nil, non-zero). +- **Clear Failures**: Panic when an assertion fails, providing an explicit and actionable message. +- **Optimized for Speed**: Perfect for prototypes and small applications where fast feedback is critical. + +## Usage + +Here’s how you can use `must` in your code to handle assertions concisely: + +### Example 1: Validate Non-Nil Values + +```go +package main + +import ( + "fmt" + "github.com/yyle88/must" +) + +func main() { + // Simulate a function that may return an error + result, err := someFunction() + must.Done(err) // Panics if err is non-nil + must.Nice(result) // Panics if result is the zero value (nil or empty) + + fmt.Println("Result is:", result) +} + +func someFunction() (string, error) { + // For demonstration, returning a result and no error + return "Hello, must!", nil +} ``` -res, ok := run() -must.True(ok) //假如结果非真-就崩掉 -must.Nice(res) //假如结果为空-就崩掉 -``` -## 特别注意 -在代码里随意 panic 是不规范的,或者说,是不道德的,就像随地内个啥。 +### Example 2: Assert a Boolean Condition + +```go +package main -因此这个包只建议在写 demo/test 时用,而不建议在写 service 时用。 +import ( + "fmt" + "github.com/yyle88/must" +) -## 起名思路 +func main() { + // Simulate a check that should return true + isActive := checkStatus() + must.True(isActive) // Panics if isActive is false -因为这俩是我常用的包,因此我就不用 assert 或者 require 作为模块名啦。 + fmt.Println("Status is active:", isActive) +} + +func checkStatus() bool { + // Returning true for demonstration + return true +} ``` -"github.com/stretchr/testify/assert" -"github.com/stretchr/testify/require" + +### Example 3: Ensure Length Matches + +```go +package main + +import ( + "fmt" + "github.com/yyle88/must" +) + +func main() { + // Simulating an array + arr := []int{1, 2, 3} + must.Length(arr, 3) // Panics if the length is not 3 + + fmt.Println("Array length is correct:", len(arr)) +} ``` -相比之下我觉得 `must` 是不错的选择。 +## Why `must`? + +- **Simplicity Over Verbosity**: In Go, error handling is often verbose. `must` simplifies assertions into short, easy-to-understand statements. +- **Immediate Feedback**: Perfect for scenarios where you need to catch conditions early—be it during development or in fast-paced prototypes. +- **Zero-Cost Abstraction**: `must` introduces no additional complexity and is highly idiomatic, keeping your Go codebase clean and easy to maintain. + +## Core Assertions + +- **`must.True(condition bool)`**: Asserts that a boolean condition is `true`. Panics if `false`. +- **`must.Done(err error)`**: Verifies that an error is `nil`. Panics if the error is not `nil`. +- **`must.Nice(value interface{})`**: Asserts that the value is not the zero value (i.e., empty or uninitialized). Panics if zero. +- **`must.Length(array interface{}, length int)`**: Ensures the provided array or slice has the expected length. +- **`must.Equals(a, b interface{})`**: Compares two values for equality and panics if they are not equal. + +## A Word of Caution + +While `must` is great for quick feedback during development or testing, **it should not be used for production error handling**. If you need fine-grained control over errors, consider using traditional Go error-handling techniques. + +## Future Plans + +- Expand assertion types, like checking for non-empty strings or range conditions. +- Improve documentation with more complex examples and use cases. +- Introduce integrations with logging frameworks to output detailed panics in production environments (if needed). + +## Conclusion -在汉语式英语里,must表示必然/必须,相对更强硬些,但是在英语里,似乎表示务必,但真搞不定就搞不定吧,这就是非母语编程不得劲的地方。 +With `must`, Go developers get a tool that prioritizes **assertive correctness**. Whether you're in the middle of a prototype or just testing, you don’t need to check everything manually. Let `must` take care of the basic sanity checks for you so you can focus on building great software. -因此在这里说明下起名的思路。 +For now, `must` might be small, but it’ll make sure your foundations are solid. -我只认识4个字母的单词,5个字母的都认识不全,因此我还是倾向于使用4个字母的单词做模块名/函数名,这样用起来也方便。 +--- -## 断言三剑客 -现在已经有 `done` 和 `sure` 两个基础包,现在再有 `must` 就相当于有三剑客啦。 +## Give stars -sure: [软硬兼施](https://github.com/yyle88/sure) +Feel free to contribute or improve the package! Your stars and pull requests are welcome. -done: [确保结果](https://github.com/yyle88/done) +## Thank You -must: [不对就崩](https://github.com/yyle88/must) +If you find this package valuable, give it a star on GitHub! Thank you!!! diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 0000000..ab38304 --- /dev/null +++ b/README.zh.md @@ -0,0 +1,52 @@ +# must +must means assert means require. while the assert/require are using in testcase. must is using in main code. + +这个包的作用就是断言,比如该为真的地方返回假,或者该没有err的地方返回错误,就直接崩掉就行。 + +## 安装 +``` +go get github.com/yyle88/must +``` + +## 简单样例 +``` +res, err := run() +must.Done(err) //假如出现错误-就崩掉 +must.Nice(res) //假如结果为空-就崩掉 +``` + +``` +res, ok := run() +must.True(ok) //假如结果非真-就崩掉 +must.Nice(res) //假如结果为空-就崩掉 +``` + +## 特别注意 +在代码里随意 panic 是不规范的,或者说,是不道德的,就像随地内个啥。 + +因此这个包只建议在写 demo/test 时用,而不建议在写 service 时用。 + +## 起名思路 + +因为这俩是我常用的包,因此我就不用 assert 或者 require 作为模块名啦。 +``` +"github.com/stretchr/testify/assert" +"github.com/stretchr/testify/require" +``` + +相比之下我觉得 `must` 是不错的选择。 + +在汉语式英语里,must表示必然/必须,相对更强硬些,但是在英语里,似乎表示务必,但真搞不定就搞不定吧,这就是非母语编程不得劲的地方。 + +因此在这里说明下起名的思路。 + +我只认识4个字母的单词,5个字母的都认识不全,因此我还是倾向于使用4个字母的单词做模块名/函数名,这样用起来也方便。 + +## 断言三剑客 +现在已经有 `done` 和 `sure` 两个基础包,现在再有 `must` 就相当于有三剑客啦。 + +sure: [软硬兼施](https://github.com/yyle88/sure) + +done: [确保结果](https://github.com/yyle88/done) + +must: [不对就崩](https://github.com/yyle88/must) diff --git a/go.mod b/go.mod index b1d3660..2cb94df 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,14 @@ go 1.22.6 require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 - github.com/yyle88/zaplog v0.0.10 + github.com/yyle88/zaplog v0.0.14 go.uber.org/zap v1.27.0 ) require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/yyle88/mutexmap v1.0.4 // indirect + github.com/yyle88/mutexmap v1.0.7 // indirect go.uber.org/multierr v1.11.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 86eba06..f1bced4 100644 --- a/go.sum +++ b/go.sum @@ -12,10 +12,10 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/yyle88/mutexmap v1.0.4 h1:Y4VfNE28HBYwpw/UaZyTUZAjYcob3qPMubQWxgdYl9s= -github.com/yyle88/mutexmap v1.0.4/go.mod h1:lAEDkpXe9iSU0/8L/SaXAQ6TUa+4A9nnl9OQ56zHEQY= -github.com/yyle88/zaplog v0.0.10 h1:4jzeWB7h7IQ126hcE5mRMjVo99k8+xpLjQTtbie8JZ8= -github.com/yyle88/zaplog v0.0.10/go.mod h1:dS72cgivcfuXM6ExW6+f/YiPVGOzQ7spQL5jECrEdcU= +github.com/yyle88/mutexmap v1.0.7 h1:i4inpmq0fEvwxG9XGunxBVQBt0eAZEWL7ezurkQifqY= +github.com/yyle88/mutexmap v1.0.7/go.mod h1:lAEDkpXe9iSU0/8L/SaXAQ6TUa+4A9nnl9OQ56zHEQY= +github.com/yyle88/zaplog v0.0.14 h1:AN7pcBbo+QsHU3jYdu6NJdhckZ5OsnCBB5Wf8xQpBz8= +github.com/yyle88/zaplog v0.0.14/go.mod h1:EVbOBQhJ0kn7dPa7bpJs/8zEY5N6XQS9SodeojwQmdE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/must.go b/must.go index 6dd431e..815a462 100644 --- a/must.go +++ b/must.go @@ -1,125 +1,147 @@ package must import ( + "github.com/pkg/errors" "github.com/yyle88/zaplog" "go.uber.org/zap" ) -// True 期望真,当传入假时 panic +// True expects the value to be true. Panics if the value is false. +// True 期望值为 true。如果值为 false,则触发 panic。 func True(v bool) { if !v { zaplog.ZAPS.P1.LOG.Panic("expect TRUE while got FALSE", zap.Bool("v", v)) } } -// Done 期望无错误,假如传入的错误非空时 panic,认为起名为 NoError 比较冗长而且暗示期望有错,这不符合设计预期,设计的预期就是必然无错 +// Done expects no error. Panics if the provided error is non-nil. +// Done 期望没有错误。如果提供的错误不为 nil,则触发 panic。 func Done(err error) { if err != nil { zaplog.ZAPS.P1.LOG.Panic("NO ERROR BUG", zap.Error(err)) } } -// Nice 期望非零值,当传入零值时 panic,当传入非零值时返回它 +// Nice expects a non-zero value. Panics if the value is zero, returns the value if non-zero. +// Nice 期望一个非零值。如果值为零,则触发 panic;如果值非零,则返回该值。 func Nice[V comparable](a V) V { - var b V //zero + var b V // zero value if a == b { zaplog.ZAPS.P1.LOG.Panic("A IS ZERO VALUE", zap.Any("a", a)) } return a } -// Zero 期望零值,当传入非零值时 panic +// Zero expects a zero value. Panics if the value is non-zero. +// Zero 期望值为零。如果值不为零,则触发 panic。 func Zero[V comparable](a V) { - var b V //zero + var b V // zero value if a != b { zaplog.ZAPS.P1.LOG.Panic("A IS NOT ZERO VALUE", zap.Any("a", a)) } } -// None 期望零值,当传入非零值时 panic -// 就像 Zero 表示零值,而 None 表示空值,当然二者可以混用,只是含义不同,打印的日志略有不同 -// 当然也可以传 err 表示没有错误 -// 我不想写 must.NoError(err) 这样的函数,毕竟没错就没错,就用正向判定就行 +// None expects a zero value (empty). Panics if the value is non-zero. +// None 期望值为零(空)。如果值不为零,则触发 panic。 func None[V comparable](a V) { - var b V //zero + var b V // zero value if a != b { zaplog.ZAPS.P1.LOG.Panic("A IS NOT NONE VALUE", zap.Any("a", a)) } } -// Equals 期望相等,假如不相等就 panic +// Equals expects the values to be equal. Panics if they are not equal. +// Equals 期望值相等。如果值不相等,则触发 panic。 func Equals[V comparable](a, b V) { if a != b { zaplog.ZAPS.P1.LOG.Panic("A AND B ARE NOT EQUALS", zap.Any("a", a), zap.Any("b", b)) } } -// Is 期望相等,注意这里不是 errors.Is 的逻辑,而是期望 Equals 的逻辑 +// Is expects equality, not the logic of errors.Is, but the logic of Equals. Panics if the values are not equal. +// Is 期望相等,不是 errors.Is 的逻辑,而是 Equals 的逻辑。如果值不相等,则触发 panic。 func Is[V comparable](a, b V) { if a != b { zaplog.ZAPS.P1.LOG.Panic("A AND B ARE NOT EQUALS", zap.Any("a", a), zap.Any("b", b)) } } -// Ok 跟 Nice 相同,期望非零值,假如传入零值就 panic +// Ise expects the errors to be equal, similar to the behavior of errors.Is. Panics if they are not equal. +// Ise 期望错误相等,类似于 errors.Is 的行为。如果错误不相等,则触发 panic。 +func Ise(err, target error) { + if !errors.Is(err, target) { + zaplog.ZAPS.P1.LOG.Panic("ERROR IS NOT SAME", zap.Error(err), zap.Error(target)) + } +} + +// Ok expects a non-zero value. Panics if the value is zero. +// Ok 期望一个非零值。如果值为零,则触发 panic。 func Ok[V comparable](a V) { - var b V //zero + var b V // zero value if a == b { zaplog.ZAPS.P1.LOG.Panic("A IS ZERO VALUE NOT OK", zap.Any("a", a)) } } -// OK 跟 Ok 相同,只是很多时候我们喜欢大写的 OK 而不是这种 Ok,因此两者都提供让调用者自己选择喜欢的 +// OK expects a non-zero value. Panics if the value is zero. Provides an alternative name for preference. +// OK 期望一个非零值。如果值为零,则触发 panic。提供一个偏好的替代名称。 func OK[V comparable](a V) { - var b V //zero + var b V // zero value if a == b { zaplog.ZAPS.P1.LOG.Panic("A IS ZERO VALUE NOT OK", zap.Any("a", a)) } } -// TRUE 和 True 逻辑相同,毕竟是断言,直接用大写表达更强硬的意图 +// TRUE expects the value to be true. Panics if the value is false. +// TRUE 期望值为 true。如果值为 false,则触发 panic。 func TRUE(v bool) { if !v { zaplog.ZAPS.P1.LOG.Panic("expect TRUE while got FALSE", zap.Bool("v", v)) } } -// FALSE 和 False 逻辑相同,毕竟是断言,直接用大写表达更强硬的意图 +// FALSE expects the value to be false. Panics if the value is true. +// FALSE 期望值为 false。如果值为 true,则触发 panic。 func FALSE(v bool) { if v { zaplog.ZAPS.P1.LOG.Panic("expect FALSE while got TRUE", zap.Bool("v", v)) } } -// False 期望假,当传入真的时候 panic,因为很少有期望假的时候,因此这个函数不和 True 放在一起,而是放在末席 +// False expects the value to be false. Panics if the value is true. +// False 期望值为 false。如果值为 true,则触发 panic。 func False(v bool) { if v { zaplog.ZAPS.P1.LOG.Panic("expect FALSE while got TRUE", zap.Bool("v", v)) } } -// Have 意思是 NotEmpty 非空 slice +// Have checks that the slice is not empty. Panics if the slice is empty. +// Have 检查切片是否为空。如果切片为空,则触发 panic。 func Have[T any](a []T) { if len(a) == 0 { - zaplog.ZAPS.P1.LOG.Panic("expect LENGTH > 0 while got an none slice") + zaplog.ZAPS.P1.LOG.Panic("expect LENGTH > 0 while got an empty slice") } } -// Length 期望长度是 n,否则 panic +// Length expects the slice to have length n. Panics if the length is not n. +// Length 期望切片的长度为 n。如果长度不是 n,则触发 panic。 func Length[T any](a []T, n int) { if len(a) != n { zaplog.ZAPS.P1.LOG.Panic("expect LENGTH = n while not equals", zap.Int("len", len(a)), zap.Int("n", n)) } } -// Len 和 Length 作用相同,只是缩写,我更倾向于使用缩写 +// Len is an abbreviation of Length, serving the same purpose. Panics if the length is not n. +// Len 是 Length 的缩写,功能相同。如果长度不是 n,则触发 panic。 func Len[T any](a []T, n int) { if len(a) != n { zaplog.ZAPS.P1.LOG.Panic("expect LENGTH = n while not equals", zap.Int("len", len(a)), zap.Int("n", n)) } } -// In 检查元素是否在数组里 +// In checks if the value is in the slice. Panics if the value is not found. +// In 检查值是否在切片中。如果未找到该值,则触发 panic。 func In[T comparable](v T, a []T) { for i := range a { if a[i] == v { @@ -129,7 +151,8 @@ func In[T comparable](v T, a []T) { zaplog.ZAPS.P1.LOG.Panic("expect value in slice while not in", zap.Any("v", v), zap.Int("len", len(a))) } -// Contains 检查数组是否包含元素 +// Contains checks if the slice contains the value. Panics if the value is not found. +// Contains 检查切片是否包含该值。如果未找到该值,则触发 panic。 func Contains[T comparable](a []T, v T) { for i := range a { if a[i] == v {