{"id":26685465,"url":"https://github.com/gyozatech/sushi","last_synced_at":"2025-03-26T10:16:19.627Z","repository":{"id":153796434,"uuid":"630006195","full_name":"gyozatech/sushi","owner":"gyozatech","description":"Series of utilities ","archived":false,"fork":false,"pushed_at":"2023-10-23T11:50:17.000Z","size":113,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2023-10-23T12:39:24.568Z","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/gyozatech.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,"governance":null}},"created_at":"2023-04-19T13:23:47.000Z","updated_at":"2023-04-26T15:40:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"254b00d1-e878-4a72-8525-0725c4794700","html_url":"https://github.com/gyozatech/sushi","commit_stats":null,"previous_names":[],"tags_count":4,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyozatech%2Fsushi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyozatech%2Fsushi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyozatech%2Fsushi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gyozatech%2Fsushi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gyozatech","download_url":"https://codeload.github.com/gyozatech/sushi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245632399,"owners_count":20647194,"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":"2025-03-26T10:16:18.943Z","updated_at":"2025-03-26T10:16:19.613Z","avatar_url":"https://github.com/gyozatech.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sushi: a series of Go utilities\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) \n[![made-with-Go](https://img.shields.io/badge/Made%20with-Go-1f425f.svg)](http://golang.org)\n[![Open Source Love \nsvg1](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/)\n\n![alt text](assets/sushi.png?raw=true)\n\n## Function\n\n`Function` represents any generic transformation applied on a generic type `T` into another (or the same) type `V`.\nThe transformation can generate an error, which is returned in output, as its declaration shows:\n\n```go\ntype Function[T any, V any] func(t T) (*V, error)\n```\n\nTo implement a `Function` type you can simply so something like this:\n\n```go\nfunc myfunc(input int) (*string, error) {\n    if input \u003c 10 {\n        return nil, fmt.Errorf(\"input is less than 10!\")\n    } \n    output := fmt.Sprintf(\" -- %d -- \", input-5)\n    return \u0026output, nil\n}\n```\n\n## Either\n\n`Either` represents the result of an operation which can either be a successful result or an error as shown by its declaration:\n\n```go\ntype Either[V any] struct {\n        result *V\n        err    error\n}\n```\n\nYou can force the generation of an `Either` leaving aside any computation using these two utility methods:\n\n```go\nsuccessEither := functional.EitherFromResult[int](10)\nfailureEither := functional.EitherFromError[string](fmt.Error(\"error!\"))\n```\n\nAfter a computation, you can check if an `Either` wraps a success result or an error through the following functions:\n\n```go\nsuccessEither.IsResult() // -\u003e true\nsuccessEither.IsError()  // -\u003e false\n\nfailureEither.IsResult() // -\u003e false\nfailureEither.IsError()  // -\u003e true\n```\n\nAnd then, you can get the result or the error accordingly:\n\n```go\nresult := successEither.getResult()\nfmt.Println(*result)\n\nerr := failureEither.getError()\nfmt.Println(err)\n```\n\nIf you use the function `GetResult()` in presence of an error, the goroutine panics, so better check before attempting to get a result.\nIf you want to get both the values from an `Either` you can:\n\n```go\nresult, err := failureEither.Get()\n```\n\nIf you want to establish a fallback result in case of error you can use the following:\n\n```go\nresult := failureEither.GetOrElse(\"fallbackValue\") // it will return a pointer to \"fallbackValue\"\n```\n\n## Future\n\nIt is the implementation of an async task running on a different goroutine. \nIt takes advantage of both `Function` and `Either` types.\n`Future` implementation is under `goutils/functional` package. To import it:\n\n```go\nimport (\n    \"github.com/gyozatech/sushi/functional\"\n)\n```\n\nTo use a `Future` you need to define a `Function` which represents the task to be implemented asynchronously and the input to that task:\n\n```go\n\nfunc task(input int) (*string, error) {\n    if input \u003c 10 {\n        return nil, fmt.Errorf(\"input is less than 10!\")\n    } \n    output := fmt.Sprintf(\" -- %d -- \", input-5)\n    return \u0026output, nil\n}\n\nfunc main() {\n    // create and process a task asynchronously\n    future := functional.ProcessAsync(task, 10)\n    // block the current goroutine until the result is ready\n    either := future.WaitForResult()\n    // get the result\n    result := either.GetResult()\n    fmt.Printf(\"%T : '%v' \\n\", result, *result)\n}\n```\n\nYou can execute heavy tasks in parallel by simply doing:\n\n```go\nfuture1 := functional.ProcessAsync(task, 10)\nfuture2 := functional.ProcessAsync(task, 100)\nfuture3 := functional.ProcessAsync(task, 65)\n\nres1, err1 := future1.WaitForResult().Get()\nres2, err2 := future2.WaitForResult().Get()\nres3, err3 := future3.WaitForResult().Get()\n```\n\nThe function `WaitForResult()` is a blocking operation, so that the time to wait in the main goroutine is the max time among the executions of the three tasks.\n\nYou can also choose not to start the tasks directly this way:\n\n```go\n// prepare the future without starting its execution\ninput := 10\nfuture := functional.NewFuture(task, input)\n\n// execute asynchronously\nfuture.Process()\n\n// wait and get the result\nres, err := future.WaitForResult().Get()\n```\n----\n## Utils\n\n### `EnrichContextWithValue` and `FetchContextValue`\n\nThese are useful to enrich the conxtext and fetch values from it:\n\n```go\nimport \n(\n    \"github.com/gyozatech/sushi/utils\"\n    \"context\"\n)\nfunc LetsTry() {\n    ctx := context.TODO()\n    ctx = utils.EnrichContextWithValue(ctx, \"name\", \"Alessandro\")\n    ctx = utils.EnrichContextWithValue(ctx, \"age\", 35)\n    ctx = utils.EnrichContextWithValue(ctx, \"subscribed\", true)\n    ctx = utils.EnrichContextWithValue(ctx, \"book\", Book{Title: \"Jurassic Park\", Author: \"Michael Chricton\"})\n\n    var name *string = utils.FetchContextValue[string](ctx, \"name\")\n    var age *int = utils.FetchContextValue[int](ctx, \"age\")\n    var subscribed *bool = utils.FetchContextValue[bool](ctx, \"subscribed\")\n    var book *Book = utils.FetchContextValue[Book](ctx, \"book\")\n    var unexisting *string= utils.FetchContextValue[string](ctx, \"unexisting\")\n}\n\n```\n\n### `IsEqual`\nIs equal is a strong utility to compare equalness, much more elastic than deepEqual:\n\n```go\nimport (\n    \"github.com/gyozatech/sushi/utils\"\n    \"fmt\"\n)\n\nfunc LetsTry() error {\n    isEqual := utils.IsEqual([]*string{ \"1\", \"2\", \"3\"}, []uint16{ 1, 2, 3 })\n    if !isEqual {\n        return fmt.Errorf(\"They should be equal!\")\n    }\n}\n```\n\n### `CollectResults`\nAllows you to collect all results of a function in a slice:\n\n```go\nimport (\n    \"github.com/gyozatech/sushi/utils\"\n    \"fmt\"\n)\n\nfunc GetUser(id string) (email string, subscribed bool, age int, err error) {\n    // ...\n    return \"alessandro@email.com\", true, 35, nil\n}\n\nfunc LetsTry() error {\n    age := CollectResults(GetUser(\"12345\"))[2]\n    fmt.Println(\"Age:\", age)\n}\n```\n\n### `ForEach`\nAllows you to apply a mutator over a slice\n```go\nimport (\n    \"github.com/gyozatech/sushi/functional\"\n    \"fmt\"\n)\n\nfunc LetsTry() {\n    // output type\n    type DB struct {\n\t    Type string\n\t    Version string\n\t}\n\t\n    // input slice\n\tdbCodes := []string{\"mysql-v8.5.6\", \"postgres-v5.3.1\", \"postgres-v4.2.4\", \"mysql-v11.2.3\"}\n\n    // mutator function from string to DB\n    var fetchDBFromCode functional.Function[string, DB] = func(dbCode string) (*DB, error) {\n\t     split := strings.Split(dbCode, \"-\")\n\t     if len(split) != 2 {\n\t         return nil, fmt.Errorf(\"invalid DB version provided %s\", dbCode)\n\t     }\n\t     return \u0026DB{ Type: split[0], Version: split[1] }, nil\n\t}\n\t// apply mutator to every element of the initial slice with ForEach\n\tdatabases, err := functional.ForEach(dbCodes, fetchDBFromCode)\n\tif err != nil {\n\t    fmt.Printf(\"Error: %s \\n\", err.Error())\n\t} else {\n\t    fmt.Printf(\"%+v \\n\", databases)\n\t}\n}\n\n```\n\n### `FindOne` and `FindMany`\n\nThese two functions allow filtering a slice through a condition:\n```go\nimport (\n    \"github.com/gyozatech/sushi/utils\"\n)\n\nfunc LetsTry() {\n\n    type Book struct {\n        ID int\n        Title string\n        Author string\n    }\n\n    books := []Book{\n        {ID: 1, Title: \"Jurassic Park\", Author: \"Michael Chricton\"},\n        {ID: 2, Title: \"The Lost World\", Author: \"Michael Chricton\"},\n        {ID: 3, Title: \"Relic\", Author: \"Preston \u0026 Child\"},\n    }\n\n    jkBook := utils.FindOne(books, func(b Book) bool{\n        return b.Title == \"Jurassic Park\"\n    })\n\n    chrictonBooks := utils.FindMany(books, func(b Book) bool{\n        return b.Author == \"Michael Chricton\"\n    })\n\n    _ = jkBook\n    _ = chrictonBooks\n\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgyozatech%2Fsushi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgyozatech%2Fsushi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgyozatech%2Fsushi/lists"}