u

package module
v1.27.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2021 License: Apache-2.0, MIT Imports: 20 Imported by: 28

README

u

😄 Go common utility functions

go.dev reference License GitHub release Made by Manfred Touron

Go Release PR GolangCI codecov Go Report Card CodeFactor

Inspired by https://github.com/kjk/u

Usage

FUNCTIONS

func B64Decode(input string) ([]byte, error)
    B64Decode try to decode an input string and returns bytes if success.

func B64Encode(input []byte) string
    B64Encode returns a base64 encoded string of input bytes.

func BoolPtr(val bool) *bool
    BoolPtr returns a pointer to a bool of value 'val'.

func CaptureStderr() (func() string, error)
    CaptureStderr temporarily pipes os.Stderr into a buffer.

func CaptureStdout() (func() string, error)
    CaptureStdout temporarily pipes os.Stdout into a buffer.

func CaptureStdoutAndStderr() (func() string, error)
    CaptureStdoutAndStderr temporarily pipes os.Stdout and os.Stderr into a
    buffer.

func CheckErr(err error)
    CheckErr panics if the passed error is not nil.

func CombineFuncs(left func(), right ...func()) func()
    CombineFuncs create a chain of functions. This can be particularly useful
    for creating cleanup function progressively. It solves the infinite loop you
    can have when trying to do it manually:
    https://play.golang.org/p/NQem8UJ500t.

func CommandExists(command string) bool
    CommandExists checks whether a command is available in the $PATH.

func CreateEmptyFileWithSize(path string, size uint) error
    CreateEmptyFileWithSize creates a new file of the desired size, filled with
    zeros.

func CurrentUsername(fallback string) string
    CurrentUsename returns the current user's username. If username cannot be
    retrieved, it returns the passed fallback.

func DirExists(path string) bool
    DirExists checks whether a path exists and is a directory.

func ExecStandaloneOutputs(cmd *exec.Cmd) ([]byte, []byte, error)
    ExecStandaloneOutputs runs the command and returns its standard output and
    standard error.

func ExpandPath(path string) (string, error)
    ExpandPath performs various expansions on a given path.

    - Replaces ~/ with $HOME/. - Returns absolute path. - Expands env vars.
    TODO: - Follow symlinks.

func FanIn(chans ...<-chan interface{}) <-chan interface{}
    FanIn merges multiple input chans events into one.

func FileExists(path string) bool
    FileExists checks whether a path exists and is a regular file.

func Future(fn func() (interface{}, error)) <-chan FutureRet
    Future starts running the given function in background and return a chan
    that will return the result of the execution.

func IsASCII(buf []byte) bool
    IsASCII checks whether a buffer only contains ASCII characters.

func IsBinary(buf []byte) bool
    IsBinary returns whether the provided buffer looks like binary or
    human-readable.

    It is inspired by the implementation made in the Git project.
    https://github.com/git/git/blob/49f38e2de47a401fc2b0f4cce38e9f07fb63df48/xdiff-interface.c#L188.

func JSON(input interface{}) string
    JSON returns a JSON representation of the passed input.

func MustCaptureStderr() func() string
    MustCaptureStderr wraps CaptureStderr and panics if initialization fails.

func MustCaptureStdout() func() string
    MustCaptureStdout wraps CaptureStdout and panics if initialization fails.

func MustCaptureStdoutAndStderr() func() string
    MustCaptureStdoutAndStderr wraps CaptureStdoutAndStderr and panics if
    initialization fails.

func MustExpandPath(path string) string
    MustExpandPath wraps ExpandPath and panics if initialization fails.

func MustTempFileName(dir, pattern string) string
    MustTempFileName wraps TempFileName and panics if initialization fails.

func MustTempfileWithContent(content []byte) (*os.File, func())
    MustTempfileWithContent wraps TempfileWithContent and panics if
    initialization fails.

func PathExists(path string) bool
    PathExists checks whether a path exists or not.

func PrettyJSON(input interface{}) string
    PrettyJSON returns an indented JSON representation of the passed input.

func RandomLetters(n int) string
    RandomLetters returns a string containing 'n' random letters.

func SafeExec(cmd *exec.Cmd) string
    SafeExec runs a command and return a string containing the combined standard
    output and standard error. If the program fails, the result of `err` is
    appended to the output.

func Sha1(data []byte) []byte
func Sha1Hex(data []byte) string
func ShortDuration(d time.Duration) string
    ShortDuration returns a short human-friendly representation of a duration.
    For duration < 100 days, the output length will be <= 7.

func SilentClose(closer io.Closer)
    SilentClose calls an io.Closer.Close() function and ignore potential errors.

    You can use it as `defer SilenceClose(f)`

func TempFileName(dir, pattern string) (string, error)
    TempFileName returns a valid temporary file name (the file is not created).

func TempfileWithContent(content []byte) (*os.File, func(), error)
    TempfileWithContent creates a tempfile with specified content written in it,
    it also seeks the file pointer so you can read it directly. The second
    returned parameter is a cleanup function that closes and removes the temp
    file.

func UniqueInterfaces(input []interface{}) []interface{}
    UniqueInterfaces removes duplicate values from an interface slice.

func UniqueInts(input []int) []int
    UniqueInts removes duplicate values from an int slice.

func UniqueStrings(input []string) []string
    UniqueStrings removes duplicate values from a string slice.

func Unzip(src string, dest string) ([]string, error)
    Unzip decompresses a zip archive, moving all files and folders within the
    zip file to an output directory. Based on
    https://golangcode.com/unzip-files-in-go/ (MIT).

func UnzipBytes(src []byte, dest string) ([]string, error)
    UnzipBytes is similar to Unzip but takes a zip archive as bytes instead of
    looking for a real file.

func WaitForCtrlC()

TYPES

type FutureRet struct {
	Ret interface{}
	Err error
}
    FutureRet is a generic struct returned by Future.

type MutexMap struct {
	// Has unexported fields.
}
    MutexMap manages a pool of mutexes that can be get by key. MutexMap is
    thread-safe.

func (mm *MutexMap) Lock(key string) func()
    Lock locks a mutex by key, and returns a callback for unlocking unlock. Lock
    will automatically create a new mutex for new keys.

func (mm *MutexMap) RLock(key string) func()
    RLock locks a mutex by key for reading, and returns a callback for unlocking
    unlock. RLock will automatically create a new mutex for new keys.

type UniqueChild interface {
	SetChild(childFn func(context.Context))
	CloseChild()
}
    UniqueChild is a goroutine manager (parent) that can only have one child at
    a time. When you call UniqueChild.SetChild(), UniqueChild cancels the
    previous child context (if any), then run a new child. The child needs to
    auto-kill itself when its context is done.

func NewUniqueChild(ctx context.Context) UniqueChild
    NewUniqueChild instantiates and returns a UniqueChild manager.

See go.dev reference

Install

Using go
$ go get moul.io/u

Benchmarks

