{"id":21296897,"url":"https://github.com/reddec/struct-view","last_synced_at":"2025-07-11T18:32:07.106Z","repository":{"id":81702667,"uuid":"200985576","full_name":"reddec/struct-view","owner":"reddec","description":"golang generators for structs","archived":false,"fork":false,"pushed_at":"2023-03-06T23:03:42.000Z","size":97,"stargazers_count":7,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-02T11:02:03.961Z","etag":null,"topics":["code-generation","events","golang","ring-buffer"],"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/reddec.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":"support/events/ws.go","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-07T06:20:24.000Z","updated_at":"2023-10-19T06:00:07.000Z","dependencies_parsed_at":"2024-06-21T16:33:08.843Z","dependency_job_id":"a2909cd1-7af8-4c88-af1b-b8c44ec73ca6","html_url":"https://github.com/reddec/struct-view","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/reddec/struct-view","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddec%2Fstruct-view","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddec%2Fstruct-view/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddec%2Fstruct-view/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddec%2Fstruct-view/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reddec","download_url":"https://codeload.github.com/reddec/struct-view/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddec%2Fstruct-view/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264870440,"owners_count":23676227,"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":["code-generation","events","golang","ring-buffer"],"created_at":"2024-11-21T14:30:40.703Z","updated_at":"2025-07-11T18:32:06.647Z","avatar_url":"https://github.com/reddec.png","language":"Go","readme":"# Tools to manipulate with structs\n\nInstallation: `go install github.com/reddec/struct-view/cmd/...@latest`\n\n* [Events generator](#events-generator)\n* [Cache generator](#cache-generator)\n* [Timed cache](#timed-cache)\n* [Binary encoding](#binary-gen)\n* [JSON array enum generator](#json-enum-array-gen)\n* [Ring buffer generator](#ring-buffer-generator)\n* struct-view\n\n## Events generator\n\n\n```\nUsage:\n  events-gen [OPTIONS] [Directories...]\n\nApplication Options:\n  -p, --package=     Package name (can be override by output dir) (default: events) [$PACKAGE]\n  -o, --output=      Generated output destination (- means STDOUT) (default: -) [$OUTPUT]\n  -P, --private      Make generated event structures be private by prefix 'event' [$PRIVATE]\n      --event-bus=   Generate structure that aggregates all events [$EVENT_BUS]\n  -m, --mirror       Mirror all events to the universal emitter [$MIRROR]\n  -f, --from-mirror  Create producer events as from mirror (only for event bus) [$FROM_MIRROR]\n  -i, --ignore-case  Ignore event case for universal source (--from-mirror) [$IGNORE_CASE]\n  -s, --sink         Make a sink method for event bus to subscribe to all events [$SINK]\n  -e, --emitter=     Create emitter factory [$EMITTER]\n  -l, --listener=    Create method to subscribe for all events (default: SubscribeAll) [$LISTENER]\n  -H, --hint=        Give a hint about events (eventName -\u003e struct name) [$HINT]\n  -c, --context      Add context to events [$CONTEXT]\n\nHelp Options:\n  -h, --help         Show this help message\n```\n\n### Basic usage\n\n\nYou should declare go file with types that will be used as event types. For example:\n\n```go\npackage basic\n//go:generate events-gen -p basic -o events.go .\n\n// event:\"UserCreated\"\n// event:\"UserRemoved\"\ntype User struct {\n\tID   int64\n\tName string\n}\n\n// event:\"UserSubscribed\"\n// event:\"UserLeaved\"\ntype Subscription struct {\n\tUserID int64\n\tMail   string\n}\n```\n\nMagic comment `event:\"\"` gives an instruction to the event generator hint that this type is used as parameter for event\nwith name defined in commas (`UserCreated`, `UserRemoved` for type `User`).\n\nYou may use option `ref` (like `event:\"EventName,ref\"`) to use payload by reference.\n\nInstruction for go generator `events-gen -p basic -o events.go .` tells us to generate events to file `events.go` with\npackage `basic` and look for source files in current (`.`) directory. \n\nFeel free to look in **examples/basic** directory to see generated file.\n\nFinally you can embed event emitter to you struct like this: \n```go\npackage basic\n\ntype UserApp struct {\n    Subscribed UserSubscribed\n    Leaved     UserLeaved\n    //...\n}\n```\n\n\n### Advanced usage\n\nQuite often we need to have some aggregated event source (aka event bus) that aggregates several event emitters in one \nplace.\n\nBy using example above we may imagine situation that all our events actually relates to the one application. Let's call\nit `App`.\n\nWe also what to let other developers easily extend logic of our product by adding event listeners and (probably) let them\nalso emits events using only one source of truth.\n\nIn terms of `events-gen` this pattern called `EventBus`.\n\nLet's improve our previous application and change generator invocation to \n`events-gen --event-bus Events -P -p advance -o events.go .`. It means generate event bus (events aggregator) \ncalled `Events` and make all generated definitions private (`-P`) except bus itself. We also changed package to `advance`\nso you may look into **examples/advance**.\n\n\u003e To mark generated structures as a private (`-P`) is completely optional, however, it's common case that if we already aggregated\n\u003e our events to the one structure then we probably don't want to expose events objects to outer world except only through event bus.\n\nGenerated event bus will looks something like\n\n```go\ntype Events struct {\n    UserCreated    eventUserCreated\n    UserRemoved    eventUserRemoved\n    UserSubscribed eventUserSubscribed\n    UserLeaved     eventUserLeaved\n}\n``` \n\nFinally you can embed event bus to you struct like this: \n```go\npackage advance\n\ntype App struct {\n    Events Events\n    //...\n}\n```\n\n### Multiple packages as a source\n\nSometimes you need to create events for types that you should not modify (add comments) or for types from different\npackages. \n\n`events-gen` supports as positional arguments multiple source directories that needs to be scanned. Also it is possible\nto give generator a \"hint\": expected event name and type. In this case generator will create events object for types that\nmatches hints as well as marked by comments.\n\nAs an addition to the example above let's imagine other package called `transactions` located in `../transactions` directory\nwith types `UserTX` and `BankTX` that we want to use as our events `UserTxCreated` and `BankTxCreated`. So we need to modify\ngenerator command (used example from basic to reduce number flags, however, you are not restricted in that) as: \n\n`events-gen -p basic -o events.go -H UserTxCreate:UserTX -H BankTxCreated:BankTX . ../transactions` \n\n### Mirroring and integration\n\nEvent-based approach for complex systems most of the time means integration with other external, legacy or just other\ncomponents. Common case for enterprise solution is to use integration bus (IBM MQ, RabbitMQ, ....) as a transport for events.\n\nFor such cases you may use mirroring (`-m`) as well as global sink (`-m`). Both ways let you consume events in unified \nway without caring about types.\n\nThose approaches very similar to each other, however, mirroring (`-m`) is a bit faster but supports only one sink and \nglobal sink (`-s`) that just subscribes to all events and has no limits for the number of listeners.\n\nSo for the example above generator will create:\n\n**mirroring** (`-m`)\n\n```go\nfunc EventsWithMirror(mirror func(eventName string, payload interface{})) *Events \n```\n\n**global sink** (`-s`)\n\n```go\nfunc (bus *Events) Sink(sink func(eventName string, payload interface{})) *Events\n```\n\nFunction parameters are self-explainable, but:\n\n* `eventName` - name of event (`UserCreated`, `UserRemoved`,...)\n* `payload` - original event object (not reference, a value)\n\n\n#### From mirror\n\nAll described above were about consuming events made by our generated events bus. However, you may want also\ntransparently integrate external system used as source of events and propagate them to the local instance. For example,\nyou may want use notification from a message broker (RabbitMQ, IBM MQ, HTTP REST,...) as internal events.\n\n```\n+----------------+                +-----------+  \u003c--- emit  --- +-----------+\n| exteral system | --- event ---\u003e | event bus |                 | component |\n+----------------+    (as emit)   +-----------+  --- listen --\u003e +-----------+\n```   \n\nIn terms of `events-generator` such approach called `FromMirror` and it's available ony together with `EventBus`.\n\nGenerated code could be a bit tricky, however, to generate `FromMirror` handlers just add `-f` flag to the generator.\n It will produce (for example above) methods for events:\n\n**universal emitter**\n\n`func (ev *Events) Emit(eventName string, payload interface{})`\n\nEmits event by name. Payload should event type (reference or value). Silently drops invalid parameters:\nunknown event, incorrect payload.\n\n**universal payload fabric**\n\n`func (ev *Events) Payload(eventName string) interface{}`\n\nCreates payload value (reference) by event name or returns nil.\n\n\nBoth of this methods require case-sensitive event name, however, by flag `-i` it can be switched to case-insensitive mode.\n\n### Emitter\n\nIt might be useful to use emitter in an external code or already existent code, however, use instance of a `EventBus`\ncould be not an ideal decision due to increase of code coupling.\n\nFor that reason you may create additional emitter by flag `-e \u003cEmitterFunc\u003e` that will generate additional method `EmitterFunc`\nin a `EventBus` and additional structure that aggregates all `Emit()` methods in one place. By using this approach you\nmay require an interface in your code instead of exact implementation.\n\nSo for the basic example, described above, `-e Emitter` the implementation of this interface will be generated \n(note: interface by itself will not be generated due to a best-practice \"accept interface, return structure\"):\n\n\n```go\ntype Sample interface {\n    UserCreated(payload User)\n    UserRemoved(payload User)\n    UserSubscribed(payload Subscription)\n    UserLeaved(payload Subscription)\n}\n```\n\n\n### Listener\n\nTo subscribe on all events exists method `SubscribeAll`, however, name of the method could be overloaded by \n`-l \u003clistener method\u003e` flag. If method name is empty, method will not be generated.\n\n### Context\n\nTo add `context` argument for all events, add flag `-c` \n\n## Cache generator\n\nGenerates multi-level cache for key-value data with a separate synchronization unit per value.\n\n```\nUsage:\n  cache-gen [OPTIONS]\n\nApplication Options:\n  -p, --package=      Package name (can be override by output dir) (default: cache) [$PACKAGE]\n  -o, --output=       Generated output destination (- means STDOUT) (default: -) [$OUTPUT]\n  -k, --key-type=     Key type [$KEY_TYPE]\n  -v, --value-type=   Value type [$VALUE_TYPE]\n  -i, --key-import=   Import for key type [$KEY_IMPORT]\n  -I, --value-import= Import for value type [$VALUE_IMPORT]\n  -t, --type-name=    Typename for cache (default: Manager) [$TYPE_NAME]\n\nHelp Options:\n  -h, --help          Show this help message\n```\n\n**Common use-case**: you have a service that contains user profiles (identified by ID) and you want to cache results\nto prevent multiple requests for the same user. However, you expect that your code could be used from multiple\nthreads/gorountines so parallel requests to different users should not block each-other, but code should make only\none request per unique user id.\n\nExample:\n\n```\ncache-gen -p users -k int64 -v *UserProfile -I github.com/MyCompany/MyTypes -o user_cached.go\n```\n\n* `-p users` sets package name to `users`\n* `-k int64` sets key type (user id) to int64\n* `-v *UserProfile` sets value type (user profile) as a ref to user-defined type\n* `-I github.com/MyCompany/MyType`  sets import for user-defined package for value type (`UserProfile`)\n* `-o user_cached.go` sets output to file named `user_cached.go`\n\nType name (`-t`) not set so default name will be used (`Manager`).\n\nResult (functions body omitted):\n\n\n```go\npackage cache\n\nimport (\n\t\"context\"\n\tmytypes \"github.com/MyCompany/MyTypes\"\n\t\"sync\"\n)\n\ntype UpdaterManager interface {\n\tUpdate(ctx context.Context, key int64) (*mytypes.UserProfile, error)\n}\ntype UpdaterManagerFunc func(ctx context.Context, key int64) (*mytypes.UserProfile, error)\n\nfunc (fn UpdaterManagerFunc) Update(ctx context.Context, key int64) (*mytypes.UserProfile, error) {} // body omitted\n\nfunc NewManagerFunc(updateFunc UpdaterManagerFunc) *Manager {} // body omitted\n\nfunc NewManager(updater UpdaterManager) *Manager {} // body omitted\n\ntype Manager struct {\n    // fields omitted\n}\n\nfunc (mgr *Manager) Find(key int64) *cacheManager {}\nfunc (mgr *Manager) FindOrCreate(key int64) *cacheManager {}\nfunc (mgr *Manager) Get(ctx context.Context, key int64) (*mytypes.UserProfile, error) {}\nfunc (mgr *Manager) Set(key int64, value *mytypes.UserProfile) {}\nfunc (mgr *Manager) Purge(key int64) {}\nfunc (mgr *Manager) PurgeAll() {}\nfunc (mgr *Manager) Snapshot() map[int64]*mytypes.UserProfile {}\n\ntype cacheManager struct {\n    // fields omitted    \n}\n\nfunc (cache *cacheManager) Valid() bool {}\nfunc (cache *cacheManager) Invalidate() {}\nfunc (cache *cacheManager) Key() int64 {}\nfunc (cache *cacheManager) Get() *mytypes.UserProfile {}\nfunc (cache *cacheManager) Ensure(ctx context.Context) (*mytypes.UserProfile, error) {}\nfunc (cache *cacheManager) Set(value *mytypes.UserProfile) {}\nfunc (cache *cacheManager) Update(ctx context.Context, force bool) error {}\n```\n\nSee full example in `examples/cache`\n\n\n## Timed cache\n\nGenerate simple cache with expiration time\n\n```\nUsage:\n  timed-cache [OPTIONS]\n\nApplication Options:\n  -p, --package=      Package name (can be override by output dir) (default: cache) [$PACKAGE]\n  -o, --output=       Generated output destination (- means STDOUT) (default: -) [$OUTPUT]\n  -v, --value-type=   Value type [$VALUE_TYPE]\n  -I, --value-import= Import for value type [$VALUE_IMPORT]\n  -t, --type-name=    Typename for cache (default: Manager) [$TYPE_NAME]\n  -a, --array         Is value should be an array [$ARRAY]\n\nHelp Options:\n  -h, --help          Show this help message\n\n```\n\n## Binary gen\n\nGenerate very simple static binary marshal/unmarshal for struct. Unknown fields are ignored. Goal is support same\nencoding/decoding with C/C++ structures with same encoding layout (aka: result should be decodable on Big endian machines\nlike `(struct *my_type)(buffer)`).\n\nCurrently supported types:\n\n* `uint8`, `uint16`, `uint32`, `uint64`\n\n```\nUsage:\n  binary-gen [OPTIONS]\n\nApplication Options:\n  -o, --output=    Generated output destination (- means STDOUT) (default: -) [$OUTPUT]\n  -t, --type-name= TypeName for generator (default: Manager) [$TYPE_NAME]\n\nHelp Options:\n  -h, --help       Show this help message\n\n```\n\nsee [examples/binarygen](examples/binarygen) directory\n\n## JSON-enum array gen\n\nGenerates type alias, validator for values (according to entered values) and custom JSON Unmarshal with embedded checks.\n\n\n```\nUsage:\n  json-enum-gen [OPTIONS] [Values...]\n\nApplication Options:\n  -p, --package=     Package name (can be override by output dir) (default: enum) [$PACKAGE]\n  -o, --output=      Generated output destination (- means STDOUT) (default: -) [$OUTPUT]\n  -t, --type-name=   Enum name (default: Manager) [$TYPE_NAME]\n  -s, --source-type= Source type name (default: string) [$SOURCE_TYPE]\n\nHelp Options:\n  -h, --help         Show this help message\n\n```\n\n\nFor example you need to check incoming request to play some channel (ex: podcast). Request message will look like\n\n```json\n{\n  \"channel\" : \"channel name\",\n  \"speed\": 1 \n}\n```\n\nYou want to allow your customers choose only specific values for speed: 0.5, 1, 1.5, 2, 2.5\n\nCommon solution is to create enum, defined possible values, generate JSON wrapper. But **it will bring mess to your code**\nwith meaningless values like `Channel0.5 = 0.5; Channel1 = 1` and so on.\n\nThis command will let you keep clean code as much as possible:\n\n```\njson-enum-gen -s int -o speed.go -t Speed 0.5 1 1.5 2 2.5\n``` \n\nwill generate code:\n\n\n```go\n// Code generated by json-enum-gen. DO NOT EDIT.\n//go:generate json-enum-gen -s int -t Speed 0.5 1 1.5 2 2.5\npackage enum\n\nimport (\n        \"encoding/json\"\n        \"errors\"\n)\n\ntype Speed int\n\nfunc (v Speed) Get() int {\n        return int(v)\n}\nfunc (v Speed) IsValid() bool {\n        switch int(v) {\n        case 0.5, 1, 1.5, 2, 2.5:\n                return true\n        default:\n                return false\n        }\n}\nfunc (v *Speed) UnmarshalJSON(data []byte) error {\n        var parsed int\n        err := json.Unmarshal(data, \u0026parsed)\n        if err != nil {\n                return err\n        }\n        typed := Speed(parsed)\n        if !typed.IsValid() {\n                return errors.New(\"Invalid value for type Speed. Possible options are: 0.5, 1, 1.5, 2, 2.5\")\n        }\n        *v = typed\n        return nil\n}\n\n```\n\n## Sync map gen\n\nGenerates Java-like thread-safe map\n\nGenerated code will implement next interface:\n\n```go\npackage sample\n\ntype UpdaterFunc func(key KeyType) (ValueType, error)\n\ntype SyncItem interface {\n    Valid() bool\n    Invalidate() \n    Key() KeyType\n    Get() ValueType\n    Set(value ValueType)\n    Ensure(updater UpdaterFunc) (ValueType, error)\n}\n\ntype SyncMap interface{\n    Find(key KeyType) SyncItem\n    FindOrCreate(key KeyType) SyncItem\n    Get(key KeyType, construct UpdaterFunc) (ValueType, error)\n    Set(key KeyType, value ValueType)\n    Purge(key KeyType)\n    PurgeAll()\n    Snapshot() map[KeyType]ValueType\n} \n```\n\nUsage\n\n```\nUsage:\n  syncmap-gen [OPTIONS]\n\nApplication Options:\n  -p, --package=      Package name (can be override by output dir) (default: cache) [$PACKAGE]\n  -o, --output=       Generated output destination (- means STDOUT) (default: -) [$OUTPUT]\n  -k, --key-type=     Key type [$KEY_TYPE]\n  -v, --value-type=   Value type [$VALUE_TYPE]\n  -i, --key-import=   Import for key type [$KEY_IMPORT]\n  -I, --value-import= Import for value type [$VALUE_IMPORT]\n  -t, --type-name=    TypeName for cache (default: Manager) [$TYPE_NAME]\n\nHelp Options:\n  -h, --help          Show this help message\n```\n\n### Params gen\n\n\nScans struct methods and generate wrappers for all parameters for all exported methods\n\nExample:\n\n```go\n\ntype App struct {}\n\nfunc (app *App) Sum(a, b, c int) int {return a + b +c}\n```\n\ninvoke\n\n`params-gen -t App -o params.go`\n\nwill generate\n\n\n```go\ntype SumParams struct {\n\tA int `form:\"a\" json:\"a\" xml:\"a\" yaml:\"a\"`\n\tB int `form:\"b\" json:\"b\" xml:\"b\" yaml:\"b\"`\n\tC int `form:\"c\" json:\"c\" xml:\"c\" yaml:\"c\"`\n}\n\nfunc (sp *SumParams) Invoke(app *App) int {\n\treturn app.Sum(sp.A, sp.B, sp.C)\n}\n```\n\n\n\nUsage\n\n```\nUsage:\n  params-gen [OPTIONS]\n\nApplication Options:\n  -p, --package=   Package name (can be override by output dir) [$PACKAGE]\n  -o, --output=    Generated output destination (- means STDOUT) (default: -) [$OUTPUT]\n      --dir=       Directory to scan (default: .) [$DIR]\n  -t, --type-name= TypeName for cache (default: Manager) [$TYPE_NAME]\n\nHelp Options:\n  -h, --help       Show this help message\n\n```\n\n\n## Ring buffer generator\n\nClassical fixed-buffer circle (ring) container, where new data overwrites old one. [Wikipedia](https://en.wikipedia.org/wiki/Circular_buffer).\n\nComplexity\n\n| Operation | Complexity |\n|-----------|------------|\n| Add       |  O(1)      |\n| Get       |  O(1)      |\n| Remove    |  N/A       |\n| Copy      |  O(N)      |\n\n\n**Example:**\n\nGenerate ring buffer for `int` type\n \n`ring-buffer-gen -p abc -t int --name IntBuffer`\n\nwill produce (implementation details omitted):\n\n```go\npackage abc\n\n// New instance of ring buffer\nfunc NewIntBuffer(size uint) *IntBuffer {}\n\n// Wrap pre-allocated buffer to ring buffer\nfunc WrapIntBuffer(buffer []int) *IntBuffer {}\n\n// Ring buffer for type int\ntype IntBuffer struct {\n\tseq  uint64\n\tdata []int\n}\n\n// Add new element to the ring buffer. If buffer is full, the oldest element will be overwritten\nfunc (rb *IntBuffer) Add(value int) {}\n\n// Get element by index. Negative index is counting from end\nfunc (rb *IntBuffer) Get(index int) (ans int) {}\n\n// Length of used elements. Always in range between zero and maximum capacity\nfunc (rb *IntBuffer) Len() int {}\n\n// Clone of ring buffer with shallow copy of underlying buffer\nfunc (rb *IntBuffer) Clone() *IntBuffer {}\n\n// Flatten copy of underlying buffer. Data is always ordered in an insertion order\nfunc (rb *IntBuffer) Flatten() []int {}\n\n```\n\n**Usage**\n\n```\nUsage:\n  ring-buffer-gen [OPTIONS]\n\nApplication Options:\n  -p, --package=      Package name (can be override by output dir) (default: enum) [$PACKAGE]\n  -o, --output=       Generated output destination (- means STDOUT) (default: -) [$OUTPUT]\n  -t, --type-name=    Type name to wrap [$TYPE_NAME]\n      --name=         Result structure name (default: RingBuffer) [$NAME]\n      --synchronized  Make collection be synchronized [$SYNCHRONIZED]\n  -i, --import=       Import for type [$IMPORT]\n  -n, --notify        Notify events in case of updates [$NOTIFY]\n\nHelp Options:\n  -h, --help          Show this help message\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freddec%2Fstruct-view","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freddec%2Fstruct-view","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freddec%2Fstruct-view/lists"}