{"id":13764052,"url":"https://github.com/esemplastic/unis","last_synced_at":"2025-05-10T17:31:30.996Z","repository":{"id":57497044,"uuid":"90438490","full_name":"esemplastic/unis","owner":"esemplastic","description":"UNIS: A Common Architecture for String Utilities within the Go Programming Language.","archived":false,"fork":false,"pushed_at":"2017-05-09T16:17:33.000Z","size":81,"stargazers_count":70,"open_issues_count":2,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-08-03T15:06:06.142Z","etag":null,"topics":["golang","string","utility-wrapper"],"latest_commit_sha":null,"homepage":"https://godoc.org/github.com/esemplastic/unis","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/esemplastic.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":"2017-05-06T05:01:03.000Z","updated_at":"2022-09-27T09:55:16.000Z","dependencies_parsed_at":"2022-09-03T23:20:39.969Z","dependency_job_id":null,"html_url":"https://github.com/esemplastic/unis","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esemplastic%2Funis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esemplastic%2Funis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esemplastic%2Funis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esemplastic%2Funis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/esemplastic","download_url":"https://codeload.github.com/esemplastic/unis/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224982988,"owners_count":17402404,"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":["golang","string","utility-wrapper"],"created_at":"2024-08-03T15:01:11.127Z","updated_at":"2024-11-16T23:31:00.995Z","avatar_url":"https://github.com/esemplastic.png","language":"Go","readme":" ![](https://raw.githubusercontent.com/esemplastic/unis/master/logo.png) **A Common Architecture for String Utilities in Go.**\n\u003cp\u003e\n\u003ca href=\"https://travis-ci.org/esemplastic/unis\"\u003e\u003cimg src=\"https://api.travis-ci.org/esemplastic/unis.svg?branch=master\u0026style=flat-square\" alt=\"Build Status\"\u003e\u003c/a\u003e\n\u003ca href='https://codecov.io/gh/esemplastic/unis/branch/master'\u003e\u003cimg src='https://img.shields.io/codecov/c/github/esemplastic/unis/master.svg' alt='Coverage Status' /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/avelino/awesome-go\"\u003e\u003cimg src=\"https://img.shields.io/badge/awesome-%E2%9C%93-ff69b4.svg?style=flat-square\" alt=\"Awesome GoLang\"\u003e\u003c/a\u003e\n\u003ca href=\"http://goreportcard.com/report/esemplastic/unis\"\u003e\u003cimg src=\"https://img.shields.io/badge/report%20card%20-a%2B-006699.svg?style=flat-square\" alt=\"http://goreportcard.com/report/esemplastic/unis\"\u003e\u003c/a\u003e\n\u003ca href=\"https://godoc.org/github.com/esemplastic/unis\"\u003e\u003cimg src=\"https://img.shields.io/badge/docs-%20reference-5272B4.svg?style=flat-square\" alt=\"Docs\"\u003e\u003c/a\u003e\n\u003ca href=\"https://gitter.im/unis-go/Lobby#\"\u003e\u003cimg src=\"https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square\" alt=\"Chat\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n`UNIS` shares a common architecture and the necessary `interfaces` that will help you to refactor your project or application to a better place to work on. Choose one way to organise your `string utilities`, across your different projects.\n\nDevelopers can now, move forward and implement their own types of string utilities based on the UNIS architecture. \n\n**Apply good design patterns** from the beginning and you will be saved from a lot of work later on. Trust me.\n\n\n## Installation\n\nThe only requirement is the [Go Programming Language](https://golang.org/dl/).\n\n```sh\n$ go get -u github.com/esemplastic/unis\n```\n\n## What's inside?\n\n* `Processor`: Manipulates a receiver string, based on the implementation, and returns its new format.\n* `Validator`: Validates a receiver string, based on the implementation, and returns a boolean and an error.\n* `Divider`: Splits a receiver string into two pieces, based on the implementation, and returns them.\n* `Joiner`: Receives two strings, combines them into one, based on the implementation, and returns it.\n\n## Getting started\n\nUNIS contains some basic implementations that you can see and learn from!\n\nFirst of all, let's take look at the `unis.Processor` interface. \n```go\n// Processor is the most important interface of this package.\n//\n// It's being used to implement basic string processors.\n// Users can use all these processors to build more on their packages.\n//\n// A Processor should change the \"original\" and returns its result based on that.\ntype Processor interface {\n\t// Process accepts an \"original\" string and returns a result based on that.\n\tProcess(original string) (result string)\n}\n```\nAs you can see, it contains just a function which accepts a string and returns a string.\nEverything implements that interface only -- **No, please don't close the browser yet!**\n\n### TIP: Convert standard `strings` or `path` functions to `UNIS.Processor`\n\nThe majority of `strings` and `path` packages contain functions like \n`strings.ToLower` which is a type of `func(string) string`, guess what -- `unis.ProcessorFunc` it's type of `func(string)string` too, so UNIS is 100% compatible with standard library!\n\nProof of concept:\n\n```go\n// [...]\npathCleaner := unis.ProcessorFunc(path.Clean)\ntoLower := unis.ProcessorFunc(strings.ToLower)\n\nunis.NewChain(pathCleaner, toLower)\n// [...]\n```\n\n### Let's begin\n\nHow many times are you using `strings.Replace` in your project? -- Correct, a lot.\nSpawning `strings.Replace` many times can be a dangerous decision because you may forget a replacement somewhere after a trivial change.\n\nUNIS has a function which can help you structure all of your `strings.Replace` to one spot\nbased on the `replacements`. Replacements is just a map[$oldstring]$newstring.\n\n```go\n// NewReplacer accepts a map of old and new string values.\n// The \"old\" will be replaced with the \"new\" one.\n//\n// Same as for loop with a strings.Replace(\"original\", old, new, -1).\nNewReplacer(replacements map[string]string) ProcessorFunc\n```\n\n\u003e `ProcessorFunc` is just an ease-to-use \"alias\" for a `Processor`. It's a func that accepts the same arguments as `Processor.Process` and \nin the same time implements the `Processor` interface. Exactly like the `net/http.HandlerFunc` function.\n\nExample:\n\n```go\npackage main\n\nimport (\n\t\"github.com/esemplastic/unis\"\n)\n\nconst slash = \"/\"\n\n// SlashFixer removes double (system) slashes and returns the fixed path.\nvar SlashFixer = unis.NewReplacer(map[string]string{\n\t\"\\\\\": slash,\n\t\"//\": slash,\n})\n\nfunc main() {\n\toriginal := \"\\\\home\\\\/users//Downloads\"\n\tresult := SlashFixer(original) // /home/users/Downloads\n\tprint(original)\n\tprint(\" |\u003e \")\n\tprintln(result)\n}\n```\n\n\u003e `SlashFixer` is an `unis.ProcessorFunc`, can be called as `SlashFixer.Process(string)` or simply `SlashFixer(string)`.\n\n### Chain\n\n```go\n// Processors is a list of string Processor.\nProcessors []Processor\n\n// NewChain returns a new chain of processors\n// the result of the first goes to the second and so on.\nNewChain(processors ...Processor) ProcessorFunc\n```\n\nExample:\n```go\npackage main\n\nimport (\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/esemplastic/unis\"\n)\n\nfunc NewPathNormalizer() unis.Processor {\n\tslash := \"/\"\n\treplacer := unis.NewReplacer(map[string]string{\n\t\t`\\`:  slash,\n\t\t`//`: slash,\n\t})\n\n\tsuffixRemover := unis.NewSuffixRemover(slash)\n\tslashPrepender := unis.NewTargetedJoiner(0, slash[0])\n\n\ttoLower := unis.ProcessorFunc(strings.ToLower) // convert standard functions to UNIS and add to the chain.\n\tcleanPath := unis.ProcessorFunc(path.Clean)    // convert standard functions to UNIS and add to the chain.\n\treturn unis.NewChain(\n\t\tcleanPath,\n\t\tslashPrepender,\n\t\treplacer,\n\t\tsuffixRemover,\n\t\ttoLower,\n\t)\n}\n\nvar defaultPathNormalizer = NewPathNormalizer()\n\nfunc NormalizePath(path string) string {\n\tif path == \"\" {\n\t\treturn path\n\t}\n\treturn defaultPathNormalizer.Process(path)\n}\n\nfunc main() {\n\toriginal := \"api\\\\////users/\"\n\tresult := NormalizePath(original) // /api/users\n\tprint(original)\n\tprint(\" |\u003e \")\n\tprintln(result)\n}\n\n```\n\n### Conditional\n\n```go\n// NewConditional runs the 'p' processor, if the string didn't\n// changed then it assumes that that processor has being a failure\n// and it returns a Chain of the 'alternative' processor(s).\nNewConditional(p Processor, alternative ...Processor) ProcessorFunc\n```\n\n\n### Prefix\n\n```go\n// NewPrefixRemover accepts a \"prefix\" and returns a new processor\n// which returns the result without that \"prefix\".\nNewPrefixRemover(prefix string) ProcessorFunc\n\n// NewPrepender accepts a \"prefix\" and returns a new processor\n// which returns the result prepended with that \"prefix\".\nNewPrepender(prefix string) ProcessorFunc\n\n// NewExclusivePrepender accepts a \"prefix\" and returns a new processor\n// which returns the result prepended with that \"prefix\"\n// if the \"original\"'s prefix != prefix.\n// The difference from NewPrepender is that\n// this processor will make sure that\n// the prefix is that \"prefix\" series of characters,\n// i.e:\n// 1. \"//path\" -\u003e NewPrepender(\"/\") |\u003e \"//path\"\n//    It has a prefix already, so it doesn't prepends the \"/\" to the \"//path\",\n//    but it doesn't checks if that is the correct prefix.\n// 1. \"//path\" -\u003e NewExclusivePrepender(\"/\") |\u003e \"/path\"\n//     Checks if that is the correct prefix, if so returns as it's,\n//     otherwise replace the duplications and prepend the correct prefix.\nNewExclusivePrepender(prefix string) ProcessorFunc\n```\n\n\n### Suffix\n\n```go\n// NewSuffixRemover accepts a \"suffix\" and returns a new processor\n// which returns the result without that \"suffix\".\nNewSuffixRemover(suffix string) ProcessorFunc\n\n// NewAppender accepts a \"suffix\" and returns a new processor\n// which returns the result appended with that \"suffix\".\nNewAppender(suffix string) ProcessorFunc \n```\n\n### Range\n\n```go\n// NewRange accepts \"begin\" and \"end\" indexes.\n// Returns a new processor which tries to\n// return the \"original[begin:end]\".\nNewRange(begin, end int) ProcessorFunc\n\n// NewRangeBegin almost same as NewRange but it\n// accepts only a \"begin\" index, that means that\n// it assumes that the \"end\" index is the last of the \"original\" string.\n//\n// Returns the \"original[begin:]\".\nNewRangeBegin(begin int) ProcessorFunc\n\n// NewRangeEnd almost same as NewRange but it\n// accepts only an \"end\" index, that means that\n// it assumes that the \"start\" index is 0 of the \"original\".\n//\n// Returns the \"original[0:end]\".\nNewRangeEnd(end int) ProcessorFunc\n```\n\n### Joiner\n\n```go\n// NewTargetedJoiner accepts an \"expectedIndex\" as int\n// and a \"joinerChar\" as byte and returns a new processor\n// which returns the result concated with that \"joinerChar\"\n// if the \"original\" string[expectedIndex] != joinerChar.\n//\n// i.e:\n// 1. \"path\", NewTargetedJoiner(0, '/') |\u003e \"/path\"\n// 2. \"path/anything\", NewTargetedJoiner(5, '*') |\u003e \"path/*anything\".\nNewTargetedJoiner(expectedIndex int, joinerChar byte) ProcessorFunc\n```\n\n### Bonus: Divider\n\nWe saw that everything are `Processors` at the end.\nUNIS has some other interfaces like `Divider` too, which should\nsplit a string into two different string pieces, and `Joiner` which should joins two pieces into one. \n\n\n```go\n\n// Divider should be implemented by all string dividers.\ntype Divider interface {\n\t// Divide takes a string \"original\" and splits it into two pieces.\n\tDivide(original string) (part1 string, part2 string)\n}\n\n// NewDivider returns a new divider which splits\n// a string into two pieces, based on the \"separator\".\n//\n// On failure returns the original path as its first\n// return value, and empty as it's second.\nNewDivider(separator string) Divider\n\n// NewInvertOnFailureDivider accepts a Divider \"divider\"\n// and returns a new one.\n//\n// It calls the previous \"divider\" if succed then it returns\n// the result as it is, otherwise it inverts the order of the result.\n//\n// Rembmer: the \"divider\" by its nature, returns the original string\n// and empty as second parameter if the divide action has being a failure.\nNewInvertOnFailureDivider(divider Divider) Divider\n\n// Divide is an action which runs a new divider based on the \"separator\"\n// and the \"original\" string.\nDivide(original string, separator string) (string, string)\n```\n\n\n### Validator\n\n```go\n// Validator is just another interface\n// for string utilities.\n// All validators should implement this interface.\n// Contains only one function \"Valid\" which accepts\n// a string and returns a boolean and an error.\n// It should compare that string \"str\" with\n// something and returns a true, nil or false, err.\n//\n// Validators can be used side by side with Processors.\n//\n// See .If for more.\ntype Validator interface {\n\tValid(str string) (ok bool, err error)\n}\n\n// ValidatorFunc is just an \"alias\" for the Validator interface.\n// It implements the Validator.\ntype ValidatorFunc func(str string) (bool, error)\n```\n\n`Validators` can be used side by side with `Processors`.\n\n```go\n// If receives a \"validator\" Validator and two Processors,\n// the first processor will be called when that validator passed,\n// the second processor will be called when the validator failed.\n// Both of the processors (\"succeed\" and \"failure\"), as always,\n// can be results of .NewChain.\n//\n// Returns a new string processor which checks the \"validator\"\n// against the \"original\" string, if passed then it runs the\n// \"succeed\", otherwise it runs the \"failure\".\n//\n// Remember: it returns a ProcessorFunc, meaning that can be used in a new chain too.\nIf(validator Validator, succeed Processor, failure Processor) ProcessorFunc\n```\n\nExample: \n\n```go\n// [...]\nmailTo := unis.If(unis.IsMail, unis.NewPrepender(\"mailto:\"), unis.ClearProcessor)\n// it checks if a string is an e-mail,if so then it runs the prepender\n// otherwise it runs the unis.ClearProcessor which returns an empty string.\n\nmailTo(\"ismail@homail.com\") // returns \"mailto:ismail@hotmail.com\"\nmailTo(\"invalidmail@google.com.2\") // returns \"\"\n\n// [...]\n```\n\n\u003e `IsMail` is a `ValidatorFunc`, see [expression_validator_test.go](https://github.com/esemplastic/unis/blob/master/expression_validator_test.go) for more.\n\n\n`IsMail` is a built'n `Validator` based on a the `expression matcher` which accepts a regex expression and validates the receiver string. \n \n```go\n// NewMatcher returns a new validator which\n// returns true and a nil error if the \"expression\"\n// matches against a receiver string.\nNewMatcher(expression string) ValidatorFunc\n```\n\nLet's create a custom matcher which matches if a string is a positive number.\n\n```go\npackage main\n\nimport (\n\t\"github.com/esemplastic/unis\"\n)\n\nfunc main() {\n\tisPositiveNumber := unis.NewMatcher(\"^([0-9]*[1-9][0-9]*(\\.[0-9]+)?|[0]+\\.[0-9]*[1-9][0-9]*)$\")\n\tisPositiveNumber(\"-0\") // or isPositiveNumber.Valid(\"-0\"), returns false, nil \n\tisPositiveNumber(\"0\") // returns false, nil\n\tisPositiveNumber(\"0.1\") // returns true, nil\n\tisPositiveNumber(\"-1\") // returns false, nil\n\tisPositiveNumber(\"1\") // returns true, nil\n\tisPositiveNumber(\"astring\") // returns false, nil\n}\n```\n\nThe `error` output argument of the `NewMatcher` is filled when the expression is invalid.\n\n```go\nm := NewMatcher(\"\\xf8\\xa1\\xa1\\xa1\\xa1\")\nok, err := m.Valid(\"something\") // ok is false, err.Error() is \"error parsing regexp: invalid UTF-8: ....\"\n```\n\nUNIS never panics on its own functions, but we need a way to notify the user,\nin case he doesn't catch the `error` second output argument,\nthat\nsomething critical happened before the validator returns (the `regexp.Compile` is happening before returning the validator).\n\n\nSo we have a global `Logger` variable which accepts a receiver string message and logs to the console by-default.\nThis behavior can be changed by setting the `Logger` to an empty func.\n\n```go\nunis.Logger = func(string){} // disables the logging of the \"panic-level\" messages.\n```\n\n\n## Support\n\nHelp me share realistic design patterns by [starring](https://github.com/esemplastic/unis/stargazers) the project!\nIf you would like to add your implementation of a `UNIS Processor`, feel free to push a [PR](https://github.com/esemplastic/unis/pulls)!\n\n\n## Philosophy\n\nThe UNIS philosophy is to provide small, robust tooling for common string actions, making it a great solution for an extensible project.\n\nI would love to see UNIS as a common place of all Go's `extensible string utilities` that any Gopher will find and use with ease.\nThe goal is to make this repository authored by a `Community` which cares about code extensibility, stability and buety! \n\n## Versioning\n\nCurrent: **0.0.3**  \nDate: 8 May 2017\n\nRead more about Semantic Versioning 2.0.0\n\n - http://semver.org/\n - https://en.wikipedia.org/wiki/Software_versioning\n - https://wiki.debian.org/UpstreamGuide#Releases_and_Versions\n\n## Contributing\n\nI'd love to see contributions! \n\nIf you are interested in contributing to the UNIS project, please read the [SPECS.md](SPECS.md) and make a [PR](https://github.com/esemplastic/unis/pulls).\n\n## People\n\nA list of all contributors can be found [here](CONTRIBUTORS.md).\n\n## TODO\n\n- [x] Tests\n- [x] Documentation\n- [ ] Advanced Examples and Usage on a real project.\n\n## License\n\nUnless otherwise noted, the source files are distributed\nunder the 3-Clause BSD License found in the [LICENSE](LICENSE) file.\n","funding_links":[],"categories":["Utilities","实用工具","公用事业公司","工具库`可以提升效率的通用代码库和工具`","工具库","實用工具","Utility"],"sub_categories":["Utility/Miscellaneous","高级控制台界面","Advanced Console UIs","HTTP Clients","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","Fail injection","实用程序/Miscellaneous","查询语","高級控制台界面","交流"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesemplastic%2Funis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fesemplastic%2Funis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesemplastic%2Funis/lists"}