github.com/mckael/restic@v0.8.3/internal/restic/backend_find.go (about)

     1  package restic
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/restic/restic/internal/errors"
     7  )
     8  
     9  // ErrNoIDPrefixFound is returned by Find() when no ID for the given prefix
    10  // could be found.
    11  var ErrNoIDPrefixFound = errors.New("no ID found")
    12  
    13  // ErrMultipleIDMatches is returned by Find() when multiple IDs with the given
    14  // prefix are found.
    15  var ErrMultipleIDMatches = errors.New("multiple IDs with prefix found")
    16  
    17  // Find loads the list of all files of type t and searches for names which
    18  // start with prefix. If none is found, nil and ErrNoIDPrefixFound is returned.
    19  // If more than one is found, nil and ErrMultipleIDMatches is returned.
    20  func Find(be Lister, t FileType, prefix string) (string, error) {
    21  	match := ""
    22  
    23  	ctx, cancel := context.WithCancel(context.TODO())
    24  	defer cancel()
    25  
    26  	err := be.List(ctx, t, func(fi FileInfo) error {
    27  		if prefix == fi.Name[:len(prefix)] {
    28  			if match == "" {
    29  				match = fi.Name
    30  			} else {
    31  				return ErrMultipleIDMatches
    32  			}
    33  		}
    34  
    35  		return nil
    36  	})
    37  
    38  	if err != nil {
    39  		return "", err
    40  	}
    41  
    42  	if match != "" {
    43  		return match, nil
    44  	}
    45  
    46  	return "", ErrNoIDPrefixFound
    47  }
    48  
    49  const minPrefixLength = 8
    50  
    51  // PrefixLength returns the number of bytes required so that all prefixes of
    52  // all names of type t are unique.
    53  func PrefixLength(be Lister, t FileType) (int, error) {
    54  	// load all IDs of the given type
    55  	list := make([]string, 0, 100)
    56  
    57  	ctx, cancel := context.WithCancel(context.TODO())
    58  	defer cancel()
    59  
    60  	err := be.List(ctx, t, func(fi FileInfo) error {
    61  		list = append(list, fi.Name)
    62  		return nil
    63  	})
    64  
    65  	if err != nil {
    66  		return 0, err
    67  	}
    68  
    69  	// select prefixes of length l, test if the last one is the same as the current one
    70  	id := ID{}
    71  outer:
    72  	for l := minPrefixLength; l < len(id); l++ {
    73  		var last string
    74  
    75  		for _, name := range list {
    76  			if last == name[:l] {
    77  				continue outer
    78  			}
    79  			last = name[:l]
    80  		}
    81  
    82  		return l, nil
    83  	}
    84  
    85  	return len(id), nil
    86  }