{"id":13564954,"url":"https://github.com/viant/toolbox","last_synced_at":"2025-04-13T14:00:21.029Z","repository":{"id":57480743,"uuid":"61064110","full_name":"viant/toolbox","owner":"viant","description":"Toolbox - go utility library","archived":false,"fork":false,"pushed_at":"2024-12-10T21:02:01.000Z","size":1210,"stargazers_count":200,"open_issues_count":5,"forks_count":30,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-04-06T11:05:21.491Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/viant.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","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}},"created_at":"2016-06-13T19:33:35.000Z","updated_at":"2025-03-27T23:52:38.000Z","dependencies_parsed_at":"2024-03-17T21:49:33.174Z","dependency_job_id":"4f300520-ae64-4313-a878-19a56cc2b46e","html_url":"https://github.com/viant/toolbox","commit_stats":{"total_commits":1045,"total_committers":28,"mean_commits":37.32142857142857,"dds":"0.17511961722488034","last_synced_commit":"849d06c4e1aede4346d63b99849d6cd731433bbc"},"previous_names":[],"tags_count":83,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viant%2Ftoolbox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viant%2Ftoolbox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viant%2Ftoolbox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viant%2Ftoolbox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/viant","download_url":"https://codeload.github.com/viant/toolbox/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248724585,"owners_count":21151560,"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-01T13:01:38.505Z","updated_at":"2025-04-13T14:00:21.001Z","avatar_url":"https://github.com/viant.png","language":"Go","readme":"# Toolbox - go utility library\n\n[![GoReportCard](https://goreportcard.com/badge/github.com/viant/toolbox)](https://goreportcard.com/report/github.com/viant/toolbox)\n[![GoDoc](https://godoc.org/github.com/viant/toolbox?status.svg)](https://godoc.org/github.com/viant/toolbox)\n\nThis library is compatible with Go 1.8+\n\nPlease refer to [`CHANGELOG.md`](CHANGELOG.md) if you encounter breaking changes.\n\n- [Motivation](#Motivation)\n- [Collection Utilities](#Collection-Utilities)\n- [Converter \u0026\u0026 Conversion Utilities](#Conversion-Utilities)\n- [Struct Utilities](#Struct-Utilities)\n- [Function Utilities](#Function-Utilities)\n- [Time Utilities](#TimeUtilities)\n- [Storage API](#storage)\n- [Data substitution](data/)\n- [Text Utilities](text/)\n- [ServiceRouter](#ServiceRouter)\n- [Decoder and Encoder](#DecoderandEncoder)\n- [Logger](#Logger)\n- [BatchLimiter](#BatchLimiter)\n- [AST Based FileSetInfo](#ast-based-filesetinfo)\n- [License](#License)\n- [Credits and Acknowledgements](#Credits-and-Acknowledgements)\n\n\n\n## Motivation\n\nThis library was developed as part of [Datastore Connectivity](https://github.com/viant/dsc/) and Testibility libraries: ([Assertly](https://github.com/viant/assertly),  [Datastore testing](https://github.com/viant/dsunit/), [End to end testing](https://github.com/viant/endly/)) \nas a way to share utilities, and other abstractions that may be useful in other projects.\n\n\n\u003ca name=\"Collection-Utilities\"\u003e\u003c/a\u003e\n\n### Collection Utilities\n\n\n#### Iterator\n\nExample\n```go\n\tslice := []string{\"a\", \"z\", \"c\"}\n\titerator := toolbox.NewSliceIterator(slice)\n    value := \"\"\n    for iterator.HasNext() {\n        iterator.Next(\u0026value)\n        ...\n    }\n```\n\n#### Slice utilities\n\nThe following methods work on **any slice type.**\n\n**ProcessSlice**\n\nExample\n```go\n\tvar aSlice interface{}\n\t\n\ttoolbox.ProcessSlice(aSlice, func(item interface{}) bool {\n    \t\t...\n    \t\treturn true //to continue to next element return true\n    })\n\t\n```\n\n\n**ProcessSliceWithIndex**\n\nExample:\n```go\n\tvar aSlice interface{}\n\t\n\ttoolbox.ProcessSlice(aSlice, func(index int, item interface{}) bool {\n    \t\t...\n    \t\treturn true //to continue to next element return true\n    })\n\t\n```\n\n\n**IndexSlice**\n\nExample:\n```go\n    type Foo struct{\n\t\tid int\n\t\tname string\n\t}\n\n\tvar aSlice = []Foo{ Foo{1, \"A\"}, Foo{2, \"B\"} }\n\tvar indexedMap = make(map[int]Foo)\n\t\n\ttoolbox.IndexSlice(aSlice, indexedMap, func(foo Foo) int {\n\t\treturn foo.id\n\t})\n\t\n\t\n```\n\n\n**CopySliceElements**\n\nExample:\n```go\n   source := []interface{}{\n   \t\t\"abc\", \"def\", \"cyz\",\n   \t}\n   \tvar target = make([]string, 0)\n   \ttoolbox.CopySliceElements(source, \u0026target)\n\t\n\t\n```\n\n\n**FilterSliceElements**\n\nExample:\n```go\n\tsource := []interface{}{\n\t\t\"abc\", \"def\", \"cyz\",\"adc\",\n\t}\n\tvar target = make([]string, 0)\n\t\n\ttoolbox.FilterSliceElements(source, func(item string) bool {\n\t\treturn strings.HasPrefix(item, \"a\") //this matches any elements starting with a\n\t}, \u0026target)\n```\n\n\n**HasSliceAnyElements**\n\nExample:\n```go\n    source := []interface{}{\n\t\t\"abc\", \"def\", \"cyz\",\"adc\",\n\t}\n\ttoolbox.HasSliceAnyElements(source, \"cyz\")\n```\n\n\n**SliceToMap**\n\nExample:\n```go\n    var source = []Foo{ Foo{1, \"A\"}, Foo{2, \"B\"} }\n\tvar target = make(map[int]string)\n\ttoolbox.SliceToMap(source, target, func(foo Foo) int {\n\t\treturn foo.id\n\t},\n\tfunc(foo Foo) string {\n\t\treturn foo.name\n\t})\n\t\n\n```\n\n\n**TransformSlice**\n\nExample:\n```go\ntype Product struct{ vendor, name string }\n\tproducts := []Product{\n\t\tProduct{\"Vendor1\", \"Product1\"},\n\t\tProduct{\"Vendor2\", \"Product2\"},\n\t\tProduct{\"Vendor1\", \"Product3\"},\n\t\tProduct{\"Vendor1\", \"Product4\"},\n\t}\n\tvar vendors=make([]string, 0)\n\ttoolbox.TransformSlice(products, \u0026vendors, func(product Product) string {\n\t\treturn product.vendor\n\t})\n```\n\n\n#### Map utilities\n\n**ProcessMap**\n\nThe following methods work on **any map type.**\n\nExample:\n```go\n    var aMap interface{}\n\ttoolbox.ProcessMap(aMap, func(key, value interface{}) bool {\n    \t\t...\n    \t\treturn true //to continue to next element return true\n    })\n\n```\n\n\n**CopyMapEntries**\n\nExample:\n```go\n    type Foo struct{id int;name string}\n\t\n\tsource := map[interface{}]interface{} {\n\t\t1: Foo{1, \"A\"},\n\t\t2: Foo{2, \"B\"},\n\t}\n\tvar target = make   (map[int]Foo)\n\n\ttoolbox.CopyMapEntries(source, target)\n```\n\n\n**MapKeysToSlice**\n\nExample:\n```go\n    aMap := map[string]int {\n\t\t\"abc\":1,\n\t\t\"efg\":2,\n\t}\n\tvar keys = make([]string, 0)\n\ttoolbox.MapKeysToSlice(aMap, \u0026keys)\n```\n\t\n\n**GroupSliceElements**\n\nExample:\n```go\n\ttype Product struct{vendor,name string}\n    \tproducts := []Product{\n    \t\tProduct{\"Vendor1\", \"Product1\"},\n    \t\tProduct{\"Vendor2\", \"Product2\"},\n    \t\tProduct{\"Vendor1\", \"Product3\"},\n    \t\tProduct{\"Vendor1\", \"Product4\"},\n    \t}\n    \n    \tproductsByVendor := make(map[string][]Product)\n    \ttoolbox.GroupSliceElements(products, productsByVendor, func(product Product) string {\n    \t\treturn product.vendor\n    \t})\n```\n\n\n**SliceToMultimap**\n\n```go\t\n\ttype Product struct {\n    \t\tvendor, name string\n    \t\tproductId    int\n    \t}\n    \n    \tproducts := []Product{\n    \t\tProduct{\"Vendor1\", \"Product1\", 1},\n    \t\tProduct{\"Vendor2\", \"Product2\", 2},\n    \t\tProduct{\"Vendor1\", \"Product3\", 3},\n    \t\tProduct{\"Vendor1\", \"Product4\", 4},\n    \t}\n    \n    \tproductsByVendor := make(map[string][]int)\n    \ttoolbox.SliceToMultimap(products, productsByVendor, func(product Product) string {\n    \t\treturn product.vendor\n    \t},\n    \tfunc(product Product) int {\n    \t\treturn product.productId\n    \t})\n```\n\n\u003ca name=\"Conversion-Utilities\"\u003e\u003c/a\u003e\n### Converter \u0026\u0026 Conversion Utilities\n\nConverter transforms, data between any compatible or incompatible data type including struct/basicType/map/slice/interface{}\nOn top of that it supports custom tag to map field to target data type (i.e map)\n\n\n```go\n    myStruct :=  //some struct ...\n    myMap := make(map[string]interface{})\n    converter := NewConverter(dateLayout, keyTag) \t\n    err = converter.AssignConverted(\u0026myMap, myStruct)\n    err = converter.AssignConverted(myStruct, myMap) \n```\n\n\n\u003ca name=\"Struct-Utilities\"\u003e\u003c/a\u003e\n### Struct Utilities\n\n\n**ScanStructMethods**\n\nScan struct methods\n\n```go\n    service := New()\n    err = toolbox.ScanStructMethods(service, 1, func(method reflect.Method) error {\n\t\tfmt.Printf(\"%v\\n\", method.Name)\n\t\treturn nil\n\t})\n\n```\n\n**ProcessStruct**\n\nScan struct fields\n\n```go\n   service := New()\n    err = toolbox.ProcessStruct(service,\n        func(field reflect.StructField, value reflect.Value) error {\n            fmt.Print(field.Type.Name)\n            return nil\n    })\n\n```\n\n\n\u003ca name=\"Function-Utilities\"\u003e\u003c/a\u003e\n### Function Utilities\n\n\n\n\n\u003ca name=\"TimeUtilities\"\u003e\u003c/a\u003e\n### Time Utilities\t\n\t\n**DateFormatToLayout**\n\nJava date format style to go date layout conversion.\n\n\n```go\t\n    dateLaout := toolbox.DateFormatToLayout(\"yyyy-MM-dd hh:mm:ss z\")\n    timeValue, err := time.Parse(dateLaout, \"2016-02-22 12:32:01 UTC\")\n```\n\n**TimeAt** \n\nProvide dynamic semantic for creating time object\n\n```go\n    \n    tomorrow, err = TimeAt(\"tomorrow\")//tomorrow in local timezone\n    timeInUTC, err := TimeAt(\"2 days ago in UTC\") //or 2DayAgoInUTC\n    yesterdayUTC, err := TimeAt(\"yesterdayInUTC\")//yesterday in UTC\n    hoursAhead, err := TimeAt(\"50 hours ahead\")\n\n```\n\n**TimeDiff**\n\nProvide dynamic semantic for creating time object based on time dif\n\n```go\n\n    lastyear, _ := time.Parse(DateFormatToLayout(\"yyyy-MM-dd\"), \"2017-01-01\")\n    ts1, e := TimeDiff(lastyear, \"50 hours earlier\")\n    ts2, e := TimeDiff(lastyear, \"3 days before in Poland\")\n\t\n```\n\n**DayElapsed** \n```go\n\n    t0, _ := time.Parse(DateFormatToLayout(\"yyyy-MM-dd hh:mm:ss\"), \"2017-01-01 12:00:00\")\n    dayElapsedInT0, err := ElapsedDay(t0) //0.5\n\t\n```\n\n**ElapsedToday**\n```go\n\n    elapscedInLocalTz, err := ElapsedTodayInPct(\"\")  \n    elapscedInUTC, err := ElapsedToday(\"UTC\")\n\t\n```\n \n**RemainingToday**\n```go\n\n    elapscedInLocalTz, err := RemainingTodayInPct(\"\")\n    elapscedInUTC, err := RemainingToday(\"UTC\")\n\t\n```\n\n**AtTime**\n```go\n    atTime := \u0026AtTime{\n        WeekDay: \"*\",\n        Hour:    \"*\",\n        Minute:  \"10,30\",\n\t}\n    \n    //returns the nearest future time for xx:10 or xx:30  \n    nextScheduleTime := atTime.Next(time.Now)\n```\n\u003ca name=\"storage\"\u003e\u003c/a\u003e\n\n## Storage\n\n\n[Storage API](storage/README.md) provides unified way of accessing local or remote storage system.  \n\nThis API has been deprecated, please consider using [Abstract Storage](https://github.com/viant/afs)\n\n**Example** \n\n```go\n    import (\n    \t\"github.com/viant/toolbox/storage\"\n    \t_ \"github.com/viant/toolbox/storage/gs\"\t\n    )\n    \n    \n    destinationURL := \"gs://myBucket/set1/content.gz\"\n    destinationCredentialFile = \"gs-secret.json\"\n    storageService, err := storage.NewServiceForURL(destinationURL, destinationCredentialFile)\n\n```\n\n\n\n\u003ca name=\"tet\"\u003e\u003c/a\u003e\n### Text utilities\n\n\n**Text Case Format** \n\nYou can format with [format.Case](format/case.go).Format(text, destCase)\n\n```go\n    formatted := format.CaseLowerUnderscore.Format(text, format.CaseLowerCamel)\n```\n\n\n\u003ca name=\"Tokenizer\"\u003e\u003c/a\u003e\n### Tokenizer\n\n\u003ca name=\"ServiceRouter\"\u003e\u003c/a\u003e\n### ServiceRouter\n\nThis ServiceRouter provides simple WebService Endpoint abstractin and RESET Client utilities.\n\n\nTake as example of a ReverseService defined as follow\n\n```go\n\ntype ReverseService interface {\n        Reverse(values []int) []int \n}\n\ntype reverseService struct{}\n\nfunc (r *reverseService) Reverse(values []int) []int {\n\tvar result = make([]int, 0)\n\tfor i := len(values) - 1; i \u003e= 0; i-- {\n\t\tresult = append(result, values[i])\n\t}\n\n\treturn result\n}\n\n```\n\nIn order to define Endpoint for this service,  define a server, a router with the service routes;\n\n\n\n```qo\n\n\ntype Server struct {\n    service ReverseService\n    port string\n}\n\nfunc (s *Server) Start() {\n    \n    router := toolbox.NewServiceRouter(\n\t\ttoolbox.ServiceRouting{\n\t\t\tHTTPMethod: \"GET\",\n\t\t\tURI:        \"/v1/reverse/{ids}\",\n\t\t\tHandler:    s.service.Reverse,\n\t\t\tParameters: []string{\"ids\"}, \n\t\t},\n\t\ttoolbox.ServiceRouting{\n\t\t\tHTTPMethod: \"POST\",\n\t\t\tURI:        \"/v1/reverse/\",\n\t\t\tHandler:    s.service.Reverse,\n\t\t\tParameters: []string{\"ids\"},\n\t\t})\n\t\t\n        http.HandleFunc(\"/v1/\", func(writer http.ResponseWriter, reader *http.Request) {\n            err := router.Route(writer, reader)\n            if err != nil {\n                response.WriteHeader(http.StatusInternalServerError)\n            }\n        })\n    \n        fmt.Printf(\"Started test server on port %v\\n\", port)\n        log.Fatal(http.ListenAndServe(\":\"+port, nil))\n}\n\n```\n**ServiceRouting** parameters define handler parameters that can be extracted from URI, QueryString, or from Post Body (json payload)\nIn addition two special parameter names are supported: @httpRequest, @httpResponseWriter  to pass in request, and response object respectively.\n\n\nThe REST client utility invoking our reverse service may look as follow\n \n \n```go\n\n               var result = make([]int, 0)\n               err := toolbox.RouteToService(\"get\", \"http://127.0.0.1:8082/v1/reverse/1,7,3\", nil, \u0026result)\n               //...\n               err := toolbox.RouteToService(\"post\", \"http://127.0.0.1:8082/v1/reverse/\", []int{1, 7, 3}, \u0026result)\n\n``` \n\n\nBy default a service router uses reflection to call the matched routes handler, it is possible to avoid reflection overhead by providing the custom handler invoker.\n\n```go\n\n\nvar ReverseInvoker = func(serviceRouting *toolbox.ServiceRouting, request *http.Request, response http.ResponseWriter, uriParameters map[string]interface{}) error {\n\tvar function = serviceRouting.Handler.(func(values []int) []int)\n\tidsParam := uriParameters[\"ids\"]\n\tids := idsParam.([]string)\n\tvalues := make([]int, 0)\n\tfor _, item := range ids {\n\t\tvalues = append(values, toolbox.AsInt(item))\n\t}\n\tvar result = function(values)\n\terr := toolbox.WriteServiceRoutingResponse(response, request, serviceRouting, result)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n//...\n\n \n        router := toolbox.NewServiceRouter(\n\t\ttoolbox.ServiceRouting{\n\t\t\tHTTPMethod: \"GET\",\n\t\t\tURI:        \"/v1/reverse/{ids}\",\n\t\t\tHandler:    s.service.Reverse,\n\t\t\tParameters: []string{\"ids\"},\n\t\t\tHandlerInvoker: ReverseInvoker,\n\t\t})\n//...\t\t\n\t\t\n\n```\n\n\n\n\n\u003ca name=\"DecoderandEncoder\"\u003e\u003c/a\u003e\n### Decoder and Encoder \n\n#### Decoder\n\nThis library defines DecoderFactory interface to delegate  decoder creation, \nThis library comes with standard JSON and UnMarshaler (protobuf) factory implementation.\n\n Example\n  \n```go\n    factory :=toolbox.NewJsonDecoderFactory()\n    ....\n    \n    decoder := factory.Create(reader)\n    foo := \u0026Foo{}\n    err = decoder.Decode(foo)\n\n\n\n    marshalerFactory := toolbox.NewUnMarshalerDecoderFactory()\n    decoder := marshalerFactory.Create(reader)\n    foo := \u0026Foo{}\n    err = decoder.Decode(foo)\n```\n\n\n#### Encoder\n\nThis library defines EncoderFactory interface to delegate encoder creation, \nThis library comes with standard JSON and Marshaler (protobuf) factory implementation.\n\n Example\n  \n```go\n        factory :=toolbox.NewJsonEncoderFactory()\n        ....\n        buffer := new(bytes.Buffer)\n        \n        \n        decoder := factory.Create(buffer)\n        err = decoder.Encode(foo)\n    \n    \n    \n        marshalerFactory := toolbox.NewMarshalerEncoderFactory()\n        decoder := marshalerFactory.Create(buffer)\n        err = decoder.Encode(foo)\n```\n\n\n\n\u003ca name=\"Logger\"\u003e\u003c/a\u003e\n### Logger\n\n\nThis library provides a file logger implementation that optimizes writes.\nLog messages are queues until max queue size or flush frequency are met.\nOn top of that Ctrl-C also forces immediate log messages flush to disk.\n\nFile template support java style time format to manage rotation on the file name level.\n\n```go\n    logger, err := toolbox.NewFileLogger(toolbox.FileLoggerConfig{\n\t\tLogType:           \"test\",\n\t\tFileTemplate:      \"/tmp/test[yyyyMMdd-hhmm].log\",\n\t\tQueueFlashCount:   250,\n\t\tMaxQueueSize:      500,\n\t\tFlushFrequencyInMs: 2000,\n\t\tMaxIddleTimeInSec: 1,\n\t}, toolbox.FileLoggerConfig{\n       \t\tLogType:           \"transaction\",\n       \t\tFileTemplate:      \"/tmp/transaction[yyyyMMdd-hhmm].log\",\n       \t\tQueueFlashCount:   250,\n       \t\tMaxQueueSize:      500,\n       \t\tFlushFrequencyInMs:2000,\n       \t\tMaxIddleTimeInSec: 1,\n       \t},\n\t)\n\n    logger.Log(\u0026toolbox.LogMessage{\n        MessageType: \"test\",\n        Message:     message\n    })\n    \n    logger.Log(\u0026toolbox.LogMessage{\n            MessageType: \"transaction\",\n            Message:     message\n        })\n```\n\t\t\n\u003ca name=\"BatchLimiter\"\u003e\u003c/a\u003e\n### BatchLimiter\n\n\nThis library provides a batch limiter, that enables controling number of active go routines.\n\n\n```go\n\n     var tasks []*Task\n     var batchSize = 4\n\t limiter:= toolbox.NewBatchLimiter(batchSize, len(tasks))\n   \t for i, _ :=  range tasks {\n            go func(task *Task) {\n                    limiter.Acquire()\n                    defer limiter.Done()\n                    task.Run();\n        \t}(tasks[i])\n\t}\n\tlimiter.Wait()\n\n```\n\n### AST Based FileSetInfo \n\n\n```go\n    pkgPath := \"\"\n\tsource := path.Join(pkgPath)\n\tfilesetInfo, err :=toolbox.NewFileSetInfo(source)\n    myType := fileSetInfo.Type(\"MyType\")\n    fields := myType.Fields()\n    method := myType.Receivers\n``` \n\n\n\n\n## GoCover\n\n[![GoCover](https://gocover.io/github.com/viant/toolbox)](https://gocover.io/github.com/viant/toolbox)\n\n\t\n\t\n\u003ca name=\"License\"\u003e\u003c/a\u003e\n## License\n\nThe source code is made available under the terms of the Apache License, Version 2, as stated in the file `LICENSE`.\n\nIndividual files may be made available under their own specific license,\nall compatible with Apache License, Version 2. Please see individual files for details.\n\n\n\u003ca name=\"Credits-and-Acknowledgements\"\u003e\u003c/a\u003e\n\n##  Credits and Acknowledgements\n\n**Library Author:** Adrian Witas\n\n**Contributors:**\n","funding_links":[],"categories":["实用工具","Utilities","公用事业公司","工具库`可以提升效率的通用代码库和工具`","Go","實用工具","Utility","工具库"],"sub_categories":["高级控制台界面","Advanced Console UIs","Utility/Miscellaneous","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","实用程序/Miscellaneous","HTTP Clients","Fail injection","查询语","高級控制台界面","交流"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviant%2Ftoolbox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fviant%2Ftoolbox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviant%2Ftoolbox/lists"}