{"id":26380460,"url":"https://github.com/sirkon/opgen","last_synced_at":"2025-03-17T05:31:48.079Z","repository":{"id":61626525,"uuid":"543505698","full_name":"sirkon/opgen","owner":"sirkon","description":"Option builder generator.","archived":false,"fork":false,"pushed_at":"2022-10-01T07:57:18.000Z","size":20,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-06-20T02:10:32.370Z","etag":null,"topics":["codegen","codegeneration","codegenerator","go","golang"],"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/sirkon.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2022-09-30T08:39:01.000Z","updated_at":"2022-09-30T18:10:54.000Z","dependencies_parsed_at":"2022-10-18T17:30:40.192Z","dependency_job_id":null,"html_url":"https://github.com/sirkon/opgen","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/sirkon%2Fopgen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirkon%2Fopgen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirkon%2Fopgen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sirkon%2Fopgen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sirkon","download_url":"https://codeload.github.com/sirkon/opgen/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243980833,"owners_count":20378474,"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":["codegen","codegeneration","codegenerator","go","golang"],"created_at":"2025-03-17T05:31:47.384Z","updated_at":"2025-03-17T05:31:48.070Z","avatar_url":"https://github.com/sirkon.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# opgen\n\nOption builder generator.\n\n## Installation.\n\n```shell\ngo get github.com/sirkon/main@latest\n```\n\n## Rationale.\n\nThere are so-called functional options described nicely in this [article](https://golang.cafe/blog/golang-functional-options-pattern.html).\n\nBut imagine a situation where we have a custom buffered io package where both reader and writer need a way to set up\na buffer size and there will be also options that are reader and writer specific. Something like:\n\n\n```go\nr, err := cio.NewBufferedReader(\n\tr, \n\tcio.WithBufferSize(size), \n\tcio.WithStartPosition(pos), \n\tcio.WithMaxReadPosition(readend), // This is reader specific options. \n)\n\n...\n\nw, err := cio.NewBufferedWriter(w, cio.WithBufferSize(size))\n```\n\nThis code will not compile because options for reader and writer will have different type in order to prevent\n`cio.WithMaxReadPosition` usage for writer.\n\nThere are ways to overcome this using generics: [article](https://golang.design/research/generic-option/)\n\nBut in the end the usage will be not exactly nice – shared options to have mandatory type parameter:\n\n```go\nr, err := cio.NewBufferedReader(r, cio.WithBufferSize[*BufferedReader](size))\n```\n.\n\nThis utility provides a different way to functional options: we don't set options directly, we fulfill them with a \ngenerated builder:\n\n```go\nr, err := cio.NewBufferedReader(r, cio.BufferedReaderOptions().BufferSize(size))\n```\n\nwhere builders for reader and writer will have different types and therefore no type clashes.\n\n## How to use.\n\nOption builder generation requires a package with Go code where you set up options for each type.\n\nImagine we have `github.com/company/cio` repository with `BufferedReader` type in it. Then we create a package\n`github.com/company/cio/internal/options` with a set of files in it.\n\n```go\n// github.com/company/cio/internal/options/buffered_reader_options.go\npackage options\n\nimport (\n\t\"log\"\n)\n\n// BufferedReaderBufferSize sets a buffer size, a default value is 4096.\nconst BufferedReaderBufferSize int = 4096\n\n// BufferedReaderLogger sets an error logger for a buffered reader.\nfunc BufferedReaderLogger(err error) {\n\tlog.Println(err)\n}\n```\n\nNow, generate a builder:\n\n```shell\nopgen -s internal/options -d options_gen.go BufferedReader\n```\n\nIt will look for `BufferedReaderXXX` named constants (will provide a default value), variables (no default value) and\nfunctions (will provide a default value – the function itself) where a type of each option will match a type of constant/variable/function.\n\nThe builder will have methods in case of the file above:\n\n```go\nfunc (b BufferedReaderOptionsType) BufferSize(v int) BufferedReaderOptionsType { … }\nfunc (b BufferedReaderOptionsType) Logger(v func(error)) BufferedReaderOptionsType { … }\n```\n\nAnd the usage is supposed to be like this:\n\n```go\nv, err := cio.NewBufferedReader(\n    r, \n    cio.BufferedReaderOptions().\n        BufferSize(8192).\n        Logger(func (err error) {\n            fmt.Println(err)\n        }), \n)\n```\n.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsirkon%2Fopgen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsirkon%2Fopgen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsirkon%2Fopgen/lists"}