benchmark                                           iter           time/iter
---------                                           ----           ---------
BenchmarkUnzip-8                                    4101     251654.00 ns/op
BenchmarkUnzipBytes-8                               4842     213715.00 ns/op
BenchmarkB64Encode/1-8                          30219784         38.44 ns/op
BenchmarkB64Encode/1-parallel-8                120309013         10.42 ns/op
BenchmarkB64Encode/1000-8                         962917       1256.00 ns/op
BenchmarkB64Encode/1000-parallel-8               1627962        815.00 ns/op
BenchmarkB64Encode/1000000-8                        1094    1092692.00 ns/op
BenchmarkB64Encode/1000000-parallel-8               3328     364672.00 ns/op
BenchmarkB64Decode/1000-8                        1000000       1091.00 ns/op
BenchmarkB64Decode/1000-parallel-8               1971834        598.80 ns/op
BenchmarkB64Decode/10000-8                        131664       8624.00 ns/op
BenchmarkB64Decode/10000-parallel-8               274162       4768.00 ns/op
BenchmarkB64Decode/100000-8                        15345      77537.00 ns/op
BenchmarkB64Decode/100000-parallel-8               32480      34873.00 ns/op
BenchmarkIsBinary/small-valid-8                173952991          6.74 ns/op
BenchmarkIsBinary/small-valid-parallel-8       836416648          1.45 ns/op
BenchmarkIsBinary/long-valid-8                   1916740        625.70 ns/op
BenchmarkIsBinary/long-valid-parallel-8          8315928        149.70 ns/op
BenchmarkIsBinary/small-invalid-8              170598688          7.08 ns/op
BenchmarkIsBinary/small-invalid-parallel-8     783116866          1.72 ns/op
BenchmarkCommandExists/go-8                       145177       8699.00 ns/op
BenchmarkCommandExists/go-parallel-8              278449       4384.00 ns/op
BenchmarkCommandExists/asddsa-8                    38422      32856.00 ns/op
BenchmarkCommandExists/asddsa-parallel-8           69381      18171.00 ns/op
BenchmarkSafeExec-8                                   92   11439103.00 ns/op
BenchmarkIsASCII-8                             227565444          5.34 ns/op
BenchmarkCombineFuncs-8                         23210830         52.56 ns/op
BenchmarkFuture-8                                4820587        263.70 ns/op
BenchmarkRandomLetters/1000-8                     783525       1572.00 ns/op
BenchmarkRandomLetters/1000-parallel-8             91543      12743.00 ns/op
BenchmarkRandomLetters/10000-8                     81429      14677.00 ns/op
BenchmarkRandomLetters/10000-parallel-8             9618     127306.00 ns/op
BenchmarkRandomLetters/100000-8                     8164     148755.00 ns/op
BenchmarkRandomLetters/100000-parallel-8             914    1299007.00 ns/op
BenchmarkUniqueStrings/slice1-8                  2245912        553.70 ns/op
BenchmarkUniqueStrings/slice1-parallel-8         5644569        357.00 ns/op
BenchmarkUniqueStrings/slice2-8                     9151     114607.00 ns/op
BenchmarkUniqueStrings/slice2-parallel-8           20236      55601.00 ns/op
BenchmarkUniqueInts/slice1-8                     3188648        371.30 ns/op
BenchmarkUniqueInts/slice1-parallel-8            9704232        117.50 ns/op
BenchmarkUniqueInts/slice2-8                       16548      66907.00 ns/op
BenchmarkUniqueInts/slice2-parallel-8              57608      21750.00 ns/op
BenchmarkUniqueInterfaces/slice1-8               1480366        962.20 ns/op
BenchmarkUniqueInterfaces/slice1-parallel-8      4402994        261.30 ns/op
BenchmarkUniqueInterfaces/slice2-8                  4614     254938.00 ns/op
BenchmarkUniqueInterfaces/slice2-parallel-8        10000     125025.00 ns/op
BenchmarkShortDuration/Simple-8                 23745075         47.64 ns/op
BenchmarkShortDuration/Simple-parallel-8       100000000         11.24 ns/op
BenchmarkShortDuration/Complex-8                 4912780        231.40 ns/op
BenchmarkShortDuration/Complex-parallel-8       13411066         75.40 ns/op
BenchmarkBoolPtr/serial-8                     1000000000          0.32 ns/op
BenchmarkBoolPtr/parallel-8                   1000000000          0.28 ns/op
benchmark                                           iter          time/iter
---------                                           ----          ---------
BenchmarkUnzip-12                                   4551    264133.00 ns/op
BenchmarkUnzipBytes-12                              4507    243344.00 ns/op
BenchmarkB64Encode/1-12                         16033570        80.95 ns/op
BenchmarkB64Encode/1-parallel-12                59893237        17.48 ns/op
BenchmarkB64Encode/1000-12                        379872      4646.00 ns/op
BenchmarkB64Encode/1000-parallel-12              1243312       945.10 ns/op
BenchmarkB64Encode/1000000-12                        315   4063088.00 ns/op
BenchmarkB64Encode/1000000-parallel-12              2054    597679.00 ns/op
BenchmarkB64Decode/1000-12                        424038      3637.00 ns/op
BenchmarkB64Decode/1000-parallel-12              1633958       732.70 ns/op
BenchmarkB64Decode/10000-12                        33224     35140.00 ns/op
BenchmarkB64Decode/10000-parallel-12              200544      6350.00 ns/op
BenchmarkB64Decode/100000-12                        4921    326188.00 ns/op
BenchmarkB64Decode/100000-parallel-12              24049     49786.00 ns/op
BenchmarkIsBinary/small-valid-12               123109468         9.29 ns/op
BenchmarkIsBinary/small-valid-parallel-12      747959686         1.54 ns/op
BenchmarkIsBinary/long-valid-12                  5283378       240.30 ns/op
BenchmarkIsBinary/long-valid-parallel-12        26021748        41.87 ns/op
BenchmarkIsBinary/small-invalid-12             130593402         9.19 ns/op
BenchmarkIsBinary/small-invalid-parallel-12    662583970         1.57 ns/op
BenchmarkCommandExists/go-12                      132699      9937.00 ns/op
BenchmarkCommandExists/go-parallel-12             593409      1715.00 ns/op
BenchmarkCommandExists/asddsa-12                   24820     52140.00 ns/op
BenchmarkCommandExists/asddsa-parallel-12         130977      8541.00 ns/op
BenchmarkSafeExec-12                                 644   2378627.00 ns/op
BenchmarkCombineFuncs-12                         6741579       204.20 ns/op
BenchmarkFuture-12                               1504198       780.20 ns/op
BenchmarkRandomLetters/1000-12                    286212      4019.00 ns/op
BenchmarkRandomLetters/1000-parallel-12            40786     29071.00 ns/op
BenchmarkRandomLetters/10000-12                    29296     40241.00 ns/op
BenchmarkRandomLetters/10000-parallel-12            4363    286588.00 ns/op
BenchmarkRandomLetters/100000-12                    3165    400320.00 ns/op
BenchmarkRandomLetters/100000-parallel-12            421   2877871.00 ns/op
BenchmarkUniqueStrings/slice1-12                  963402      1430.00 ns/op
BenchmarkUniqueStrings/slice1-parallel-12        4290940       307.50 ns/op
BenchmarkUniqueStrings/slice2-12                    6712    246924.00 ns/op
BenchmarkUniqueStrings/slice2-parallel-12          21454     56186.00 ns/op
BenchmarkUniqueInts/slice1-12                    1453717       834.20 ns/op
BenchmarkUniqueInts/slice1-parallel-12           7538754       144.30 ns/op
BenchmarkUniqueInts/slice2-12                      10000    146356.00 ns/op
BenchmarkUniqueInts/slice2-parallel-12             61899     20556.00 ns/op
BenchmarkUniqueInterfaces/slice1-12               911822      1778.00 ns/op
BenchmarkUniqueInterfaces/slice1-parallel-12     3223353       360.10 ns/op
BenchmarkUniqueInterfaces/slice2-12                 2418    564324.00 ns/op
BenchmarkUniqueInterfaces/slice2-parallel-12        7670    140357.00 ns/op
BenchmarkShortDuration/Simple-12                13385139        98.24 ns/op
BenchmarkShortDuration/Simple-parallel-12       59156300        18.38 ns/op
BenchmarkShortDuration/Complex-12                2444396       486.20 ns/op
BenchmarkShortDuration/Complex-parallel-12       9201567       132.90 ns/op

Contribute

I really welcome contributions. Your input is the most precious material. I'm well aware of that and I thank you in advance. Everyone is encouraged to look at what they can do on their own scale; no effort is too small.

Everything on contribution is sum up here: CONTRIBUTING.md

Contributors ✨

All Contributors

Thanks goes to these wonderful people (emoji key):


Manfred Touron

🚧 📖 ⚠️ 💻

moul-bot

🚧

Darko Djalevski

💻

This project follows the all-contributors specification. Contributions of any kind welcome!

License

© 2020-2021 Manfred Touron

Licensed under the Apache License, Version 2.0 (LICENSE-APACHE) or the MIT license (LICENSE-MIT), at your option. See the COPYRIGHT file for more details.

SPDX-License-Identifier: (Apache-2.0 OR MIT)

Documentation

Overview

message from the author:

+--------------------------------------------------------------+
| * * * ░░░░░░░░░░░░░░░░░░░░  Hello  ░░░░░░░░░░░░░░░░░░░░░░░░░░|
+--------------------------------------------------------------+
|                                                              |
|     ++              ______________________________________   |
|     ++++           /                                      \  |
|      ++++          |                                      |  |
|    ++++++++++      |   Feel free to contribute to this    |  |
|   +++       |      |       project or contact me on       |  |
|   ++         |     |    manfred.life if you like this     |  |
|   +  -==   ==|     |               project!               |  |
|  (   <*>   <*>     |                                      |  |
|   |          |    /|                  :)                  |  |
|   |         _)   / |                                      |  |
|   |      +++    /  \______________________________________/  |
|    \      =+   /                                             |
|     \      +                                                 |
|     |\++++++                                                 |
|     |  ++++      ||//                                        |
|  ___|   |___    _||/__                                     __|
| /    ---    \   \|  |||                   __ _  ___  __ __/ /|
|/  |       |  \    \ /                    /  ' \/ _ \/ // / / |
||  |       |  |    | |                   /_/_/_/\___/\_,_/_/  |
+--------------------------------------------------------------+

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func B64Decode added in v1.6.0

func B64Decode(input string) ([]byte, error)

B64Decode try to decode an input string and returns bytes if success.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	ret, _ := u.B64Decode("aGVsbG8gd29ybGQh")
	fmt.Println(string(ret))
}
Output:

hello world!

func B64Encode added in v1.6.0

func B64Encode(input []byte) string

B64Encode returns a base64 encoded string of input bytes.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.B64Encode([]byte("hello world!")))
}
Output:

aGVsbG8gd29ybGQh

func BoolPtr added in v1.24.0

func BoolPtr(val bool) *bool

BoolPtr returns a pointer to a bool of value 'val'.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	truePtr := u.BoolPtr(true)
	falsePtr := u.BoolPtr(false)
	fmt.Println("true ptr:  ", *truePtr)
	fmt.Println("false ptr: ", *falsePtr)
}
Output:

true ptr:   true
false ptr:  false

func CaptureStderr added in v1.9.0

func CaptureStderr() (func() string, error)

CaptureStderr temporarily pipes os.Stderr into a buffer.

Example
package main

import (
	"fmt"
	"os"

	"moul.io/u"
)

func main() {
	os.Stderr = os.Stdout // hack to run this test as an example test

	fmt.Fprintln(os.Stderr, "AAA")
	closer, err := u.CaptureStderr()
	if err != nil {
		panic(err)
	}
	fmt.Fprintln(os.Stderr, "BBB")
	ret := closer()
	fmt.Fprintln(os.Stderr, "CCC")
	fmt.Fprintln(os.Stderr, ret)
}
Output:

