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 }