DominicBreuker/pspy

View on GitHub
internal/pspy/pspy.go

Summary

Maintainability
A
35 mins
Test Coverage
package pspy

import (
    "os"
    "time"

    "github.com/dominicbreuker/pspy/internal/config"
    "github.com/dominicbreuker/pspy/internal/logging"
    "github.com/dominicbreuker/pspy/internal/psscanner"
)

type Bindings struct {
    Logger Logger
    FSW    FSWatcher
    PSS    PSScanner
}

type Logger interface {
    Infof(format string, v ...interface{})
    Errorf(debug bool, format string, v ...interface{})
    Eventf(color int, format string, v ...interface{})
}

type FSWatcher interface {
    Init(rdirs, dirs []string) (chan error, chan struct{})
    Run() (chan struct{}, chan string, chan error)
    Enable()
}

type PSScanner interface {
    Run(triggerCh chan struct{}) (chan psscanner.PSEvent, chan error)
}

type chans struct {
    sigCh     chan os.Signal
    fsEventCh chan string
    psEventCh chan psscanner.PSEvent
}

func Start(cfg *config.Config, b *Bindings, sigCh chan os.Signal) chan struct{} {
    b.Logger.Infof("Config: %+v", cfg)
    abort := make(chan struct{}, 1)
    abort <- struct{}{}

    if !initFSW(b.FSW, cfg.RDirs, cfg.Dirs, b.Logger, sigCh) {
        return abort
    }
    triggerCh, fsEventCh, ok := startFSW(b.FSW, b.Logger, cfg.DrainFor, sigCh)
    if !ok {
        return abort
    }

    psEventCh := startPSS(b.PSS, b.Logger, triggerCh)

    triggerEvery(100*time.Millisecond, triggerCh)

    chans := &chans{
        sigCh:     sigCh,
        fsEventCh: fsEventCh,
        psEventCh: psEventCh,
    }
    exit := printOutput(cfg, b, chans)
    return exit
}

func printOutput(cfg *config.Config, b *Bindings, chans *chans) chan struct{} {
    exit := make(chan struct{})
    // fsEventColor, psEventColor := getColors(cfg.Colored)

    go func() {
        for {
            select {
            case se := <-chans.sigCh:
                b.Logger.Infof("Exiting program... (%s)", se)
                exit <- struct{}{}
            case fe := <-chans.fsEventCh:
                if cfg.LogFS {
                    b.Logger.Eventf(logging.ColorNone, "FS: %+v", fe)
                }
            case pe := <-chans.psEventCh:
                if cfg.LogPS {
                    color := logging.ColorNone
                    if cfg.Colored {
                        color = logging.GetColorByUID(pe.UID)
                    }
                    b.Logger.Eventf(color, "CMD: %+v", pe)
                }
            }
        }
    }()
    return exit
}

func initFSW(fsw FSWatcher, rdirs, dirs []string, logger Logger, sigCh <-chan os.Signal) bool {
    errCh, doneCh := fsw.Init(rdirs, dirs)
    for {
        select {
        case <-sigCh:
            return false
        case <-doneCh:
            return true
        case err := <-errCh:
            logger.Errorf(true, "initializing fs watcher: %v", err)
        }
    }
}

func startFSW(fsw FSWatcher, logger Logger, drainFor time.Duration, sigCh <-chan os.Signal) (triggerCh chan struct{}, fsEventCh chan string, ok bool) {
    triggerCh, fsEventCh, errCh := fsw.Run()
    go logErrors(errCh, logger)

    // ignore all file system events created on startup
    logger.Infof("Draining file system events due to startup...")
    ok = drainEventsFor(triggerCh, fsEventCh, drainFor, sigCh, fsw)
    logger.Infof("done")
    return
}

func startPSS(pss PSScanner, logger Logger, triggerCh chan struct{}) (psEventCh chan psscanner.PSEvent) {
    psEventCh, errCh := pss.Run(triggerCh)
    go logErrors(errCh, logger)
    return psEventCh
}

func triggerEvery(d time.Duration, triggerCh chan struct{}) {
    go func() {
        for {
            <-time.After(d)
            triggerCh <- struct{}{}
        }
    }()
}

func logErrors(errCh chan error, logger Logger) {
    for {
        err := <-errCh
        logger.Errorf(true, "ERROR: %v", err)
    }
}

func drainEventsFor(triggerCh chan struct{}, eventCh chan string, d time.Duration, sigCh <-chan os.Signal, fsw FSWatcher) bool {
    for {
        select {
        case <-sigCh:
            return false
        case <-time.After(d):
            fsw.Enable()
            return true
        }
    }
}