AAA
CCC
BBB

func CaptureStdout added in v1.9.0

func CaptureStdout() (func() string, error)

CaptureStdout temporarily pipes os.Stdout into a buffer.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println("AAA")
	closer, err := u.CaptureStdout()
	if err != nil {
		panic(err)
	}
	fmt.Println("BBB")
	ret := closer()
	fmt.Println("CCC")
	fmt.Println(ret)
}
Output:

AAA
CCC
BBB

func CaptureStdoutAndStderr added in v1.9.0

func CaptureStdoutAndStderr() (func() string, error)

CaptureStdoutAndStderr temporarily pipes os.Stdout and os.Stderr into a buffer.

Example
package main

import (
	"fmt"
	"os"

	"moul.io/u"
)

func main() {
	os.Stderr = os.Stdout // hack to run this test as an example test

	fmt.Fprintln(os.Stderr, "AAA")
	fmt.Println("BBB")
	closer, err := u.CaptureStdoutAndStderr()
	if err != nil {
		panic(err)
	}
	fmt.Fprintln(os.Stderr, "CCC")
	fmt.Println("DDD")
	ret := closer()
	fmt.Fprintln(os.Stderr, "EEE")
	fmt.Println("FFF")
	fmt.Println(ret)
}
Output:

AAA
BBB
EEE
FFF
CCC
DDD

func CheckErr added in v1.19.0

func CheckErr(err error)

CheckErr panics if the passed error is not nil.

Example
package main

import (
	"net/http"

	"moul.io/u"
)

func main() {
	_, err := http.Get("http://foo.bar")
	u.CheckErr(err) // panic
}
Output:

func CombineFuncs added in v1.6.0

func CombineFuncs(left func(), right ...func()) func()

CombineFuncs create a chain of functions. This can be particularly useful for creating cleanup function progressively. It solves the infinite loop you can have when trying to do it manually: https://play.golang.org/p/NQem8UJ500t.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	cleanup := func() { fmt.Print("A") }
	cleanup = u.CombineFuncs(cleanup, func() { fmt.Print("B") })
	cleanup = u.CombineFuncs(func() { fmt.Print("C") }, cleanup)
	cleanup()
}
Output:

CAB

func CommandExists added in v1.18.0

func CommandExists(command string) bool

CommandExists checks whether a command is available in the $PATH.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.CommandExists("go"))
	fmt.Println(u.CommandExists("asldkglsakdjaslkdg"))
}
Output:

true
false

func CreateEmptyFileWithSize added in v1.18.0

func CreateEmptyFileWithSize(path string, size uint) error

CreateEmptyFileWithSize creates a new file of the desired size, filled with zeros.

Example
package main

import (
	"fmt"
	"io/ioutil"
	"os"

	"moul.io/u"
)

func main() {
	tempname := u.MustTempFileName("", "u")

	err := u.CreateEmptyFileWithSize(tempname, 42)
	if err != nil {
		panic(err)
	}
	defer os.Remove(tempname)

	fi, err := os.Stat(tempname)
	if err != nil {
		panic(err)
	}
	fmt.Println(fi.Size())

	b, err := ioutil.ReadFile(tempname)
	if err != nil {
		panic(err)
	}
	fmt.Println(b)

}
Output:

42
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

func CurrentUsername added in v1.18.0

func CurrentUsername(fallback string) string

CurrentUsename returns the current user's username. If username cannot be retrieved, it returns the passed fallback.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.CurrentUsername("fallback"))
}
Output:

func DirExists added in v1.18.0

func DirExists(path string) bool

DirExists checks whether a path exists and is a directory.

Example
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"moul.io/u"
)

func main() {
	file, err := ioutil.TempFile("", "bar")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(file.Name())
	fmt.Println(u.DirExists("/laksjdflkasdjflaksdjfalskdfjasdlfkj")) // should not exist
	fmt.Println(u.DirExists(file.Name()))
	fmt.Println(u.DirExists(filepath.Dir(file.Name())))
}
Output:

false
false
true

func ExecStandaloneOutputs added in v1.18.0

func ExecStandaloneOutputs(cmd *exec.Cmd) ([]byte, []byte, error)

ExecStandaloneOutputs runs the command and returns its standard output and standard error.

Example
package main

import (
	"fmt"
	"os/exec"

	"moul.io/u"
)

func main() {
	stdout, stderr, err := u.ExecStandaloneOutputs(exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr"))
	fmt.Print("stdout: ", string(stdout))
	fmt.Print("stderr: ", string(stderr))
	fmt.Println("err: ", err)
}
Output:

stdout: stdout
stderr: stderr
err:  <nil>

func ExpandPath added in v1.25.0

func ExpandPath(path string) (string, error)

ExpandPath performs various expansions on a given path.

- Replaces ~/ with $HOME/. - Returns absolute path. - Expands env vars. TODO: - Follow symlinks.

Example
package main

import (
	"fmt"
	"os"

	"moul.io/u"
)

func main() {
	os.Setenv("HOME", "/home/foo") // just for example
	ret, err := u.ExpandPath("~/hello-world.txt")
	if err != nil {
		panic(err)
	}
	fmt.Println(ret)
}
Output:

/home/foo/hello-world.txt

func FanIn added in v1.20.0

func FanIn(chans ...<-chan interface{}) <-chan interface{}

FanIn merges multiple input chans events into one.

Example
package main

import (
	"fmt"
	"sort"
	"strings"

	"moul.io/u"
)

func main() {
	ch1 := make(chan interface{})
	ch2 := make(chan interface{})
	ch3 := make(chan interface{})
	merged := u.FanIn(ch1, ch2, ch3)
	done := make(chan bool)
	received := []string{}

	go func() {
		for item := range merged {
			fmt.Println("tick")
			received = append(received, fmt.Sprintf("%v", item))
		}
		done <- true
	}()

	ch1 <- 1
	ch2 <- 2
	ch3 <- 3
	close(ch1)
	ch2 <- 4
	ch2 <- 5
	ch3 <- 6
	close(ch2)
	ch3 <- 7
	close(ch3)

	<-done

	sort.Strings(received)
	fmt.Println(strings.Join(received, ", "))

}
Output:

tick
tick
tick
tick
tick
tick
tick
1, 2, 3, 4, 5, 6, 7

func FileExists added in v1.18.0

func FileExists(path string) bool

FileExists checks whether a path exists and is a regular file.

Example
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"moul.io/u"
)

func main() {
	file, err := ioutil.TempFile("", "bar")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(file.Name())
	fmt.Println(u.FileExists("/laksjdflkasdjflaksdjfalskdfjasdlfkj")) // should not exist
	fmt.Println(u.FileExists(file.Name()))
	fmt.Println(u.FileExists(filepath.Dir(file.Name())))
}
Output:

false
true
false

func Future added in v1.20.0

func Future(fn func() (interface{}, error)) <-chan FutureRet

Future starts running the given function in background and return a chan that will return the result of the execution.

Example
package main

import (
	"fmt"
	"time"

	"moul.io/u"
)

func main() {
	future := u.Future(func() (interface{}, error) {
		time.Sleep(100 * time.Millisecond)
		return "foobar", nil
	})

	// here, we can do some stuff

	ret := <-future
	fmt.Println("Ret:", ret.Ret)
	fmt.Println("Err:", ret.Err)

}
Output:

Ret: foobar
Err: <nil>

func IsASCII added in v1.26.0

func IsASCII(buf []byte) bool

IsASCII checks whether a buffer only contains ASCII characters.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.IsASCII([]byte("hello")))
}
Output:

true

func IsBinary added in v1.23.0

func IsBinary(buf []byte) bool

IsBinary returns whether the provided buffer looks like binary or human-readable.

It is inspired by the implementation made in the Git project. https://github.com/git/git/blob/49f38e2de47a401fc2b0f4cce38e9f07fb63df48/xdiff-interface.c#L188.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.IsBinary([]byte{'c', 'h', 'i', 'c', 'k', 'e', 'n'}))
	fmt.Println(u.IsBinary([]byte{'c', 'h', 'i', 0, 'k', 'e', 'n'}))
}
Output:

false
true

func JSON added in v1.6.0

func JSON(input interface{}) string

JSON returns a JSON representation of the passed input.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.JSON([]string{"hello", "world"}))
	fmt.Println(u.JSON(42))
	fmt.Println(u.JSON(nil))
}
Output:

["hello","world"]
42
null

func MustCaptureStderr added in v1.9.0

func MustCaptureStderr() func() string

MustCaptureStderr wraps CaptureStderr and panics if initialization fails.

Example
package main

import (
	"fmt"
	"os"

	"moul.io/u"
)

func main() {
	os.Stderr = os.Stdout // hack to run this test as an example test

	fmt.Fprintln(os.Stderr, "AAA")
	closer := u.MustCaptureStderr()
	fmt.Fprintln(os.Stderr, "BBB")
	ret := closer()
	fmt.Fprintln(os.Stderr, "CCC")
	fmt.Fprintln(os.Stderr, ret)
}
Output:

AAA
CCC
BBB

func MustCaptureStdout added in v1.9.0

func MustCaptureStdout() func() string

