github.com/jmigpin/editor@v1.6.0/core/fswatcher/gwatcher.go (about)

     1  package fswatcher
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"sort"
     8  	"strings"
     9  	"sync"
    10  )
    11  
    12  // graph based watcher
    13  type GWatcher struct {
    14  	w      Watcher
    15  	events chan interface{}
    16  	root   struct {
    17  		sync.Mutex
    18  		n *Node
    19  	}
    20  }
    21  
    22  func NewGWatcher(w Watcher) *GWatcher {
    23  	*w.OpMask() = AllOps
    24  
    25  	gw := &GWatcher{w: w}
    26  	gw.events = make(chan interface{})
    27  
    28  	gw.root.Lock()
    29  	gw.root.n = NewNode(string(os.PathSeparator), nil)
    30  	gw.root.Unlock()
    31  
    32  	go gw.eventLoop()
    33  	return gw
    34  }
    35  
    36  //----------
    37  
    38  func (gw *GWatcher) OpMask() *Op {
    39  	return gw.w.OpMask()
    40  }
    41  
    42  func (gw *GWatcher) Close() error {
    43  	return gw.w.Close()
    44  }
    45  
    46  //----------
    47  
    48  func (gw *GWatcher) Events() <-chan interface{} {
    49  	return gw.events
    50  }
    51  func (gw *GWatcher) eventLoop() {
    52  	defer close(gw.events)
    53  	for {
    54  		ev, ok := <-gw.w.Events()
    55  		if !ok {
    56  			break
    57  		}
    58  		switch t := ev.(type) {
    59  		case error:
    60  			gw.events <- t
    61  		case *Event:
    62  			gw.handleEv(t)
    63  		}
    64  	}
    65  }
    66  func (gw *GWatcher) handleEv(ev *Event) {
    67  	u := ev.Name
    68  	switch ev.Op {
    69  	case Create, Remove, Rename:
    70  		_ = gw.review(u)
    71  	case Modify:
    72  		_ = gw.modify(u)
    73  	}
    74  }
    75  
    76  //----------
    77  
    78  func (gw *GWatcher) Add(name string) error {
    79  	if err := gw.normalize(&name); err != nil {
    80  		return err
    81  	}
    82  
    83  	v := gw.split(name)
    84  	gw.root.Lock()
    85  	defer gw.root.Unlock()
    86  	gw.root.n.add(v, func(n *Node) {
    87  		p := n.path()
    88  		if !n.added {
    89  			err := gw.w.Add(p)
    90  			if err == nil {
    91  				n.added = true
    92  			}
    93  		}
    94  	})
    95  
    96  	return nil
    97  }
    98  
    99  //----------
   100  
   101  func (gw *GWatcher) Remove(name string) error {
   102  	if err := gw.normalize(&name); err != nil {
   103  		return err
   104  	}
   105  
   106  	v := gw.split(name)
   107  	gw.root.Lock()
   108  	defer gw.root.Unlock()
   109  	gw.root.n.remove(v, func(n *Node) {
   110  		if n.target {
   111  			n.target = false
   112  			if n.added {
   113  				n.added = false
   114  				p := n.path()
   115  				_ = gw.w.Remove(p)
   116  			}
   117  		}
   118  		if len(n.childs) == 0 {
   119  			n.delete()
   120  		}
   121  	})
   122  
   123  	return nil
   124  }
   125  
   126  //----------
   127  
   128  func (gw *GWatcher) review(name string) error {
   129  	if err := gw.normalize(&name); err != nil {
   130  		return err
   131  	}
   132  
   133  	v := gw.split(name)
   134  	gw.root.Lock()
   135  	defer gw.root.Unlock()
   136  	gw.root.n.review(v, func(n *Node) {
   137  		p := n.path()
   138  		err := gw.w.Add(p)
   139  		wasAdded := n.added
   140  		n.added = err == nil
   141  		if n.target {
   142  			if !wasAdded && n.added {
   143  				gw.events <- &Event{Op: Create, Name: p}
   144  			}
   145  			if wasAdded && !n.added {
   146  				gw.events <- &Event{Op: Remove, Name: p}
   147  			}
   148  		}
   149  	})
   150  
   151  	return nil
   152  }
   153  
   154  //----------
   155  
   156  func (gw *GWatcher) modify(name string) error {
   157  	if err := gw.normalize(&name); err != nil {
   158  		return err
   159  	}
   160  
   161  	v := gw.split(name)
   162  	gw.root.Lock()
   163  	defer gw.root.Unlock()
   164  	gw.root.n.modify(v, func(n *Node) {
   165  		if n.target {
   166  			p := n.path()
   167  			gw.events <- &Event{Op: Modify, Name: p}
   168  		}
   169  	})
   170  
   171  	return nil
   172  }
   173  
   174  //----------
   175  
   176  func (gw *GWatcher) split(name string) []string {
   177  	u := strings.Split(name, string(os.PathSeparator))
   178  	w := []string{}
   179  	for _, k := range u {
   180  		if strings.TrimSpace(k) != "" {
   181  			w = append(w, k)
   182  		}
   183  	}
   184  	return w
   185  }
   186  
   187  func (gw *GWatcher) normalize(name *string) error {
   188  	u, err := filepath.Abs(*name)
   189  	if err != nil {
   190  		return err
   191  	}
   192  	*name = u
   193  	return nil
   194  }
   195  
   196  //----------
   197  
   198  type Node struct {
   199  	name   string
   200  	childs map[string]*Node
   201  	parent *Node
   202  
   203  	target bool
   204  	added  bool
   205  }
   206  
   207  func NewNode(name string, parent *Node) *Node {
   208  	n := &Node{name: name, childs: map[string]*Node{}}
   209  	if parent != nil {
   210  		n.parent = parent
   211  		parent.childs[name] = n
   212  	}
   213  	return n
   214  }
   215  
   216  func (n *Node) delete() {
   217  	if n.parent != nil {
   218  		delete(n.parent.childs, n.name)
   219  	}
   220  }
   221  
   222  //----------
   223  
   224  func (n *Node) visit(v []string, create, visSubChilds, depthFirst bool, fn func(*Node)) {
   225  	if depthFirst {
   226  		defer fn(n)
   227  	} else {
   228  		fn(n)
   229  	}
   230  	if len(v) == 0 {
   231  		if visSubChilds {
   232  			for _, c := range n.childs {
   233  				c.visit(nil, create, visSubChilds, depthFirst, fn)
   234  			}
   235  		}
   236  		return
   237  	}
   238  	k := v[0]
   239  	c, ok := n.childs[k]
   240  	if !ok {
   241  		if !create {
   242  			return
   243  		}
   244  		c = NewNode(k, n)
   245  	}
   246  	if create && len(v) == 1 {
   247  		c.target = true
   248  	}
   249  	c.visit(v[1:], create, visSubChilds, depthFirst, fn)
   250  }
   251  
   252  //----------
   253  
   254  func (n *Node) add(v []string, fn func(*Node)) {
   255  	n.visit(v, true, false, false, fn)
   256  }
   257  func (n *Node) review(v []string, fn func(*Node)) {
   258  	n.visit(v, false, true, false, fn)
   259  }
   260  func (n *Node) remove(v []string, fn func(*Node)) {
   261  	n.visit(v, false, false, true, fn)
   262  }
   263  func (n *Node) modify(v []string, fn func(*Node)) {
   264  	n.visit(v, false, false, false, fn)
   265  }
   266  
   267  //----------
   268  
   269  func (n *Node) path() string {
   270  	if n.parent == nil {
   271  		return n.name
   272  	}
   273  	return filepath.Join(n.parent.path(), n.name)
   274  }
   275  
   276  //----------
   277  
   278  func (n *Node) SprintFlatTree() string {
   279  	s := fmt.Sprintf("{%s:", n.name)
   280  
   281  	// sort childs map keys
   282  	keys := []string{}
   283  	for k := range n.childs {
   284  		keys = append(keys, k)
   285  	}
   286  	sort.Strings(keys)
   287  
   288  	for i, k := range keys {
   289  		cn := n.childs[k]
   290  		if i > 0 {
   291  			s += ","
   292  		}
   293  		s += cn.SprintFlatTree()
   294  	}
   295  	s += "}"
   296  	return s
   297  }