github.com/haraldrudell/parl@v0.4.176/punix/errno.go (about)

     1  /*
     2  © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  // Package punix examines unix.Errno errors and identifies Unix-like platforms.
     7  package punix
     8  
     9  import (
    10  	"errors"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/haraldrudell/parl"
    15  	"golang.org/x/sys/unix"
    16  )
    17  
    18  // IsENOENT returns true if the root cause of err is file not found.
    19  // Can be used with os.Open* file not found
    20  func IsENOENT(err error) (isENOENT bool) {
    21  	return Errno(err) == unix.ENOENT
    22  }
    23  
    24  // IsConnectionRefused searches the error chsin of err for unix.ECONNREFUSED
    25  // net.Dialer errors for closed socket
    26  func IsConnectionRefused(err error) (isConnectionRefused bool) {
    27  	return Errno(err) == unix.ECONNREFUSED
    28  }
    29  
    30  // Errno scans an error chain for a unix.Errno type.
    31  //   - if no errno value exists, unix.Errno 0x0 is returned
    32  //   - unlike most error implementations, unix.Errno type is uintptr
    33  //   - to check for error condition of a unix.Errno error: if errno != 0 {…
    34  //   - to obtain errno number: int(errno)
    35  //   - to print errno number: fmt.Sprintf("%d", errno) → "1"
    36  //   - to print errno message: fmt.Sprintf("%v", unix.EPERM) → "operation not permitted"
    37  //   - to obtain errno name: unix.ErrnoName(unix.EPERM) → "EPERM"
    38  //   - to print hexadecimal errno:
    39  //
    40  // Note: unix.Errno.Error has value receiver.
    41  // Errno checks:
    42  //
    43  //	Errno(nil) == 0 → true.
    44  //	if errno != 0 {…
    45  //	int(errno) // numeric value
    46  //	 if errno == unix.ENOENT…
    47  //	 fmt.Sprintf("%d", unix.EPERM) → "1"
    48  //	 fmt.Printf("%v", errno) → "state not recoverable"
    49  //	 unix.ErrnoName(unix.EPERM) → "EPERM"
    50  //	 var i, s = int(errno), ""
    51  //	 if i < 0 { i = -i; s = "-" }
    52  //	 fmt.Printf("%s0x%x", s, i) → 0x68
    53  func Errno(err error) (errnoValue unix.Errno) {
    54  	for ; err != nil; err = errors.Unwrap(err) {
    55  		var ok bool
    56  		if errnoValue, ok = err.(unix.Errno); ok {
    57  			return // match return
    58  		}
    59  	}
    60  	return // no match return
    61  }
    62  
    63  // ErrnoString returns the errno number as a string if
    64  // the err error chain has a non-zero syscall.Errno error.
    65  //   - if label is empty string, no label is returned
    66  //   - if no syscall.Errno is found or it is zero, the empty string is returned
    67  //   - ErrnoString("errno", nil) → ""
    68  //   - ErrnoString("errno", unix.EPERM) → "errno: EPERM 1 0x1"
    69  //   - ErrnoString("errno", unix.Errno(math.MaxUint)) → "-1 -0x1"
    70  func ErrnoString(label string, err error) (errnoNumericString string) {
    71  	var unixErrno = Errno(err)
    72  	if unixErrno == 0 {
    73  		return // no errno error return: ""
    74  	}
    75  
    76  	if label != "" {
    77  		errnoNumericString = label + ":\x20"
    78  	}
    79  
    80  	if name := unix.ErrnoName(unixErrno); name != "" {
    81  		errnoNumericString += name + "\x20"
    82  	}
    83  
    84  	var errno = int(unixErrno)
    85  	errnoNumericString += strconv.Itoa(errno) + "\x20"
    86  
    87  	var hexErrno = int64(errno)
    88  	var sign string
    89  	if hexErrno < 0 {
    90  		sign = "-"
    91  		hexErrno = -hexErrno
    92  	}
    93  	errnoNumericString += sign + "0x" + strconv.FormatInt(hexErrno, 16)
    94  
    95  	return
    96  }
    97  
    98  // ErrnoError gets the errno interpretation if the error chain does contain
    99  // a unix.Errno type.
   100  // if includeError is true, the error chain’s error message is prepended.
   101  // if includeError is true and err is nil "OK" is returned
   102  // if includeError is false or missing and no errno exists, the empty string is returned
   103  func ErrnoError(err error, includeError ...bool) (errnoString string) {
   104  	var isInclude bool
   105  	if len(includeError) > 0 {
   106  		isInclude = includeError[0]
   107  	}
   108  
   109  	// handle err == nil case
   110  	if err == nil {
   111  		if isInclude {
   112  			return "OK"
   113  		} else {
   114  			return
   115  		}
   116  	}
   117  
   118  	// handle includeError
   119  	var sList []string
   120  	if isInclude {
   121  		sList = append(sList, err.Error())
   122  	}
   123  
   124  	// handle errno
   125  	if unixErrno := Errno(err); unixErrno != 0 {
   126  		sList = append(sList, parl.Sprintf("errno:'%s'0x%x:temporary:%t:timeout:%t",
   127  			unixErrno.Error(),
   128  			uint(unixErrno),
   129  			unixErrno.Temporary(),
   130  			unixErrno.Timeout(),
   131  		))
   132  	}
   133  
   134  	return strings.Join(sList, "\x20")
   135  }