MustCaptureStdout wraps CaptureStdout and panics if initialization fails.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println("AAA")
	closer := u.MustCaptureStdout()
	fmt.Println("BBB")
	ret := closer()
	fmt.Println("CCC")
	fmt.Println(ret)
}
Output:

AAA
CCC
BBB

func MustCaptureStdoutAndStderr added in v1.9.0

func MustCaptureStdoutAndStderr() func() string

MustCaptureStdoutAndStderr wraps CaptureStdoutAndStderr and panics if initialization fails.

Example
package main

import (
	"fmt"
	"os"

	"moul.io/u"
)

func main() {
	os.Stderr = os.Stdout // hack to run this test as an example test

	fmt.Fprintln(os.Stderr, "AAA")
	fmt.Println("BBB")
	closer := u.MustCaptureStdoutAndStderr()
	fmt.Fprintln(os.Stderr, "CCC")
	fmt.Println("DDD")
	ret := closer()
	fmt.Fprintln(os.Stderr, "EEE")
	fmt.Println("FFF")
	fmt.Println(ret)
}
Output:

AAA
BBB
EEE
FFF
CCC
DDD

func MustExpandPath added in v1.25.0

func MustExpandPath(path string) string

MustExpandPath wraps ExpandPath and panics if initialization fails.

Example
package main

import (
	"fmt"
	"os"

	"moul.io/u"
)

func main() {
	os.Setenv("HOME", "/home/foo") // just for example
	ret := u.MustExpandPath("~/hello-world.txt")
	fmt.Println(ret)
}
Output:

/home/foo/hello-world.txt

func MustTempFileName added in v1.18.0

func MustTempFileName(dir, pattern string) string

MustTempFileName wraps TempFileName and panics if initialization fails.

Example
package main

import (
	"os"

	"moul.io/u"
)

func main() {
	tempname := u.MustTempFileName("", "u")
	f, _ := os.Create(tempname)
	f.Close()
	os.Remove(tempname)
}
Output:

func MustTempfileWithContent added in v1.9.0

func MustTempfileWithContent(content []byte) (*os.File, func())

MustTempfileWithContent wraps TempfileWithContent and panics if initialization fails.

Example
package main

import (
	"fmt"
	"io/ioutil"

	"moul.io/u"
)

func main() {
	f, cleanup := u.MustTempfileWithContent([]byte("AAA\nBBB\nCCC"))
	defer cleanup()

	out, err := ioutil.ReadAll(f)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(out))

}
Output:

AAA
BBB
CCC

func PathExists added in v1.18.0

func PathExists(path string) bool

PathExists checks whether a path exists or not.

Example
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"moul.io/u"
)

func main() {
	file, err := ioutil.TempFile("", "bar")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(file.Name())
	fmt.Println(u.PathExists("/laksjdflkasdjflaksdjfalskdfjasdlfkj")) // should not exist
	fmt.Println(u.PathExists(file.Name()))
	fmt.Println(u.PathExists(filepath.Dir(file.Name())))
}
Output:

false
true
true

func PrettyJSON added in v1.6.0

func PrettyJSON(input interface{}) string

PrettyJSON returns an indented JSON representation of the passed input.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.PrettyJSON([]string{"hello", "world"}))
	fmt.Println(u.PrettyJSON(42))
	fmt.Println(u.PrettyJSON(nil))
}
Output:

[
  "hello",
  "world"
]
42
null

func RandomLetters added in v1.22.0

func RandomLetters(n int) string

RandomLetters returns a string containing 'n' random letters.

Example
package main

import (
	"fmt"
	"math/rand"

	"moul.io/u"
)

func main() {
	rand.Seed(42)
	fmt.Println(u.RandomLetters(8))
	fmt.Println(u.RandomLetters(8))
	fmt.Println(u.RandomLetters(8))
	fmt.Println(u.RandomLetters(42))
}
Output:

HRukpTTu
eZPtNeuv
unhuksqV
GzAdxlgghEjkMVeZJpmKqakmTRgKfBSWYjUNGkdmdt

func SafeExec added in v1.18.0

func SafeExec(cmd *exec.Cmd) string

SafeExec runs a command and return a string containing the combined standard output and standard error. If the program fails, the result of `err` is appended to the output.

Example
package main

import (
	"fmt"
	"os/exec"

	"moul.io/u"
)

func main() {
	out := u.SafeExec(exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr; exit 1"))
	fmt.Println(out)
}
Output:

stdout
stderr

error: exit status 1

func Sha1

func Sha1(data []byte) []byte
Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.Sha1([]byte("hello world!")))
}
Output:

[67 12 227 77 2 7 36 237 117 161 150 223 194 173 103 199 119 114 209 105]

func Sha1Hex

func Sha1Hex(data []byte) string
Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.Sha1Hex([]byte("hello world!")))
}
Output:

430ce34d020724ed75a196dfc2ad67c77772d169

func ShortDuration added in v1.9.0

func ShortDuration(d time.Duration) string

ShortDuration returns a short human-friendly representation of a duration. For duration < 100 days, the output length will be <= 7.

Example
package main

import (
	"fmt"
	"time"

	"moul.io/u"
)

func main() {
	fmt.Println(u.ShortDuration(time.Nanosecond * 0))
	fmt.Println(u.ShortDuration(time.Nanosecond))
	fmt.Println(u.ShortDuration(time.Nanosecond * 12))
	fmt.Println(u.ShortDuration(time.Nanosecond * 123))
	fmt.Println(u.ShortDuration(time.Nanosecond * 1234))
	fmt.Println(u.ShortDuration(time.Nanosecond * 12345))
	fmt.Println(u.ShortDuration(time.Nanosecond * 123456))
	fmt.Println(u.ShortDuration(time.Nanosecond * 1234567))
	fmt.Println(u.ShortDuration(time.Nanosecond * 12345678))
	fmt.Println(u.ShortDuration(time.Nanosecond * 123456789))
	fmt.Println(u.ShortDuration(time.Nanosecond * 1234567891))
	fmt.Println(u.ShortDuration(time.Nanosecond * 12345678912))
	fmt.Println(u.ShortDuration(time.Nanosecond * 123456789123))
	fmt.Println(u.ShortDuration(time.Nanosecond * 1234567891234))
	fmt.Println(u.ShortDuration(time.Nanosecond * 12345678912345))
	fmt.Println(u.ShortDuration(time.Nanosecond * 123456789123456))
	fmt.Println(u.ShortDuration(time.Nanosecond * 1234567891234567))
	fmt.Println(u.ShortDuration(time.Nanosecond * 12345678912345678))
	fmt.Println(u.ShortDuration(time.Nanosecond * 123456789123456789))
	fmt.Println(u.ShortDuration(time.Nanosecond * 1234567891234567891))
	fmt.Println("-------")
	fmt.Println(u.ShortDuration(time.Nanosecond))
	fmt.Println(u.ShortDuration(time.Microsecond))
	fmt.Println(u.ShortDuration(time.Millisecond))
	fmt.Println(u.ShortDuration(time.Second))
	fmt.Println(u.ShortDuration(time.Minute))
	fmt.Println(u.ShortDuration(time.Hour))
	fmt.Println(u.ShortDuration(time.Hour * 24))
	fmt.Println(u.ShortDuration(time.Hour + time.Second))
}
Output:

0s
1ns
12ns
123ns
1.2µs
12.3µs
123.5µs
1.2ms
12.3ms
123.5ms
1.2s
12.3s
2m3s
20m35s
3h25m46s
1d10h18m
14d6h56m
142d21h21m
1428d21h33m
14288d23h32m
-------
1ns
1µs
1ms
1s
1m
1h
1d
1h0m1s

func SilentClose added in v1.6.0

func SilentClose(closer io.Closer)

SilentClose calls an io.Closer.Close() function and ignore potential errors.

You can use it as `defer SilenceClose(f)`

Example
package main

import (
	"os"

	"moul.io/u"
)

func main() {
	f, _ := os.Open("file.txt")
	defer u.SilentClose(f)
}
Output:

func TempFileName added in v1.18.0

func TempFileName(dir, pattern string) (string, error)

TempFileName returns a valid temporary file name (the file is not created).

Example
package main

import (
	"fmt"
	"os"

	"moul.io/u"
)

func main() {
	tempname, err := u.TempFileName("", "u")
	if err != nil {
		panic(err)
	}
	if u.FileExists(tempname) {
		panic("there is already one file with tempname")
	}
	f, err := os.Create(tempname)
	if err != nil {
		panic(err)
	}
	defer os.Remove(tempname)
	f.Close()
	fmt.Println("Everything is OK!")
}
Output:

Everything is OK!

func TempfileWithContent added in v1.9.0

func TempfileWithContent(content []byte) (*os.File, func(), error)

TempfileWithContent creates a tempfile with specified content written in it, it also seeks the file pointer so you can read it directly. The second returned parameter is a cleanup function that closes and removes the temp file.

Example
package main

import (
	"fmt"
	"io/ioutil"

	"moul.io/u"
)

func main() {
	f, cleanup, err := u.TempfileWithContent([]byte("AAA\nBBB\nCCC"))
	if err != nil {
		panic(err)
	}
	defer cleanup()

	out, err := ioutil.ReadAll(f)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(out))

}
Output:

AAA
BBB
CCC

func UniqueInterfaces added in v1.21.0

func UniqueInterfaces(input []interface{}) []interface{}

