github.com/influx6/npkg@v0.8.8/njobs/njobs.go (about)

     1  package njobs
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path"
    10  	"reflect"
    11  
    12  	"github.com/influx6/npkg/nerror"
    13  	"github.com/influx6/npkg/nexec"
    14  )
    15  
    16  type Job interface {
    17  	Do(interface{}) (interface{}, error)
    18  }
    19  
    20  type JobFunction func(interface{}) (interface{}, error)
    21  
    22  func (fn JobFunction) Do(data interface{}) (interface{}, error) {
    23  	return fn(data)
    24  }
    25  
    26  // MoveLastForward moves the last result forward instead of the result
    27  // from this job.
    28  func MoveLastForward(doer JobFunction) JobFunction {
    29  	return func(lastResult interface{}) (interface{}, error) {
    30  		var _, newErr = doer(lastResult)
    31  		if newErr != nil {
    32  			return nil, nerror.WrapOnly(newErr)
    33  		}
    34  		return lastResult, nil
    35  	}
    36  }
    37  
    38  // ReadDir creates a new directory with giving mode.
    39  func ReadDir(dir string) JobFunction {
    40  	return func(rootDirData interface{}) (interface{}, error) {
    41  		var rootDir, ok = rootDirData.(string)
    42  		if !ok {
    43  			return nil, nerror.New("Expected rootDir path string as input")
    44  		}
    45  		var targetDir = path.Join(rootDir, dir)
    46  		var openedDir, err = os.Open(targetDir)
    47  		if err != nil {
    48  			return nil, nerror.WrapOnly(err)
    49  		}
    50  		var dirListing, dirListingErr = openedDir.Readdir(-1)
    51  		if dirListingErr != nil {
    52  			return nil, nerror.WrapOnly(dirListingErr)
    53  		}
    54  		return dirListing, nil
    55  	}
    56  }
    57  
    58  // ForEach expects a type of list of items and a function handle each items.
    59  // If skipResult is false, then the returned values of the doer function is gathered
    60  // into a new array and returned else, a nil array is always returned.
    61  //
    62  // Because the results are gathered into a new array when skipResult is false,
    63  // this means if an error occurred midway, you will receive an array with partial
    64  // results and an error.
    65  func ForEach(doer JobFunction, skipResult bool) JobFunction {
    66  	return func(targetList interface{}) (interface{}, error) {
    67  		var refValue = reflect.ValueOf(targetList)
    68  		if refValue.Kind() == reflect.Ptr {
    69  			refValue = refValue.Elem()
    70  		}
    71  		var refKind = refValue.Kind()
    72  		if refKind != reflect.Slice && refKind != reflect.Array {
    73  			return nil, nerror.New("argument is neither a slice or array")
    74  		}
    75  
    76  		var results []interface{}
    77  
    78  		if !skipResult {
    79  			results = make([]interface{}, 0, refValue.Len())
    80  		}
    81  
    82  		for i := 0; i < refValue.Len(); i++ {
    83  			var item = refValue.Index(i)
    84  			var result, resultErr = doer(item.Interface())
    85  			if resultErr != nil {
    86  				return results, nerror.Wrap(resultErr, "Failed processing: %#v", item)
    87  			}
    88  			if skipResult {
    89  				continue
    90  			}
    91  			results = append(results, result)
    92  		}
    93  
    94  		return results, nil
    95  	}
    96  }
    97  
    98  // Mkdir creates a new directory with giving mode.
    99  func Mkdir(dir string, mod os.FileMode) JobFunction {
   100  	return func(rootDirData interface{}) (interface{}, error) {
   101  		var rootDir, ok = rootDirData.(string)
   102  		if !ok {
   103  			return nil, nerror.New("Expected rootDir path string as input")
   104  		}
   105  		var targetDir = path.Join(rootDir, dir)
   106  		if err := os.MkdirAll(targetDir, mod); err != nil && err != os.ErrExist {
   107  			return nil, nerror.WrapOnly(err)
   108  		}
   109  		return targetDir, nil
   110  	}
   111  }
   112  
   113  // Println deletes incoming path string
   114  func Println(format string, writer io.Writer) JobFunction {
   115  	return Printf(format+"\n", writer)
   116  }
   117  
   118  // Printf deletes incoming path string
   119  func Printf(format string, writer io.Writer) JobFunction {
   120  	return func(dir interface{}) (interface{}, error) {
   121  		if _, err := fmt.Fprintf(writer, format, dir); err != nil {
   122  			return dir, err
   123  		}
   124  		return dir, nil
   125  	}
   126  }
   127  
   128  // DeletePath deletes incoming path string
   129  func DeletePath() JobFunction {
   130  	return func(dir interface{}) (interface{}, error) {
   131  		var rootDir, ok = dir.(string)
   132  		if !ok {
   133  			return nil, nerror.New("Expected rootDir path string as input")
   134  		}
   135  		var err = os.RemoveAll(rootDir)
   136  		if err != nil {
   137  			return rootDir, nerror.WrapOnly(err)
   138  		}
   139  		return rootDir, nil
   140  	}
   141  }
   142  
   143  // DeleteDir returns a new function to delete a dir provided.
   144  func DeleteDir(targetFile string) JobFunction {
   145  	return func(dir interface{}) (interface{}, error) {
   146  		var err = os.RemoveAll(targetFile)
   147  		if err != nil {
   148  			return targetFile, nerror.WrapOnly(err)
   149  		}
   150  		return targetFile, nil
   151  	}
   152  }
   153  
   154  // DeleteFile returns a new function to delete a file provided.
   155  func DeleteFile(targetFile string) JobFunction {
   156  	return func(dir interface{}) (interface{}, error) {
   157  		var err = os.Remove(targetFile)
   158  		if err != nil {
   159  			return targetFile, nerror.WrapOnly(err)
   160  		}
   161  		return targetFile, nil
   162  	}
   163  }
   164  
   165  // DeleteDirectoryFrom returns a new function to delete a file within directory passed to function.
   166  func DeleteDirectoryFrom(name string) JobFunction {
   167  	return func(dir interface{}) (interface{}, error) {
   168  		var rootDir, ok = dir.(string)
   169  		if !ok {
   170  			return nil, nerror.New("Expected rootDir path string as input")
   171  		}
   172  		var targetDir = path.Join(rootDir, name)
   173  		var err = os.RemoveAll(targetDir)
   174  		if err != nil {
   175  			return targetDir, nerror.WrapOnly(err)
   176  		}
   177  		return targetDir, nil
   178  	}
   179  }
   180  
   181  // DeleteFileFrom returns a new function to delete a file within directory passed to function.
   182  func DeleteFileFrom(name string) JobFunction {
   183  	return func(dir interface{}) (interface{}, error) {
   184  		var rootDir, ok = dir.(string)
   185  		if !ok {
   186  			return nil, nerror.New("Expected rootDir path string as input")
   187  		}
   188  		var targetFile = path.Join(rootDir, name)
   189  		var err = os.Remove(targetFile)
   190  		if err != nil {
   191  			return targetFile, nerror.WrapOnly(err)
   192  		}
   193  		return targetFile, nil
   194  	}
   195  }
   196  
   197  // JoinPath expects to receive a string which is path which it applies a the
   198  // join string to.
   199  func JoinPath(join string) JobFunction {
   200  	return func(dir interface{}) (interface{}, error) {
   201  		var rootDir, ok = dir.(string)
   202  		if !ok {
   203  			return nil, nerror.New("Expected rootDir path string as input")
   204  		}
   205  		return path.Join(rootDir, join), nil
   206  	}
   207  }
   208  
   209  // BackupPath expects to receive a string which is path which it applies a '..' to
   210  // to backup to the root directory.
   211  func BackupPath() JobFunction {
   212  	return JoinPath("..")
   213  }
   214  
   215  func WhenFileExists(job JobFunction) JobFunction {
   216  	return func(targetPath interface{}) (interface{}, error) {
   217  		var targetPathString, isString = targetPath.(string)
   218  		if !isString {
   219  			return nil, nerror.New("Expected value to be a string")
   220  		}
   221  		var _, statErr = os.Stat(targetPathString)
   222  		if statErr != nil && statErr != os.ErrNotExist {
   223  			return nil, nerror.WrapOnly(statErr)
   224  		}
   225  		return job(targetPathString)
   226  	}
   227  }
   228  
   229  func ExecuteCommand(ctx context.Context, dir string, cmd string, args []string, in io.Reader, envs map[string]string) JobFunction {
   230  	return func(targetPath interface{}) (interface{}, error) {
   231  		var result bytes.Buffer
   232  		var errBuf bytes.Buffer
   233  
   234  		var cmd = nexec.New(
   235  			nexec.Command(cmd),
   236  			nexec.SubCommands(args...),
   237  			nexec.Dir(dir),
   238  			nexec.Input(in),
   239  			nexec.Output(&result),
   240  			nexec.Err(&errBuf),
   241  			nexec.Envs(envs),
   242  			nexec.Async(),
   243  		)
   244  
   245  		if _, cmdErr := cmd.Exec(ctx); cmdErr != nil {
   246  			return nil, nerror.Wrap(cmdErr, "ErrorMessage: %q", errBuf.String())
   247  		}
   248  		return result.String(), nil
   249  	}
   250  }
   251  
   252  func WhenFilePathNotExists(targetFile string, job JobFunction) JobFunction {
   253  	return func(dir interface{}) (interface{}, error) {
   254  		var _, statErr = os.Stat(targetFile)
   255  		if statErr != nil && statErr != os.ErrNotExist {
   256  			return nil, nerror.WrapOnly(statErr)
   257  		}
   258  		if statErr != nil && statErr == os.ErrNotExist {
   259  			return job(targetFile)
   260  		}
   261  		return targetFile, nil
   262  	}
   263  }
   264  
   265  func WhenDirFileNotExists(targetPath string, job JobFunction) JobFunction {
   266  	return func(dir interface{}) (interface{}, error) {
   267  		var rootDir, ok = dir.(string)
   268  		if !ok {
   269  			return nil, nerror.New("Expected value to be a string")
   270  		}
   271  		var targetFile = path.Join(rootDir, targetPath)
   272  		var _, statErr = os.Stat(targetFile)
   273  		if statErr != nil && statErr != os.ErrNotExist {
   274  			return nil, nerror.WrapOnly(statErr)
   275  		}
   276  		if statErr != nil && statErr == os.ErrNotExist {
   277  			return job(targetFile)
   278  		}
   279  		return targetFile, nil
   280  	}
   281  }
   282  
   283  func WhenNextPathNotExists(job JobFunction) JobFunction {
   284  	return func(targetPath interface{}) (interface{}, error) {
   285  		var targetPathString, isString = targetPath.(string)
   286  		if !isString {
   287  			return nil, nerror.New("Expected value to be a string")
   288  		}
   289  		var _, statErr = os.Stat(targetPathString)
   290  		if statErr != nil && statErr != os.ErrNotExist {
   291  			return nil, nerror.WrapOnly(statErr)
   292  		}
   293  		if statErr != nil && statErr == os.ErrNotExist {
   294  			return job(targetPathString)
   295  		}
   296  		return targetPathString, nil
   297  	}
   298  }
   299  
   300  // File returns a new function to create a file within directory if not existing
   301  // passed to function.
   302  func File(name string, mod os.FileMode, r io.Reader) JobFunction {
   303  	return func(dir interface{}) (interface{}, error) {
   304  		var rootDir, ok = dir.(string)
   305  		if !ok {
   306  			return nil, nerror.New("Expected value to be a string")
   307  		}
   308  		var targetFile = path.Join(rootDir, name)
   309  		var _, statErr = os.Stat(targetFile)
   310  		if statErr == nil {
   311  			return targetFile, nil
   312  		}
   313  
   314  		var createdFile, err = os.OpenFile(targetFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mod)
   315  		if err != nil {
   316  			return nil, nerror.WrapOnly(err)
   317  		}
   318  		defer func() {
   319  			_ = createdFile.Close()
   320  		}()
   321  		if _, err := io.Copy(createdFile, r); err != nil {
   322  			return nil, nerror.WrapOnly(err)
   323  		}
   324  		return targetFile, nil
   325  	}
   326  }
   327  
   328  // NewFile returns a new function to create a file within directory passed to function.
   329  func NewFile(name string, mod os.FileMode, r io.Reader) JobFunction {
   330  	return func(dir interface{}) (interface{}, error) {
   331  		var rootDir, ok = dir.(string)
   332  		if !ok {
   333  			return nil, nerror.New("Expected value to be a string")
   334  		}
   335  		var targetFile = path.Join(rootDir, name)
   336  		var createdFile, err = os.OpenFile(targetFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mod)
   337  		if err != nil {
   338  			return nil, nerror.WrapOnly(err)
   339  		}
   340  		defer func() {
   341  			_ = createdFile.Close()
   342  		}()
   343  		if _, err := io.Copy(createdFile, r); err != nil {
   344  			return nil, nerror.WrapOnly(err)
   345  		}
   346  		return targetFile, nil
   347  	}
   348  }
   349  
   350  // Jobs manages a series of sequential jobs to be executed
   351  // one after another.
   352  type Jobs struct {
   353  	jobs []Job
   354  }
   355  
   356  func (j *Jobs) Add(jb Job) {
   357  	j.jobs = append(j.jobs, jb)
   358  }
   359  
   360  func (j *Jobs) Do(data interface{}) (interface{}, error) {
   361  	var err error
   362  	var d = data
   363  	for _, job := range j.jobs {
   364  		d, err = job.Do(d)
   365  		if err != nil {
   366  			return d, nerror.WrapOnly(err)
   367  		}
   368  	}
   369  	return d, nil
   370  }