{"id":50691188,"url":"https://github.com/anatolelucet/tiq","last_synced_at":"2026-06-09T03:02:22.915Z","repository":{"id":319303667,"uuid":"1075248730","full_name":"AnatoleLucet/tiq","owner":"AnatoleLucet","description":"Modular Go Struct tags parser","archived":false,"fork":false,"pushed_at":"2025-10-17T17:15:04.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-18T16:58:22.428Z","etag":null,"topics":["extractor","go","golang","library","parser","reflection","struct","tags"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AnatoleLucet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-13T08:40:32.000Z","updated_at":"2025-10-18T16:04:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"53a467f5-81cd-4a88-ab5d-dbe55407c4a7","html_url":"https://github.com/AnatoleLucet/tiq","commit_stats":null,"previous_names":["anatolelucet/tiq"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/AnatoleLucet/tiq","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoleLucet%2Ftiq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoleLucet%2Ftiq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoleLucet%2Ftiq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoleLucet%2Ftiq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AnatoleLucet","download_url":"https://codeload.github.com/AnatoleLucet/tiq/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoleLucet%2Ftiq/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34089329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["extractor","go","golang","library","parser","reflection","struct","tags"],"created_at":"2026-06-09T03:02:22.071Z","updated_at":"2026-06-09T03:02:22.903Z","avatar_url":"https://github.com/AnatoleLucet.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\u003ccode\u003etiq\u003c/code\u003e\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003eModular Golang Struct tags parser that's actually useful.\u003c/p\u003e\n\n```go\ntype Config struct {\n\tUrl  string `env:\"name=URL, type=string\"`\n\tPort int    `env:\"name=PORT, type=port\"`\n}\n\ntype EnvSchema struct {\n\tName string `tag:\"env | get('name')\"`\n\tType string `tag:\"env | get('type')\"`\n}\n\nconf := Config{}\n\ninspector, err := tiq.Inspect(\u0026conf)\nfor _, field := range inspector.Fields() {\n    env, err := tiq.Parse[EnvSchema](field)\n\n    value := os.Getenv(env.Name)\n    field.Set(validate(env.Type, value))\n}\n\nconf.Url // now set to the value of the URL env var\nconf.Port // now set to the value of the PORT env var\n```\n\n## Installation\n\n```bash\ngo get github.com/AnatoleLucet/tiq\n```\n\n## Usage\n\n`tiq` is a modular Golang Struct tags parser. It offers a very simple [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) to extract what you need from tags, and multiple APIs to [inspect](#tiqinspect) and [update](#tiqset) user defined structs.\n\nThe DSL is designed to be as straightforward as possible for you to pick up and get what's happening at a glance, even if you've never used `tiq` before.\n\n```go\nimport (\n    \"github.com/AnatoleLucet/tiq\"\n)\n\n// User defined struct with tags that can by parsed by the `load()` function.\ntype Config struct {\n\tUrl  string `env:\"name=URL, type=string, optional\"`\n\tPort int    `env:\"name=PORT, type=port, oneof=8080|3000|5000\"`\n}\n\nfunc main() {\n    conf, err := load(\u0026Config{})\n\n    conf.Url // Now set to the value of the URL env var.\n    conf.Port // Now set to the value of the PORT env var.\n}\n\n\n// Define your schema and how to parse tags\ntype EnvSchema struct {\n    // Each field containing a `tag:\"\"` will be evaluated by tiq's DSL to\n    // extract what you need from the user defined tags.\n    // See \"The DSL\" section of the README to learn more.\n\tName     string   `tag:\"env | get('name')\"`\n\tType     string   `tag:\"env | get('type')\"`\n\tOptional bool     `tag:\"env | has('optional')\"`\n\tOneof    []string `tag:\"env | get('oneof') | split('|')\"`\n}\n\nfunc load[T any](conf *T) (*T, error) {\n    inspector, err := tiq.Inspect(conf)\n\n    for _, field := range inspector.Fields() {\n        env, err := tiq.Parse[EnvSchema](field)\n\n        value := os.Getenv(env.Name)\n\n        // Ideally you'd call a function to validate if the value\n        // is correct according what was parsed in the `env` variable:\n        // validate(env, value)\n\n        // .SetFrom() will convert the value to the target field's type using `github.com/AnatoleLucet/as`.\n        // Alternatively you could use .Set() to try and set the field's value directly without conversion.\n        field.SetFrom(value)\n    }\n\n    return conf\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eReal world example\u003c/summary\u003e\n\nIf you want to see `tiq` in action on a real project, checkout [environ](https://github.com/AnatoleLucet/environ/blob/19c756e16da1f6a2b8bbf3d614f9c56da2b264c0/environ.go#L26-L57), another project of mine, powered by `tiq`!\n\n\u003c/details\u003e\n\n### The DSL\n\nThe DSL is based on [ExprLang](https://expr-lang.org/), a simple but powerful expression language.\n\n\u003e Don't try to use functions from the official ExprLang docs, they probably won't work. Instead, take a look at `tiq`'s [function set](#functions) to find what you need!\n\n#### Basic syntax\n\n```bash\n# The most simple expression would look like this:\n`tag:\"123\"`\n# where `tag:\"...\"` is the Golang tag tiq will pick up for evaluation,\n# and `123` is the DSL expression tiq will evaluate.\n\n# To get a tag's value, simply use the name of the tag you want to get:\n`tag:\"mytag\"`\n# it will return `mytag`'s content unaltered (e.g. if given `mytag:\"content\"`, the expression above will return `content`).\n\n# Once you have the value you want to parse, you can use tiq's function set to extract entries and values from it:\n`tag:\"get(mytag, 'foo')\"`\n# here we pass `mytag`'s content to the `get()` function, and try to get the `foo` entry's value from it.\n# So when given `mytag:\"foo=bar\"`, the expression above will return `bar` (the value of the `foo` entry).\n\n# To chain one or more functions together, you can use ExprLang's pipe operator:\n`tag:\"mytag | get(\"foo\") | default(\"baz\")\"`\n# the pipe operator will pass the left operand's value as the first parameter the right operand.\n# What this means is that `\"foo=bar\" | get(\"foo\")` is equivalent to `get(\"foo=bar\", \"foo\")`.\n\n# To learn more about tiq's syntax, check out ExprLang's docs at https://expr-lang.org/docs/getting-started.\n# But remember most functions from ExprLang won't work because tiq uses its own functions set (described below).\n```\n\n#### Functions\n\n| Name        | Description                                                                                      | Usage                                    |\n| ----------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------- |\n| `get()`     | Gets an entry's value from a comma-separated key-value list.                                     | `get(\"foo=1, bar=2\", \"foo\") -\u003e 1`        |\n| `first()`   | Gets the first entry's value (or key if there's no value) from a comma-separated key-value list. | `first(\"foo=1, bar=2\") -\u003e 1`             |\n| `last()`    | Gets the last entry's value (or key if there's no value) from a comma-separated key-value list.  | `last(\"foo=1, bar=2\") -\u003e 2`              |\n| `nth()`     | Gets the nth entry's value (or key if there's no value) from a comma-separated key-value list.   | `nth(\"foo=1, bar=2\", 0) -\u003e 1`            |\n| `has()`     | Returns true or false if the entry is present in a comma-separated key-value list.               | `has(\"foo=1, bar=2\", \"bar\") -\u003e true`     |\n| `split()`   | Splits a string with the given separator.                                                        | `split(\"1\\|2\\|3\\|4\", \"\\|\") -\u003e [1 2 3 4]` |\n| `default()` | Returns a default value if the given value if `nil`.                                             | `default(nil, \"foo\") -\u003e \"foo\"`           |\n\n### `tiq.Inspect`\n\nThe inspector helps you crawl through a struct's fields, read tags from them, and update values accordingly.\n\n```go\ninspector, err := tiq.Inspect(\u0026mystruct)\n\n// Get a field by name\nfield, ok := inspector.Field(\"Name\")\n\nfield.Set(\"value\") // update the field's value\nfield.SetFrom(\"value\") // same as .Set() but converts the value to the field's type if necessary\nfield.Tag(\"mytag\") // returns the content of `mytag:\"content\"`\nfield.Tags() // returns every tags of the field in a map[string]string\n\n// Alternatively you could loop through every field on the struct:\nfor _, field := range inspector.Fields() {\n    // field.Set(\"value\")\n}\n```\n\n### `tiq.Parse`\n\nThe parser is how you retrieve what you want from tags with `tiq`. It takes a schema and a `tiq.Field` to parse tags on.\n\n```go\ntype EnvSchema struct {\n\tName     string   `tag:\"env | get('name')\"`\n\tOptional bool     `tag:\"env | has('optional')\"`\n\tOneof    []string `tag:\"env | get('oneof') | split('|')\"`\n}\n\nenv, err := tiq.Parse[EnvSchema](field) // field is usually retrieved via tiq.Inspect\n\nenv.Name // if `field` has a tag `env:\"name=foo\"`, this will be set to \"foo\", else \"\"\nenv.Optional // if `field` has a tag `env:\"optional\"`, this will be set to true, else false\nenv.Oneof // if `field` has a tag `env:\"oneof=one|two|three\"`, this will be set to [one two three], else []\n```\n\n### `tiq.Get`\n\nA simple static function to get a tag's content from anywhere.\n\n```go\ntype User struct {\n    Name string `json:\"name,omitempty\"`\n}\n\nvar user User\njson, err := tiq.Get(\u0026user, \"Name\", \"json\")\n\njson // \"name,omitempty\"\n```\n\n### `tiq.Set`\n\nA simple static function to set a field's content from anywhere.\n\n```go\ntype User struct {\n    Name string `json:\"name,omitempty\"`\n}\n\nvar user User\nerr := tiq.Set(\u0026user, \"Name\", \"Bob\")\n\nuser.Name // \"Bob\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanatolelucet%2Ftiq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanatolelucet%2Ftiq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanatolelucet%2Ftiq/lists"}