UniqueInterfaces removes duplicate values from an interface slice.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.UniqueInterfaces([]interface{}{13, "foo", "bar", 42, 13, 43, "baz"}))
}
Output:

[13 foo bar 42 43 baz]

func UniqueInts added in v1.21.0

func UniqueInts(input []int) []int

UniqueInts removes duplicate values from an int slice.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.UniqueInts([]int{13, 51, 36, 69, 92, 92, 42, 21, 36, 13, 51}))
}
Output:

[13 51 36 69 92 42 21]

func UniqueStrings added in v1.21.0

func UniqueStrings(input []string) []string

UniqueStrings removes duplicate values from a string slice.

Example
package main

import (
	"fmt"

	"moul.io/u"
)

func main() {
	fmt.Println(u.UniqueStrings([]string{"foo", "bar", "foo", "baz", "foo", "bar", "foobar", "baz", "foobaz"}))
}
Output:

[foo bar baz foobar foobaz]

func Unzip added in v1.18.0

func Unzip(src string, dest string) ([]string, error)

Unzip decompresses a zip archive, moving all files and folders within the zip file to an output directory. Based on https://golangcode.com/unzip-files-in-go/ (MIT).

Example
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"moul.io/u"
)

func main() {
	// create zipfile on fs
	f, cleanup, err := u.TempfileWithContent(zipdata_simple)
	if err != nil {
		panic(err)
	}
	defer cleanup()

	// create tempdir for dest
	tempdir, err := ioutil.TempDir("", "u")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(tempdir)

	// unzip to dest
	files, err := u.Unzip(f.Name(), tempdir)
	if err != nil {
		panic(err)
	}
	for _, file := range files {
		relPath := "." + strings.TrimPrefix(file, tempdir)
		fmt.Println(relPath)
	}
}

var zipdata_simple = []byte{
	0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x81, 0x61, 0x25, 0x3d, 0xc0, 0xd7,
	0xed, 0xc3, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x74, 0x65,
	0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54, 0x09, 0x00, 0x03, 0x71, 0xfc, 0x82, 0x4c, 0x76,
	0xfc, 0x82, 0x4c, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xf5, 0x01, 0x00, 0x00, 0x04, 0x14, 0x00,
	0x00, 0x00, 0x0b, 0xc9, 0xc8, 0x2c, 0x56, 0x00, 0xa2, 0x44, 0x85, 0x92, 0xd4, 0xe2, 0x12, 0x20,
	0x51, 0x51, 0xa2, 0x90, 0x96, 0x99, 0x93, 0xaa, 0xc7, 0x05, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x0a,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x7e, 0x25, 0x3d, 0xfe, 0x31, 0xd5, 0x54, 0x11, 0x03, 0x00,
	0x00, 0x11, 0x03, 0x00, 0x00, 0x14, 0x00, 0x1c, 0x00, 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, 0x63,
	0x6f, 0x6c, 0x6f, 0x72, 0x31, 0x36, 0x78, 0x31, 0x36, 0x2e, 0x70, 0x6e, 0x67, 0x55, 0x54, 0x09,
	0x00, 0x03, 0x3a, 0x30, 0x83, 0x4c, 0x3b, 0x30, 0x83, 0x4c, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04,
	0xf5, 0x01, 0x00, 0x00, 0x04, 0x14, 0x00, 0x00, 0x00, 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a,
	0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
	0x0f, 0x08, 0x06, 0x00, 0x00, 0x00, 0xed, 0x73, 0x4f, 0x2f, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41,
	0x4d, 0x41, 0x00, 0x00, 0xd6, 0xd8, 0xd4, 0x4f, 0x58, 0x32, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45,
	0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x41, 0x64, 0x6f, 0x62, 0x65,
	0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x00,
	0x00, 0x02, 0xa3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x74, 0x93, 0x5f, 0x48, 0x53, 0x51, 0x18,
	0xc0, 0xbf, 0x7b, 0xcf, 0xee, 0xc6, 0x75, 0x5b, 0x56, 0xda, 0x2a, 0x25, 0x6d, 0x6c, 0x06, 0x51,
	0x56, 0xf4, 0x5f, 0x23, 0xb7, 0x22, 0x22, 0xb0, 0x7a, 0x88, 0x41, 0x0f, 0x4b, 0x0c, 0x0a, 0xa3,
	0x82, 0x02, 0x1f, 0x62, 0x1a, 0x45, 0x2f, 0xa1, 0x83, 0x7a, 0x49, 0x2a, 0x86, 0x15, 0x35, 0xff,
	0x64, 0x98, 0x90, 0xa2, 0x91, 0x2e, 0x10, 0x9c, 0x2f, 0x53, 0x92, 0xc2, 0x07, 0xe9, 0xb6, 0x86,
	0x28, 0xda, 0x5d, 0xb6, 0xed, 0x5e, 0x75, 0x0e, 0xd9, 0x9f, 0xd3, 0x39, 0x9b, 0x8d, 0xad, 0xf2,
	0x83, 0xef, 0x9e, 0x7b, 0xbf, 0x3f, 0xbf, 0xf3, 0x7d, 0xdf, 0x39, 0x97, 0xc1, 0x18, 0x43, 0xa6,
	0xd8, 0xed, 0x76, 0xb0, 0xd9, 0x6c, 0x97, 0xcf, 0x9d, 0x3a, 0x7a, 0x23, 0xaf, 0xd0, 0x58, 0x14,
	0x94, 0xe4, 0x65, 0xdf, 0xc4, 0x97, 0x65, 0x25, 0x87, 0x14, 0x9c, 0x56, 0x57, 0xe1, 0x76, 0xbb,
	0xbd, 0x99, 0xf1, 0x8a, 0xcc, 0x8f, 0x96, 0x96, 0x56, 0xf4, 0xba, 0xbd, 0xcd, 0xe1, 0x68, 0xa8,
	0xbd, 0x64, 0x34, 0x1a, 0xe0, 0xb8, 0xe5, 0x1a, 0xdc, 0x6f, 0xb0, 0x43, 0x69, 0x91, 0x16, 0xa4,
	0x90, 0x04, 0xf3, 0x6c, 0xfe, 0x26, 0x12, 0x96, 0x05, 0x60, 0x68, 0x05, 0x35, 0x35, 0x35, 0x10,
	0x5e, 0x8a, 0xc0, 0xf0, 0xe0, 0x40, 0x67, 0xf3, 0x0b, 0xa7, 0xc5, 0xeb, 0x15, 0x40, 0xfc, 0x3a,
	0x02, 0xe6, 0xb3, 0x55, 0xa0, 0xe2, 0x10, 0xc4, 0x7f, 0x8e, 0x83, 0x82, 0xc1, 0x70, 0xfb, 0x51,
	0x57, 0xdd, 0x56, 0xc3, 0xb6, 0xc6, 0x58, 0x2c, 0x4a, 0x36, 0x6b, 0x49, 0x11, 0x28, 0x80, 0x65,
	0x99, 0x63, 0x05, 0xba, 0x75, 0x1d, 0x77, 0x6f, 0x56, 0x61, 0x59, 0x0a, 0x61, 0xcf, 0xe8, 0x27,
	0x7c, 0xe4, 0xd0, 0x5e, 0x7c, 0xa2, 0x6c, 0x27, 0x7e, 0xd3, 0x54, 0x8f, 0x27, 0x5c, 0xcf, 0xb0,
	0x6f, 0xc8, 0x89, 0x2f, 0x5a, 0x4e, 0x0a, 0xeb, 0xf3, 0x36, 0xb0, 0x66, 0xb3, 0x39, 0x99, 0x47,
	0x35, 0xd9, 0x82, 0x26, 0x87, 0x2f, 0x2b, 0xd1, 0x6f, 0x31, 0xed, 0x30, 0x16, 0xc1, 0xc8, 0x87,
	0x56, 0x00, 0xa4, 0x84, 0xca, 0x8a, 0xdd, 0x10, 0x8c, 0xaa, 0x41, 0xa3, 0xd5, 0x42, 0x3c, 0x91,
	0x80, 0x70, 0x64, 0x19, 0xce, 0x57, 0x56, 0x94, 0x08, 0x33, 0xf3, 0xd7, 0x37, 0x17, 0x14, 0x34,
	0x65, 0xb5, 0xc0, 0x30, 0x0c, 0x47, 0xde, 0x4b, 0xaf, 0x5e, 0x38, 0xd3, 0x7f, 0xeb, 0x8a, 0x25,
	0x7f, 0x61, 0x71, 0x09, 0x18, 0x16, 0xc1, 0xb4, 0x5f, 0x06, 0xdd, 0xda, 0x1c, 0x50, 0xa9, 0x38,
	0x60, 0x48, 0x80, 0x4a, 0xc9, 0x81, 0xa3, 0x73, 0x10, 0x1c, 0x6d, 0x3d, 0x8d, 0xf3, 0x52, 0xa8,
	0x8e, 0x02, 0xd8, 0x15, 0x50, 0x94, 0xe8, 0xd8, 0xdb, 0xf7, 0x43, 0x1d, 0x41, 0x39, 0x4c, 0xb0,
	0xa4, 0xef, 0x78, 0x02, 0x8a, 0x37, 0xe6, 0x26, 0x93, 0x02, 0x52, 0x38, 0x09, 0xa3, 0x92, 0xa7,
	0x51, 0xc2, 0x82, 0x2c, 0xf5, 0xfd, 0xa9, 0x80, 0xcd, 0x9c, 0xe8, 0x5c, 0x50, 0xbe, 0xf3, 0xc4,
	0xd9, 0x33, 0xc9, 0xab, 0x94, 0x29, 0x2a, 0x81, 0x90, 0xf9, 0x80, 0x77, 0x4a, 0x04, 0x61, 0x46,
	0x82, 0x68, 0x2c, 0x06, 0xba, 0xfc, 0x5c, 0xea, 0x0a, 0xfc, 0x17, 0x40, 0x44, 0x7a, 0xd5, 0xe5,
	0xb2, 0xf9, 0xa6, 0x45, 0x40, 0x28, 0xe5, 0x8a, 0xc6, 0xe2, 0xb0, 0x6f, 0x7b, 0x31, 0x98, 0x76,
	0x15, 0x26, 0xc3, 0x57, 0xe0, 0xea, 0xd5, 0x00, 0x10, 0x8b, 0xc7, 0x5d, 0xee, 0xd1, 0xf1, 0x25,
	0x5a, 0x7a, 0x7a, 0x50, 0x2c, 0x4b, 0x1f, 0x69, 0x20, 0x0d, 0x5b, 0x15, 0x40, 0x44, 0x16, 0xe7,
	0x82, 0x0b, 0x2c, 0xfb, 0xaf, 0x4b, 0xa1, 0x40, 0xf0, 0xc3, 0x1f, 0xa0, 0x04, 0x7f, 0xd6, 0x4d,
	0x34, 0x18, 0x0c, 0x29, 0x1a, 0x49, 0xfa, 0xee, 0x9b, 0x04, 0x44, 0x56, 0xe6, 0xaf, 0x64, 0x6a,
	0x63, 0x01, 0x43, 0x30, 0x82, 0x91, 0x5a, 0xb3, 0xe6, 0x34, 0x31, 0x35, 0xa7, 0x01, 0x1e, 0x8f,
	0x27, 0x19, 0xa4, 0x25, 0x67, 0x6e, 0xab, 0xab, 0x57, 0x7b, 0x06, 0x7b, 0x79, 0xba, 0x1b, 0x47,
	0x14, 0x21, 0xb2, 0x72, 0x0a, 0xa0, 0x47, 0xfb, 0xb8, 0xb5, 0xef, 0xd7, 0xc7, 0x51, 0xe1, 0xe5,
	0xc1, 0x03, 0xfb, 0x67, 0xb3, 0x2a, 0x28, 0x2f, 0x2f, 0x4f, 0x57, 0x20, 0x8a, 0x62, 0x42, 0x92,
	0xa4, 0xda, 0xa7, 0xed, 0xfd, 0x0f, 0x4d, 0x87, 0xf7, 0xe4, 0xce, 0xce, 0x4c, 0xc3, 0xb8, 0x30,
	0x15, 0x78, 0x37, 0x30, 0xdc, 0x8b, 0x11, 0x7f, 0xaf, 0xba, 0xba, 0x7a, 0xd2, 0x6a, 0xb5, 0x66,
	0xff, 0x4c, 0x82, 0x20, 0x64, 0x56, 0xbb, 0x48, 0xf4, 0xb9, 0xcb, 0x33, 0x61, 0x1d, 0xfb, 0xe6,
	0x8f, 0xf0, 0x3c, 0xff, 0xa0, 0xbb, 0xbb, 0xfb, 0x33, 0xb1, 0x85, 0xf4, 0x7a, 0x3d, 0xbd, 0x74,
	0x59, 0xad, 0xfd, 0x16, 0x60, 0x00, 0xa8, 0x08, 0x0d, 0x8b, 0x03, 0xe7, 0x62, 0xea, 0x00, 0x00,
	0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03,
	0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x81, 0x61, 0x25, 0x3d, 0xc0, 0xd7, 0xed, 0xc3, 0x19, 0x00,
	0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
	0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74,
	0x55, 0x54, 0x05, 0x00, 0x03, 0x71, 0xfc, 0x82, 0x4c, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xf5,
	0x01, 0x00, 0x00, 0x04, 0x14, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03, 0x0a, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x9d, 0x7e, 0x25, 0x3d, 0xfe, 0x31, 0xd5, 0x54, 0x11, 0x03, 0x00, 0x00,
	0x11, 0x03, 0x00, 0x00, 0x14, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xa4, 0x81, 0x5b, 0x00, 0x00, 0x00, 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, 0x63, 0x6f, 0x6c, 0x6f,
	0x72, 0x31, 0x36, 0x78, 0x31, 0x36, 0x2e, 0x70, 0x6e, 0x67, 0x55, 0x54, 0x05, 0x00, 0x03, 0x3a,
	0x30, 0x83, 0x4c, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xf5, 0x01, 0x00, 0x00, 0x04, 0x14, 0x00,
	0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0xa8, 0x00,
	0x00, 0x00, 0xba, 0x03, 0x00, 0x00, 0x1a, 0x00, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
	0x61, 0x20, 0x7a, 0x69, 0x70, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e,
	0x74, 0x2e,
}
Output:

