github.com/tooploox/oya@v0.0.21-0.20230524103240-1cda1861aad6/pkg/raw/requires.go (about)

     1  package raw
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/tooploox/oya/pkg/semver"
     9  	"github.com/tooploox/oya/pkg/types"
    10  )
    11  
    12  type Pack interface {
    13  	ImportPath() types.ImportPath
    14  	Version() semver.Version
    15  }
    16  
    17  type ErrNotRootOyafile struct {
    18  	Path string
    19  }
    20  
    21  func (e ErrNotRootOyafile) Error() string {
    22  	return fmt.Sprintf("missing Project: directive in %v; not root Oyafile?", e.Path)
    23  }
    24  
    25  var requireKeyRegxp = regexp.MustCompile("^Require:")
    26  var requireEntryRegexp = regexp.MustCompile("^(\\s*)([^:]+)\\:\\s*([^ #]+)")
    27  var topLevelKeyRegexp = regexp.MustCompile("^[\\s]+:")
    28  
    29  var defaultIndent = 2
    30  
    31  // AddRequire adds a Require: entry for the pack.
    32  func (raw *Oyafile) AddRequire(pack Pack) error {
    33  	if err := raw.addRequire(pack); err != nil {
    34  		return err
    35  	}
    36  	return raw.write()
    37  }
    38  
    39  // addRequire adds a Require: entry for a pack using the following algorithm:
    40  // 1. Look for and update an existing entry for the path.
    41  // 2. Look for ANY pack under Require:; if found, insert the new entry beneath it.
    42  // 3. Look for Require: key (we know it's empty), insert the new entry inside it.
    43  // 4. Look for Project: key, insert the new entry beneath it (under Require:).
    44  // 5. Fail because Oyafile has no Project: so we shouldn't be trying to add a require to it.
    45  // The method stops if any of the steps succeeds.
    46  // NOTE: It does not modify the Oyafile on disk.
    47  func (raw *Oyafile) addRequire(pack Pack) error {
    48  	if found, err := raw.updateExistingEntry(pack); err != nil || found {
    49  		return err // nil if found
    50  	}
    51  	if found, err := raw.insertBeforeExistingEntry(pack); err != nil || found {
    52  		return err // nil if found
    53  	}
    54  	if found, err := raw.insertAfter(requireKeyRegxp, formatRequire(defaultIndent, pack)); err != nil || found {
    55  		return err // nil if found
    56  	}
    57  
    58  	found, err := raw.insertAfter(projectRegexp, "Require:", formatRequire(defaultIndent, pack))
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	if !found {
    64  		return ErrNotRootOyafile{Path: raw.Path}
    65  	}
    66  	return nil
    67  }
    68  
    69  func (raw *Oyafile) updateExistingEntry(pack Pack) (bool, error) {
    70  	return raw.replaceAllWhen(
    71  		func(line string) bool {
    72  			if matches := requireEntryRegexp.FindStringSubmatch(line); len(matches) == 4 {
    73  				return types.ImportPath(matches[2]) == pack.ImportPath()
    74  			}
    75  			return false
    76  
    77  		}, []string{formatRequire(0, pack)}...)
    78  }
    79  
    80  func (raw *Oyafile) insertBeforeExistingEntry(pack Pack) (bool, error) {
    81  	return raw.insertBeforeWithin("Require", requireEntryRegexp, formatRequire(0, pack))
    82  }
    83  
    84  func formatRequire(indent int, pack Pack) string {
    85  	return fmt.Sprintf("%v%v: %v",
    86  		strings.Repeat(" ", indent),
    87  		pack.ImportPath(),
    88  		pack.Version())
    89  }