github.com/misseven0/notify@v0.0.0-20230519123055-c1422e46da05/node.go (about)

     1  // Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
     2  // Use of this source code is governed by the MIT license that can be
     3  // found in the LICENSE file.
     4  
     5  package notify
     6  
     7  import (
     8  	"errors"
     9  	"io/fs"
    10  	"os"
    11  	"path/filepath"
    12  )
    13  
    14  var errSkip = errors.New("notify: skip")
    15  
    16  type walkPathFunc func(nd node, isbase bool) error
    17  
    18  type walkFunc func(node) error
    19  
    20  func errnotexist(name string) error {
    21  	return &os.PathError{
    22  		Op:   "Node",
    23  		Path: name,
    24  		Err:  os.ErrNotExist,
    25  	}
    26  }
    27  
    28  type node struct {
    29  	Name  string
    30  	Watch watchpoint
    31  	Child map[string]node
    32  }
    33  
    34  func newnode(name string) node {
    35  	return node{
    36  		Name:  name,
    37  		Watch: make(watchpoint),
    38  		Child: make(map[string]node),
    39  	}
    40  }
    41  
    42  func (nd node) addchild(name, base string) node {
    43  	child, ok := nd.Child[base]
    44  	if !ok {
    45  		child = newnode(name)
    46  		nd.Child[base] = child
    47  	}
    48  	return child
    49  }
    50  
    51  func (nd node) Add(name string) node {
    52  	i := indexrel(nd.Name, name)
    53  	if i == -1 {
    54  		return node{}
    55  	}
    56  	for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) {
    57  		nd = nd.addchild(name[:i+j], name[i:i+j])
    58  		i += j + 1
    59  	}
    60  	return nd.addchild(name, name[i:])
    61  }
    62  
    63  func (nd node) AddDir(fn walkFunc) error {
    64  	stack := []node{nd}
    65  Traverse:
    66  	for n := len(stack); n != 0; n = len(stack) {
    67  		nd, stack = stack[n-1], stack[:n-1]
    68  		switch err := fn(nd); err {
    69  		case nil:
    70  		case errSkip:
    71  			continue Traverse
    72  		default:
    73  			return &os.PathError{
    74  				Op:   "error while traversing",
    75  				Path: nd.Name,
    76  				Err:  err,
    77  			}
    78  		}
    79  		// TODO(rjeczalik): tolerate open failures - add failed names to
    80  		// AddDirError and notify users which names are not added to the tree.
    81  		fi, err := os.ReadDir(nd.Name)
    82  		if err != nil {
    83  			return err
    84  		}
    85  		for _, fi := range fi {
    86  			if fi.Type()&(fs.ModeSymlink|fs.ModeDir) == fs.ModeDir {
    87  				name := filepath.Join(nd.Name, fi.Name())
    88  				stack = append(stack, nd.addchild(name, name[len(nd.Name)+1:]))
    89  			}
    90  		}
    91  	}
    92  	return nil
    93  }
    94  
    95  func (nd node) Get(name string) (node, error) {
    96  	i := indexrel(nd.Name, name)
    97  	if i == -1 {
    98  		return node{}, errnotexist(name)
    99  	}
   100  	ok := false
   101  	for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) {
   102  		if nd, ok = nd.Child[name[i:i+j]]; !ok {
   103  			return node{}, errnotexist(name)
   104  		}
   105  		i += j + 1
   106  	}
   107  	if nd, ok = nd.Child[name[i:]]; !ok {
   108  		return node{}, errnotexist(name)
   109  	}
   110  	return nd, nil
   111  }
   112  
   113  func (nd node) Del(name string) error {
   114  	i := indexrel(nd.Name, name)
   115  	if i == -1 {
   116  		return errnotexist(name)
   117  	}
   118  	stack := []node{nd}
   119  	ok := false
   120  	for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) {
   121  		if nd, ok = nd.Child[name[i:i+j]]; !ok {
   122  			return errnotexist(name[:i+j])
   123  		}
   124  		stack = append(stack, nd)
   125  		i += j + 1
   126  	}
   127  	if _, ok = nd.Child[name[i:]]; !ok {
   128  		return errnotexist(name)
   129  	}
   130  	delete(nd.Child, name[i:])
   131  	for name, i = name[i:], len(stack); i != 0; name, i = base(nd.Name), i-1 {
   132  		nd = stack[i-1]
   133  		if nd := nd.Child[name]; len(nd.Watch) > 1 || len(nd.Child) != 0 {
   134  			break
   135  		} else {
   136  			nd.Child = nil
   137  			nd.Watch = nil
   138  		}
   139  		delete(nd.Child, name)
   140  	}
   141  	return nil
   142  }
   143  
   144  func (nd node) Walk(fn walkFunc) error {
   145  	stack := []node{nd}
   146  Traverse:
   147  	for n := len(stack); n != 0; n = len(stack) {
   148  		nd, stack = stack[n-1], stack[:n-1]
   149  		switch err := fn(nd); err {
   150  		case nil:
   151  		case errSkip:
   152  			continue Traverse
   153  		default:
   154  			return err
   155  		}
   156  		for name, nd := range nd.Child {
   157  			if name == "" {
   158  				// Node storing inactive watchpoints has empty name, skip it
   159  				// form traversing. Root node has also an empty name, but it
   160  				// never has a parent node.
   161  				continue
   162  			}
   163  			stack = append(stack, nd)
   164  		}
   165  	}
   166  	return nil
   167  }
   168  
   169  func (nd node) WalkPath(name string, fn walkPathFunc) error {
   170  	i := indexrel(nd.Name, name)
   171  	if i == -1 {
   172  		return errnotexist(name)
   173  	}
   174  	ok := false
   175  	for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) {
   176  		switch err := fn(nd, false); err {
   177  		case nil:
   178  		case errSkip:
   179  			return nil
   180  		default:
   181  			return err
   182  		}
   183  		if nd, ok = nd.Child[name[i:i+j]]; !ok {
   184  			return errnotexist(name[:i+j])
   185  		}
   186  		i += j + 1
   187  	}
   188  	switch err := fn(nd, false); err {
   189  	case nil:
   190  	case errSkip:
   191  		return nil
   192  	default:
   193  		return err
   194  	}
   195  	if nd, ok = nd.Child[name[i:]]; !ok {
   196  		return errnotexist(name)
   197  	}
   198  	switch err := fn(nd, true); err {
   199  	case nil, errSkip:
   200  		return nil
   201  	default:
   202  		return err
   203  	}
   204  }
   205  
   206  type root struct {
   207  	nd node
   208  }
   209  
   210  func (r root) addroot(name string) node {
   211  	if vol := filepath.VolumeName(name); vol != "" {
   212  		root, ok := r.nd.Child[vol]
   213  		if !ok {
   214  			root = r.nd.addchild(vol, vol)
   215  		}
   216  		return root
   217  	}
   218  	return r.nd
   219  }
   220  
   221  func (r root) root(name string) (node, error) {
   222  	if vol := filepath.VolumeName(name); vol != "" {
   223  		nd, ok := r.nd.Child[vol]
   224  		if !ok {
   225  			return node{}, errnotexist(name)
   226  		}
   227  		return nd, nil
   228  	}
   229  	return r.nd, nil
   230  }
   231  
   232  func (r root) Add(name string) node {
   233  	return r.addroot(name).Add(name)
   234  }
   235  
   236  func (r root) AddDir(dir string, fn walkFunc) error {
   237  	return r.Add(dir).AddDir(fn)
   238  }
   239  
   240  func (r root) Del(name string) error {
   241  	nd, err := r.root(name)
   242  	if err != nil {
   243  		return err
   244  	}
   245  	return nd.Del(name)
   246  }
   247  
   248  func (r root) Get(name string) (node, error) {
   249  	nd, err := r.root(name)
   250  	if err != nil {
   251  		return node{}, err
   252  	}
   253  	if nd.Name != name {
   254  		if nd, err = nd.Get(name); err != nil {
   255  			return node{}, err
   256  		}
   257  	}
   258  	return nd, nil
   259  }
   260  
   261  func (r root) Walk(name string, fn walkFunc) error {
   262  	nd, err := r.Get(name)
   263  	if err != nil {
   264  		return err
   265  	}
   266  	return nd.Walk(fn)
   267  }
   268  
   269  func (r root) WalkPath(name string, fn walkPathFunc) error {
   270  	nd, err := r.root(name)
   271  	if err != nil {
   272  		return err
   273  	}
   274  	return nd.WalkPath(name, fn)
   275  }