./test.txt
./gophercolor16x16.png
Example (Zipslip)
package main

import (
	"fmt"
	"io/ioutil"
	"os"

	"moul.io/u"
)

func main() {
	// create zipfile on fs
	f, cleanup, err := u.TempfileWithContent(zipdata_zipline)
	if err != nil {
		panic(err)
	}
	defer cleanup()

	// create tempdir for dest
	tempdir, err := ioutil.TempDir("", "u")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(tempdir)

	// unzip to dest
	_, err = u.Unzip(f.Name(), tempdir)
	fmt.Println(err)

}

var zipdata_zipline = []byte{
	0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xb0, 0x8f, 0x4c, 0x0f,
	0x6f, 0x4f, 0xf3, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00,
	0x67, 0x6f, 0x6f, 0x64, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54, 0x09, 0x00, 0x03, 0x3d, 0xa2,
	0xd3, 0x5a, 0x3e, 0xa2, 0xd3, 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xf6, 0x01, 0x00,
	0x00, 0x04, 0x14, 0x00, 0x00, 0x00, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61,
	0x20, 0x67, 0x6f, 0x6f, 0x64, 0x20, 0x6f, 0x6e, 0x65, 0x0a, 0x50, 0x4b, 0x03, 0x04, 0x14,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xb0, 0x8f, 0x4c, 0x60, 0x41, 0x7b, 0x39, 0x14, 0x00,
	0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x65,
	0x76, 0x69, 0x6c, 0x2e, 0x74, 0x78, 0x74, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
	0x61, 0x6e, 0x20, 0x65, 0x76, 0x69, 0x6c, 0x20, 0x6f, 0x6e, 0x65, 0x0a, 0x50, 0x4b, 0x01,
	0x02, 0x1e, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xb0, 0x8f, 0x4c, 0x0f, 0x6f,
	0x4f, 0xf3, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6f,
	0x6f, 0x64, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54, 0x05, 0x00, 0x03, 0x3d, 0xa2, 0xd3, 0x5a,
	0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xf6, 0x01, 0x00, 0x00, 0x04, 0x14, 0x00, 0x00, 0x00,
	0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xb0, 0x8f,
	0x4c, 0x60, 0x41, 0x7b, 0x39, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x84, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x55, 0x00, 0x00,
	0x00, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2e, 0x2e,
	0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x65, 0x76, 0x69, 0x6c, 0x2e, 0x74, 0x78, 0x74, 0x50, 0x4b,
	0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b,
	0x01, 0x00, 0x00, 0x00, 0x00,
}
Output:

/tmp/evil.txt: illegal file path

func UnzipBytes added in v1.18.0

func UnzipBytes(src []byte, dest string) ([]string, error)

UnzipBytes is similar to Unzip but takes a zip archive as bytes instead of looking for a real file.

Example
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"moul.io/u"
)

func main() {
	// create tempdir for dest
	tempdir, err := ioutil.TempDir("", "u")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(tempdir)

	// unzip to dest
	files, err := u.UnzipBytes(zipdata_simple, tempdir)
	if err != nil {
		panic(err)
	}
	for _, file := range files {
		relPath := "." + strings.TrimPrefix(file, tempdir)
		fmt.Println(relPath)
	}
}

