github.com/vlifesystems/rulehunter@v0.0.0-20180501090014-673078aa4a83/watcher/watcher.go (about)

     1  // Copyright (C) 2016-2017 vLife Systems Ltd <http://vlifesystems.com>
     2  // Licensed under an MIT licence.  Please see LICENSE.md for details.
     3  
     4  // Package watcher is used to find experiment files that need processing
     5  package watcher
     6  
     7  import (
     8  	"github.com/vlifesystems/rulehunter/fileinfo"
     9  	"github.com/vlifesystems/rulehunter/logger"
    10  	"github.com/vlifesystems/rulehunter/quitter"
    11  	"io/ioutil"
    12  	"path/filepath"
    13  	"time"
    14  )
    15  
    16  // DirError indicates that there was an error reading the directory
    17  type DirError string
    18  
    19  func (e DirError) Error() string {
    20  	return "can not watch directory: " + string(e)
    21  }
    22  
    23  // Watch sends experiment filenames that need processing
    24  // to the filenames channel.  It checks every period of time.
    25  func Watch(
    26  	dir string,
    27  	period time.Duration,
    28  	l logger.Logger,
    29  	quit *quitter.Quitter,
    30  	files chan<- fileinfo.FileInfo,
    31  ) {
    32  	var lastLogErr error
    33  	quit.Add()
    34  	defer quit.Done()
    35  	ticker := time.NewTicker(period).C
    36  	allFiles, err := getFilesToMap(dir)
    37  	if err != nil {
    38  		lastLogErr = l.Error(err)
    39  	}
    40  
    41  	for _, file := range allFiles {
    42  		files <- file
    43  	}
    44  
    45  	// Used to only send old files every other run
    46  	flipFlop := true
    47  	for {
    48  		select {
    49  		case <-quit.C:
    50  			close(files)
    51  			return
    52  		case <-ticker:
    53  			newFiles, err := getFilesToMap(dir)
    54  			if err != nil {
    55  				if lastLogErr == nil || lastLogErr.Error() != err.Error() {
    56  					lastLogErr = l.Error(err)
    57  				}
    58  				break
    59  			}
    60  			lastLogErr = nil
    61  
    62  			for filename, file := range newFiles {
    63  				if lastFile, ok := allFiles[filename]; ok {
    64  					isNew := !fileinfo.IsEqual(lastFile, file)
    65  					if isNew {
    66  						files <- file
    67  					} else if flipFlop {
    68  						files <- file
    69  					}
    70  				} else {
    71  					files <- file
    72  				}
    73  				allFiles[filename] = file
    74  			}
    75  			allFiles = newFiles
    76  			flipFlop = !flipFlop
    77  		}
    78  	}
    79  }
    80  
    81  func GetExperimentFiles(dir string) ([]fileinfo.FileInfo, error) {
    82  	experimentFiles := make([]fileinfo.FileInfo, 0)
    83  	files, err := ioutil.ReadDir(dir)
    84  	if err != nil {
    85  		return experimentFiles, DirError(dir)
    86  	}
    87  
    88  	for _, file := range files {
    89  		ext := filepath.Ext(file.Name())
    90  		if !file.IsDir() && (ext == ".json" || ext == ".yaml") {
    91  			experimentFiles = append(experimentFiles, file)
    92  		}
    93  	}
    94  	return experimentFiles, nil
    95  }
    96  
    97  func getFilesToMap(dir string) (map[string]fileinfo.FileInfo, error) {
    98  	filesMap := make(map[string]fileinfo.FileInfo)
    99  	files, err := GetExperimentFiles(dir)
   100  	if err != nil {
   101  		return filesMap, err
   102  	}
   103  	for _, file := range files {
   104  		filesMap[file.Name()] = file
   105  	}
   106  	return filesMap, nil
   107  }