{"id":13764132,"url":"https://github.com/Parquery/gocontracts","last_synced_at":"2025-05-10T17:31:41.393Z","repository":{"id":43764821,"uuid":"144612670","full_name":"Parquery/gocontracts","owner":"Parquery","description":"A tool for design-by-contract in Go","archived":false,"fork":false,"pushed_at":"2019-01-26T07:32:40.000Z","size":98,"stargazers_count":111,"open_issues_count":1,"forks_count":7,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-11-16T23:32:37.669Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Parquery.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-13T17:33:48.000Z","updated_at":"2024-10-18T09:58:21.000Z","dependencies_parsed_at":"2022-09-04T00:02:54.961Z","dependency_job_id":null,"html_url":"https://github.com/Parquery/gocontracts","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Parquery%2Fgocontracts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Parquery%2Fgocontracts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Parquery%2Fgocontracts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Parquery%2Fgocontracts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Parquery","download_url":"https://codeload.github.com/Parquery/gocontracts/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253453361,"owners_count":21911083,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T15:01:15.239Z","updated_at":"2025-05-10T17:31:40.973Z","avatar_url":"https://github.com/Parquery.png","language":"Go","funding_links":[],"categories":["编辑器插件","Go Generate Tools","Go","Go生成工具","Go 生成工具","Libraries for creating HTTP middlewares","Go 代码生成工具"],"sub_categories":["代码分析","Routers","路由器"],"readme":"gocontracts\n===========\n![build status](https://travis-ci.com/Parquery/gocontracts.svg?branch=master)\n[![Coverage Status](https://coveralls.io/repos/github/Parquery/gocontracts/badge.svg?branch=master)](https://coveralls.io/github/Parquery/gocontracts?branch=master)\n[![Go Report Card](https://goreportcard.com/badge/github.com/Parquery/gocontracts)](https://goreportcard.com/report/github.com/Parquery/gocontracts)\n[![godoc](https://img.shields.io/badge/godoc-reference-5272B4.svg)](https://godoc.org/github.com/Parquery/gocontracts/gocontracts)\n\ngocontracts is a tool for\n[design-by-contract](https://en.wikipedia.org/wiki/Design_by_contract) in Go.\n\nIt generates pre- and post-condition checks from the function descriptions so\nthat the contracts are included in the documentation and automatically\nreflected in the code.\n\n(If you need invariants, please let us know by commenting on this issue: https://github.com/Parquery/gocontracts/issues/25.)\n\nWorkflow\n--------\nYou invoke gocontracts on an individual Go file. Gocontracts will parse the file\nand examine the descriptions of all the functions for contracts. The existing\ncontract checks will be overwritten to match the contracts in the description.\n\nA usual workflow includes defining the contracts in the function\ndescription and invoking gocontracts to automatically update them in the code.\n\nEach contract is defined as a one-line item in a bulleted list. Gocontracts\ndoes not validate the correctness of the conditions (_e.g._ undefined variables,\nsyntax errors _etc._). The code is simply inserted as-is in the header of the\nfunction body.\n\nSince contracts are logical conditions of the function, failing a contract\ncauses a `panic()`. If you need to validate the input, rather than check\nlogical pre- and post-conditions of the function, return an `error` and do\nnot abuse the contracts.\n\nRelated Projects\n================\nAt the time of this writing (2018-08-19), we found only a library that\nimplemented design-by-contract as functions (https://github.com/lpabon/godbc) and\na draft implementation based on decorators (https://github.com/ligurio/go-contracts).\n\nNone of them allowed us to synchronize the documentation with the code.\n\nExamples\n========\nSimple Example\n--------------\n\nGiven a function with contracts defined in the description, but no checks\npreviously written in code:\n\n```go\npackage somepackage\n\n// SomeFunc does something.\n//\n// SomeFunc requires:\n//  * x \u003e= 0\n//  * x \u003c 100\n//\n// SomeFunc ensures:\n//  * !strings.HasSuffix(result, \"smth\")\nfunc SomeFunc(x int) (result string) {\n\t// ...\n}\n```\n\n, gocontracts will generate the following code:\n\n```go\npackage somepackage\n\n// SomeFunc does something.\n//\n// SomeFunc requires:\n//  * x \u003e= 0\n//  * x \u003c 100\n//\n// SomeFunc ensures:\n//  * !strings.HasSuffix(result, \"smth\")\nfunc SomeFunc(x int) (result string) {\n\t// Pre-conditions\n\tswitch {\n\tcase !(x \u003e= 0):\n\t\tpanic(\"Violated: x \u003e= 0\")\n\tcase !(x \u003c 100):\n\t\tpanic(\"Violated: x \u003c 100\")\n\tdefault:\n\t\t// Pass\n\t}\n\n\t// Post-condition\n\tdefer func() {\n\t\tif strings.HasSuffix(result, \"smth\") {\n\t\t\tpanic(\"Violated: !strings.HasSuffix(result, \\\"smth\\\")\")\n\t\t}\n\t}()\n\n\t// ...\n}\n```\n\nNote that you have to manually import the `strings` package since goconracts\nis not smart enough to do that for you.\n\nAdditionally, if you want to use `go doc`, you have to indent conditions\nwith a space in the function description so that `go doc` renders them\ncorrectly as bullet points.\n\nCondition Labels\n----------------\nCertain conditions can be hard to understand when the formal definition lacks\na textual description. Gocontracts therefore allows you to introduce \ncondition labels to clarify the intent:\n\n```go\npackage somepkg\n\n// SomeFunc does something.\n//\n// SomeFunc requires:\n//  * positive: x \u003e 0\n//  * not too large: x \u003c 100\nfunc SomeFunc(x int, y int) (result string, err error) {\n\t// Pre-conditions\n\tswitch {\n\tcase !(x \u003e 0):\n\t\tpanic(\"Violated: positive: x \u003e 0\")\n\tcase !(x \u003c 100):\n\t\tpanic(\"Violated: not too large: x \u003c 100\")\n\tdefault:\n\t\t// Pass\n\t}\n\t\n\t// ...\n}\n```\n\nSince we need to distinguish the condition labels from the condition\ncode, we had to restrict the labels to strings of characters \n`[a-zA-Z0-9_;.\\-=' ]`. Otherwise, if we allowed a full character set,\nthere would be ambiguities between the label and the code.\n\n(We decided against clutter in the documentation such as Go string literals.\nIt is our hope that restricted character set should suit \n99% use cases out there. Please let us know if this is not the case.\nSee also \nhttps://github.com/golang/go/issues/16666 .)\n\nCondition Initialization\n------------------------\nGo allows you to initialize a condition and execute a simple statement before\n the check. For example, the initialization is common when checking if an\n item belongs to a map:\n \n```go\nif _, ok := someMap[3]; ok {\n\t...\n}\n```\n\nFollowing Go, Gocontracts also allows you to include the initialization in\nthe condition. The following code snippet shows you how to document \nthe initialization and what Gocontracts generates:\n\n```go\n// SomeFunc does something.\n//\n// SomeFunc requires:\n//  * _, ok := someMap[3]; ok\nfunc SomeFunc(someMap map[string]bool) {\n\t// Pre-condition\n\tif _, ok := someMap[3]; !ok {\n\t\tpanic(\"Violated: _, ok := someMap[3]; ok\")\n\t}\n\n\t// ...\n}\n``` \n\nConditioning on a Variable\n--------------------------\nUsually, functions either return an error if something went wrong or\nvalid results otherwise. To ensure the contracts conditioned on the error,\nuse implication and write:\n\n```go\n// Range returns a range of the timestamps available in the database.\n//\n// Range ensures:\n//  * err != nil || (empty || first \u003c last)\nfunc (t *Txn) Range() (first int64, last int64, empty bool, err error) {\n\t// ...\n}\n```\n\n. Gocontracts will generate the following code:\n\n```go\n// Range returns a range of the timestamps available in the database.\n//\n// Range ensures:\n//  * err != nil || (empty || first \u003c= last)\nfunc (t *Txn) Range() (first int64, last int64, empty bool, err error) {\n\t// Post-condition\n\tdefer func() {\n\t    if !(err != nil || (empty || first \u003c last)) {\n\t    \tpanic(\"Violated: err != nil || (empty || first \u003c last)\")\n\t    }\t\n\t}()\n\t\n\t// ...\n}\n```\n\nNote that conditioning can be seen as logical implication\n(`A ⇒ B` can be written as `¬A ∨ B`). In the above example, we chained\nmultiple implications as\n`err == nil ⇒ (¬ empty ⇒ first ≤ last)`.\n\nState Transitions\n-----------------\nWhen you want to formally define contracts on state transitions you need \nto capture the state _before_ and _after_ the function execution. However,\nGocontract's conventional post-conditions allow you only to access the state\n_afer_ the execution.\n\nIn order to capture the state _before_ the execution, you need to write a\n_preamble_. The preamble is a code snippet written in your documentation\nand automatically synced with the function body by gocontracts. The snippet\nmust follow the Godoc convetion and be indented with a whitespace (or a tab).\n\nThe preamble is executed just after the pre-condition(s) have been verified.\n\nHere is a brief (and admittedly a bit constructed) example \nwith the generated code already included: \n\n```go\npackage somepackage\n\n// increaseFirst increases the first element of the array.\n//\n// increaseFirst requires:\n//  * len(a) \u003e 0\n//\n// increaseFirst preamble:\n//  oldFirst := a[0]\n//\n// increaseFirst ensures:\n//  * a[0] == oldFirst + 1\nfunc increaseFirst(a []int) {\n\t// Pre-condition\n\tif !(len(a) \u003e 0) {\n\t\tpanic(\"Violated: len(a) \u003e 0\")\n\t}\n\n\t// Preamble starts.\n\toldFirst := a[0]\n\t// Preamble ends.\n\n\t// Post-condition\n\tdefer func() {\n\t\tif !(a[0] == oldFirst + 1) {\n\t\t\tpanic(\"Violated: a[0] == oldFirst + 1\")\n\t\t}\n\t}()\n\n\t// Implementation\n\ta[0]++\n}\n```\n\n\nToggling Contracts\n------------------\nWhen developing a library, it is important to give your users a possibility to toggle families of contracts so that they can adapt _your_ contracts to _their_ use case. For example, some contracts of your library should be verified in testing and in production, some should be verified only in testing of _their_ modules and others should be verified only in _your_ unit tests, but not in _theirs_.\n\nTo that end, you can use build tags to allow toggling of contracts at compile time (https://golang.org/pkg/go/build/#hdr-Build_Constraints). Define for each of the contract family a separate file which is built dependening on the build tag. In each of these files, define constant booleans (_e.g._, `InTest`, `InUTest`). Depending on the scenario, set these variables appropriately: in production scenario, `InTest = false` and `InUTest = false`, in the test scenario for others `InTest = true` and  `InUTest = false`, while in the scenario of _your_ unit tests `InTest = true` and `InUTest = true`.\n\nThe examples of the three files follow.\n\n`contracts_prod.go`:\n```go\n// +build prod,!test,!utest\n\npackage somepackage\n\nconst InTest = false\nconst InUTest = false\n```\n\n`contracts_testing.go`:\n```go\n// +build !prod,test,!utest\n\npackage somepackage\n\nconst InTest = true\nconst InUTest = false\n```\n\n`contracts_utest.go`:\n```go\n// +build !prod,!test,utest\n\npackage somepackage\n\nconst InTest = true\nconst InUTest = true\n```\n\nInclude each of these boolean constants in your contract conditions and `\u0026\u0026`\nwith the condition.\nFor example, this is how you extend the postcondition of the function `Range`\nwritten in the previous section to be verified only in _yours_ and _theirs_\ntests, but not in production:\n\n```go\n// Range returns a range of the timestamps available in the database.\n//\n// Range ensures:\n//  * !InTest || (err != nil || (empty || first \u003c= last))\nfunc (t *Txn) Range() (first int64, last int64, empty bool, err error) {\n\t...\n}\n```\n\nSince constant booleans are placed first in the conjunction,\nthe rest of the condition will not be evaluated incurring thus no\ncomputational overhead in the production at runtime.\n\nUsage\n=====\nGocontracts reads the Go file and outputs the modified source code to standard\noutput:\n\n```bash\ngocontracts /path/to/some/file.go\n```\n\nYou can modify the file in-place by supplying the `-w` argument:\n\n```bash\ngocontracts -w /path/to/some/file.go\n```\n\nIf you want to remove the contract checks from the code, supply the\n`-r` argument:\n\n```bash\ngocontracts -w -r /path/to/some/file.go\n```\n\nThe remove argument is particularly useful when you have a build system\nin place and you want to distinguish between the debug code and the\nrelease (production) code.\n\nBefore building the release code, run the gocontracts with `-r` to remove\nthe checks from the code.\n\nInstallation\n============\nWe provide x86 Linux binaries in the \"Releases\" section.\n\nTo compile from code, run:\n\n```bash\ngo get -u github.com/Parquery/gocontracts\n```\n\nDevelopment\n===========\n* Fork the repository to your user on Github.\n\n* Get the original repository:\n\t```bash\n\tgo get github.com/Parquery/gocontracts\n\t```\n\n* Indicate that the local repository is a fork:\n\t```bash\n\tcd gocontracts\n\tgit remote add fork https://github.com/YOUR-GITHUB-USERNAME/gocontracts.git\n\t```\n\n* Make your changes.\n\n* Push the changes from the local repository to your remote fork:\n\t ```bash\n\t git push fork\n\t ```\n\n* Create a pull request on Github and send it for review.\n\nVersioning\n==========\nWe follow [Semantic Versioning](http://semver.org/spec/v1.0.0.html).\nThe version X.Y.Z indicates:\n\n* X is the major version (backward-incompatible),\n* Y is the minor version (backward-compatible), and\n* Z is the patch version (backward-compatible bug fix).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FParquery%2Fgocontracts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FParquery%2Fgocontracts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FParquery%2Fgocontracts/lists"}