var zipdata_simple = []byte{
	0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x81, 0x61, 0x25, 0x3d, 0xc0, 0xd7,
	0xed, 0xc3, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x74, 0x65,
	0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54, 0x09, 0x00, 0x03, 0x71, 0xfc, 0x82, 0x4c, 0x76,
	0xfc, 0x82, 0x4c, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xf5, 0x01, 0x00, 0x00, 0x04, 0x14, 0x00,
	0x00, 0x00, 0x0b, 0xc9, 0xc8, 0x2c, 0x56, 0x00, 0xa2, 0x44, 0x85, 0x92, 0xd4, 0xe2, 0x12, 0x20,
	0x51, 0x51, 0xa2, 0x90, 0x96, 0x99, 0x93, 0xaa, 0xc7, 0x05, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x0a,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x7e, 0x25, 0x3d, 0xfe, 0x31, 0xd5, 0x54, 0x11, 0x03, 0x00,
	0x00, 0x11, 0x03, 0x00, 0x00, 0x14, 0x00, 0x1c, 0x00, 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, 0x63,
	0x6f, 0x6c, 0x6f, 0x72, 0x31, 0x36, 0x78, 0x31, 0x36, 0x2e, 0x70, 0x6e, 0x67, 0x55, 0x54, 0x09,
	0x00, 0x03, 0x3a, 0x30, 0x83, 0x4c, 0x3b, 0x30, 0x83, 0x4c, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04,
	0xf5, 0x01, 0x00, 0x00, 0x04, 0x14, 0x00, 0x00, 0x00, 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a,
	0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
	0x0f, 0x08, 0x06, 0x00, 0x00, 0x00, 0xed, 0x73, 0x4f, 0x2f, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41,
	0x4d, 0x41, 0x00, 0x00, 0xd6, 0xd8, 0xd4, 0x4f, 0x58, 0x32, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45,
	0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x41, 0x64, 0x6f, 0x62, 0x65,
	0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x00,
	0x00, 0x02, 0xa3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x74, 0x93, 0x5f, 0x48, 0x53, 0x51, 0x18,
	0xc0, 0xbf, 0x7b, 0xcf, 0xee, 0xc6, 0x75, 0x5b, 0x56, 0xda, 0x2a, 0x25, 0x6d, 0x6c, 0x06, 0x51,
	0x56, 0xf4, 0x5f, 0x23, 0xb7, 0x22, 0x22, 0xb0, 0x7a, 0x88, 0x41, 0x0f, 0x4b, 0x0c, 0x0a, 0xa3,
	0x82, 0x02, 0x1f, 0x62, 0x1a, 0x45, 0x2f, 0xa1, 0x83, 0x7a, 0x49, 0x2a, 0x86, 0x15, 0x35, 0xff,
	0x64, 0x98, 0x90, 0xa2, 0x91, 0x2e, 0x10, 0x9c, 0x2f, 0x53, 0x92, 0xc2, 0x07, 0xe9, 0xb6, 0x86,
	0x28, 0xda, 0x5d, 0xb6, 0xed, 0x5e, 0x75, 0x0e, 0xd9, 0x9f, 0xd3, 0x39, 0x9b, 0x8d, 0xad, 0xf2,
	0x83, 0xef, 0x9e, 0x7b, 0xbf, 0x3f, 0xbf, 0xf3, 0x7d, 0xdf, 0x39, 0x97, 0xc1, 0x18, 0x43, 0xa6,
	0xd8, 0xed, 0x76, 0xb0, 0xd9, 0x6c, 0x97, 0xcf, 0x9d, 0x3a, 0x7a, 0x23, 0xaf, 0xd0, 0x58, 0x14,
	0x94, 0xe4, 0x65, 0xdf, 0xc4, 0x97, 0x65, 0x25, 0x87, 0x14, 0x9c, 0x56, 0x57, 0xe1, 0x76, 0xbb,
	0xbd, 0x99, 0xf1, 0x8a, 0xcc, 0x8f, 0x96, 0x96, 0x56, 0xf4, 0xba, 0xbd, 0xcd, 0xe1, 0x68, 0xa8,
	0xbd, 0x64, 0x34, 0x1a, 0xe0, 0xb8, 0xe5, 0x1a, 0xdc, 0x6f, 0xb0, 0x43, 0x69, 0x91, 0x16, 0xa4,
	0x90, 0x04, 0xf3, 0x6c, 0xfe, 0x26, 0x12, 0x96, 0x05, 0x60, 0x68, 0x05, 0x35, 0x35, 0x35, 0x10,
	0x5e, 0x8a, 0xc0, 0xf0, 0xe0, 0x40, 0x67, 0xf3, 0x0b, 0xa7, 0xc5, 0xeb, 0x15, 0x40, 0xfc, 0x3a,
	0x02, 0xe6, 0xb3, 0x55, 0xa0, 0xe2, 0x10, 0xc4, 0x7f, 0x8e, 0x83, 0x82, 0xc1, 0x70, 0xfb, 0x51,
	0x57, 0xdd, 0x56, 0xc3, 0xb6, 0xc6, 0x58, 0x2c, 0x4a, 0x36, 0x6b, 0x49, 0x11, 0x28, 0x80, 0x65,
	0x99, 0x63, 0x05, 0xba, 0x75, 0x1d, 0x77, 0x6f, 0x56, 0x61, 0x59, 0x0a, 0x61, 0xcf, 0xe8, 0x27,
	0x7c, 0xe4, 0xd0, 0x5e, 0x7c, 0xa2, 0x6c, 0x27, 0x7e, 0xd3, 0x54, 0x8f, 0x27, 0x5c, 0xcf, 0xb0,
	0x6f, 0xc8, 0x89, 0x2f, 0x5a, 0x4e, 0x0a, 0xeb, 0xf3, 0x36, 0xb0, 0x66, 0xb3, 0x39, 0x99, 0x47,
	0x35, 0xd9, 0x82, 0x26, 0x87, 0x2f, 0x2b, 0xd1, 0x6f, 0x31, 0xed, 0x30, 0x16, 0xc1, 0xc8, 0x87,
	0x56, 0x00, 0xa4, 0x84, 0xca, 0x8a, 0xdd, 0x10, 0x8c, 0xaa, 0x41, 0xa3, 0xd5, 0x42, 0x3c, 0x91,
	0x80, 0x70, 0x64, 0x19, 0xce, 0x57, 0x56, 0x94, 0x08, 0x33, 0xf3, 0xd7, 0x37, 0x17, 0x14, 0x34,
	0x65, 0xb5, 0xc0, 0x30, 0x0c, 0x47, 0xde, 0x4b, 0xaf, 0x5e, 0x38, 0xd3, 0x7f, 0xeb, 0x8a, 0x25,
	0x7f, 0x61, 0x71, 0x09, 0x18, 0x16, 0xc1, 0xb4, 0x5f, 0x06, 0xdd, 0xda, 0x1c, 0x50, 0xa9, 0x38,
	0x60, 0x48, 0x80, 0x4a, 0xc9, 0x81, 0xa3, 0x73, 0x10, 0x1c, 0x6d, 0x3d, 0x8d, 0xf3, 0x52, 0xa8,
	0x8e, 0x02, 0xd8, 0x15, 0x50, 0x94, 0xe8, 0xd8, 0xdb, 0xf7, 0x43, 0x1d, 0x41, 0x39, 0x4c, 0xb0,
	0xa4, 0xef, 0x78, 0x02, 0x8a, 0x37, 0xe6, 0x26, 0x93, 0x02, 0x52, 0x38, 0x09, 0xa3, 0x92, 0xa7,
	0x51, 0xc2, 0x82, 0x2c, 0xf5, 0xfd, 0xa9, 0x80, 0xcd, 0x9c, 0xe8, 0x5c, 0x50, 0xbe, 0xf3, 0xc4,
	0xd9, 0x33, 0xc9, 0xab, 0x94, 0x29, 0x2a, 0x81, 0x90, 0xf9, 0x80, 0x77, 0x4a, 0x04, 0x61, 0x46,
	0x82, 0x68, 0x2c, 0x06, 0xba, 0xfc, 0x5c, 0xea, 0x0a, 0xfc, 0x17, 0x40, 0x44, 0x7a, 0xd5, 0xe5,
	0xb2, 0xf9, 0xa6, 0x45, 0x40, 0x28, 0xe5, 0x8a, 0xc6, 0xe2, 0xb0, 0x6f, 0x7b, 0x31, 0x98, 0x76,
	0x15, 0x26, 0xc3, 0x57, 0xe0, 0xea, 0xd5, 0x00, 0x10, 0x8b, 0xc7, 0x5d, 0xee, 0xd1, 0xf1, 0x25,
	0x5a, 0x7a, 0x7a, 0x50, 0x2c, 0x4b, 0x1f, 0x69, 0x20, 0x0d, 0x5b, 0x15, 0x40, 0x44, 0x16, 0xe7,
	0x82, 0x0b, 0x2c, 0xfb, 0xaf, 0x4b, 0xa1, 0x40, 0xf0, 0xc3, 0x1f, 0xa0, 0x04, 0x7f, 0xd6, 0x4d,
	0x34, 0x18, 0x0c, 0x29, 0x1a, 0x49, 0xfa, 0xee, 0x9b, 0x04, 0x44, 0x56, 0xe6, 0xaf, 0x64, 0x6a,
	0x63, 0x01, 0x43, 0x30, 0x82, 0x91, 0x5a, 0xb3, 0xe6, 0x34, 0x31, 0x35, 0xa7, 0x01, 0x1e, 0x8f,
	0x27, 0x19, 0xa4, 0x25, 0x67, 0x6e, 0xab, 0xab, 0x57, 0x7b, 0x06, 0x7b, 0x79, 0xba, 0x1b, 0x47,
	0x14, 0x21, 0xb2, 0x72, 0x0a, 0xa0, 0x47, 0xfb, 0xb8, 0xb5, 0xef, 0xd7, 0xc7, 0x51, 0xe1, 0xe5,
	0xc1, 0x03, 0xfb, 0x67, 0xb3, 0x2a, 0x28, 0x2f, 0x2f, 0x4f, 0x57, 0x20, 0x8a, 0x62, 0x42, 0x92,
	0xa4, 0xda, 0xa7, 0xed, 0xfd, 0x0f, 0x4d, 0x87, 0xf7, 0xe4, 0xce, 0xce, 0x4c, 0xc3, 0xb8, 0x30,
	0x15, 0x78, 0x37, 0x30, 0xdc, 0x8b, 0x11, 0x7f, 0xaf, 0xba, 0xba, 0x7a, 0xd2, 0x6a, 0xb5, 0x66,
	0xff, 0x4c, 0x82, 0x20, 0x64, 0x56, 0xbb, 0x48, 0xf4, 0xb9, 0xcb, 0x33, 0x61, 0x1d, 0xfb, 0xe6,
	0x8f, 0xf0, 0x3c, 0xff, 0xa0, 0xbb, 0xbb, 0xfb, 0x33, 0xb1, 0x85, 0xf4, 0x7a, 0x3d, 0xbd, 0x74,
	0x59, 0xad, 0xfd, 0x16, 0x60, 0x00, 0xa8, 0x08, 0x0d, 0x8b, 0x03, 0xe7, 0x62, 0xea, 0x00, 0x00,
	0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03,
	0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x81, 0x61, 0x25, 0x3d, 0xc0, 0xd7, 0xed, 0xc3, 0x19, 0x00,
	0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
	0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74,
	0x55, 0x54, 0x05, 0x00, 0x03, 0x71, 0xfc, 0x82, 0x4c, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xf5,
	0x01, 0x00, 0x00, 0x04, 0x14, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03, 0x0a, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x9d, 0x7e, 0x25, 0x3d, 0xfe, 0x31, 0xd5, 0x54, 0x11, 0x03, 0x00, 0x00,
	0x11, 0x03, 0x00, 0x00, 0x14, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xa4, 0x81, 0x5b, 0x00, 0x00, 0x00, 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, 0x63, 0x6f, 0x6c, 0x6f,
	0x72, 0x31, 0x36, 0x78, 0x31, 0x36, 0x2e, 0x70, 0x6e, 0x67, 0x55, 0x54, 0x05, 0x00, 0x03, 0x3a,
	0x30, 0x83, 0x4c, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xf5, 0x01, 0x00, 0x00, 0x04, 0x14, 0x00,
	0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0xa8, 0x00,
	0x00, 0x00, 0xba, 0x03, 0x00, 0x00, 0x1a, 0x00, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
	0x61, 0x20, 0x7a, 0x69, 0x70, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e,
	0x74, 0x2e,
}
Output:

