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 }