./test.txt
./gophercolor16x16.png
Example (CreateAndUnzip)
package main

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"moul.io/u"
)

func main() {
	// create a custom zip
	buf := new(bytes.Buffer)
	{
		w := zip.NewWriter(buf)
		// a.txt
		{
			hdr := zip.FileHeader{
				Name:   "a.txt",
				Method: zip.Deflate,
			}
			hdr.SetMode(0o755)
			f, err := w.CreateHeader(&hdr)
			if err != nil {
				panic(err)
			}
			_, err = f.Write([]byte("hello world!"))
			if err != nil {
				panic(err)
			}
		}

		// b.txt -> a.txt
		{
			hdr := zip.FileHeader{
				Name:    "b.txt",
				Comment: "c",
				Method:  zip.Deflate,
			}
			hdr.SetMode(0o755 | os.ModeSymlink)
			f, err := w.CreateHeader(&hdr)
			if err != nil {
				panic(err)
			}
			_, err = f.Write([]byte("a.txt"))
			if err != nil {
				panic(err)
			}
		}

		err := w.Close()
		if err != nil {
			panic(err)
		}
	}

	// unzip it
	{
		// create tempdir for dest
		tempdir, err := ioutil.TempDir("", "u")
		if err != nil {
			panic(err)
		}
		defer os.RemoveAll(tempdir)

		// unzip to dest
		files, err := u.UnzipBytes(buf.Bytes(), tempdir)
		if err != nil {
			panic(err)
		}
		for _, file := range files {
			relPath := "." + strings.TrimPrefix(file, tempdir)
			stat, err := os.Lstat(file)
			if err != nil {
				panic(err)
			}
			fmt.Println(relPath, stat.Mode()&os.ModeSymlink != 0)
		}
	}
}
Output:

./a.txt false
./b.txt true
Example (CreateAndUnzipZipSlip)
package main

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"moul.io/u"
)

func main() {
	// create second temp dir
	var victim string
	{
		var err error
		victim, err = ioutil.TempDir("", "u")
		if err != nil {
			panic(err)
		}
		defer os.RemoveAll(victim)
	}

	// create a custom zip
	buf := new(bytes.Buffer)
	{
		w := zip.NewWriter(buf)
		// a.txt
		{
			hdr := zip.FileHeader{
				Name:   "a",
				Method: zip.Deflate,
			}
			hdr.SetMode(0o755 | os.ModeSymlink)
			f, err := w.CreateHeader(&hdr)
			if err != nil {
				panic(err)
			}
			_, err = f.Write([]byte(victim))
			if err != nil {
				panic(err)
			}
		}

		// b.txt -> a.txt
		{
			hdr := zip.FileHeader{
				Name:   "a/b.txt",
				Method: zip.Deflate,
			}
			hdr.SetMode(0o755)
			f, err := w.CreateHeader(&hdr)
			if err != nil {
				panic(err)
			}
			_, err = f.Write([]byte("hello world!"))
			if err != nil {
				panic(err)
			}
		}

		err := w.Close()
		if err != nil {
			panic(err)
		}
	}

	// unzip it
	{
		// create tempdir for dest
		tempdir, err := ioutil.TempDir("", "u")
		if err != nil {
			panic(err)
		}
		defer os.RemoveAll(tempdir)

		// unzip to dest
		_, err = u.UnzipBytes(buf.Bytes(), tempdir)
		errStr := err.Error()
		errStr = strings.Replace(errStr, tempdir, "TEMPDIR", -1)
		fmt.Println(errStr)
	}
}
Output:

TEMPDIR/a/b.txt: illegal file path

func WaitForCtrlC added in v1.10.0

func WaitForCtrlC()
Example
package main

import (
	"context"

	"moul.io/u"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		// do your stuff
		<-ctx.Done()
	}()

	u.WaitForCtrlC()
}
Output:

Types

type FutureRet added in v1.20.0

type FutureRet struct {
	Ret interface{}
	Err error
}

FutureRet is a generic struct returned by Future.

type MutexMap added in v1.18.0

type MutexMap struct {
	// contains filtered or unexported fields
}

MutexMap manages a pool of mutexes that can be get by key. MutexMap is thread-safe.

Example
package main

import (
	"fmt"
	"sync"
	"time"

	"moul.io/u"
)

func main() {
	var wg sync.WaitGroup
	var m u.MutexMap

	for i := 0; i < 10; i++ {
		name := fmt.Sprintf("abc%d", i)
		wg.Add(1)
		go func(i int) {
			fmt.Printf("init %s\n", name)
			defer m.Lock("abc-X")()
			sleep := time.Duration(100/(i+1)) * time.Millisecond
			time.Sleep(sleep)
			fmt.Printf("run %s (after sleep of %s)\n", name, sleep)
			wg.Done()
		}(i)
		time.Sleep(1 * time.Millisecond)
	}

	wg.Wait()

}
Output:

init abc0
init abc1
init abc2
init abc3
init abc4
init abc5
init abc6
init abc7
init abc8
init abc9
run abc0 (after sleep of 100ms)
run abc1 (after sleep of 50ms)
run abc2 (after sleep of 33ms)
run abc3 (after sleep of 25ms)
run abc4 (after sleep of 20ms)
run abc5 (after sleep of 16ms)
run abc6 (after sleep of 14ms)
run abc7 (after sleep of 12ms)
run abc8 (after sleep of 11ms)
run abc9 (after sleep of 10ms)

func (*MutexMap) Lock added in v1.18.0

func (mm *MutexMap) Lock(key string) func()

Lock locks a mutex by key, and returns a callback for unlocking unlock. Lock will automatically create a new mutex for new keys.

func (*MutexMap) RLock added in v1.27.0

func (mm *MutexMap) RLock(key string) func()

RLock locks a mutex by key for reading, and returns a callback for unlocking unlock. RLock will automatically create a new mutex for new keys.

type UniqueChild added in v1.6.0

type UniqueChild interface {
	SetChild(childFn func(context.Context))
	CloseChild()
}

UniqueChild is a goroutine manager (parent) that can only have one child at a time. When you call UniqueChild.SetChild(), UniqueChild cancels the previous child context (if any), then run a new child. The child needs to auto-kill itself when its context is done.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"moul.io/u"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	parent := u.NewUniqueChild(ctx)

	parent.SetChild(func(ctx context.Context) {
		select {
		case <-time.After(50 * time.Millisecond):
			fmt.Print("A")
		case <-ctx.Done():
		}
	})

	time.Sleep(100 * time.Millisecond)

	parent.SetChild(func(ctx context.Context) {
		select {
		case <-time.After(50 * time.Millisecond):
			fmt.Print("B")
		case <-ctx.Done():
		}
	})

	parent.SetChild(func(ctx context.Context) {
		select {
		case <-time.After(50 * time.Millisecond):
			fmt.Print("C")
		case <-ctx.Done():
		}
	})

	time.Sleep(100 * time.Millisecond)

}
Output:

AC

func NewUniqueChild added in v1.6.0

func NewUniqueChild(ctx context.Context) UniqueChild

NewUniqueChild instantiates and returns a UniqueChild manager.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL