 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			232 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package datamodel
 | |
| 
 | |
| import (
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // Path describes a series of steps across a tree or DAG of Node,
 | |
| // where each segment in the path is a map key or list index
 | |
| // (literaly, Path is a slice of PathSegment values).
 | |
| // Path is used in describing progress in a traversal; and
 | |
| // can also be used as an instruction for traversing from one Node to another.
 | |
| // Path values will also often be encountered as part of error messages.
 | |
| //
 | |
| // (Note that Paths are useful as an instruction for traversing from
 | |
| // *one* Node to *one* other Node; to do a walk from one Node and visit
 | |
| // *several* Nodes based on some sort of pattern, look to IPLD Selectors,
 | |
| // and the 'traversal/selector' package in this project.)
 | |
| //
 | |
| // Path values are always relative.
 | |
| // Observe how 'traversal.Focus' requires both a Node and a Path argument --
 | |
| // where to start, and where to go, respectively.
 | |
| // Similarly, error values which include a Path will be speaking in reference
 | |
| // to the "starting Node" in whatever context they arose from.
 | |
| //
 | |
| // The canonical form of a Path is as a list of PathSegment.
 | |
| // Each PathSegment is a string; by convention, the string should be
 | |
| // in UTF-8 encoding and use NFC normalization, but all operations
 | |
| // will regard the string as its constituent eight-bit bytes.
 | |
| //
 | |
| // There are no illegal or magical characters in IPLD Paths
 | |
| // (in particular, do not mistake them for UNIX system paths).
 | |
| // IPLD Paths can only go down: that is, each segment must traverse one node.
 | |
| // There is no ".." which means "go up";
 | |
| // and there is no "." which means "stay here".
 | |
| // IPLD Paths have no magic behavior around characters such as "~".
 | |
| // IPLD Paths do not have a concept of "globs" nor behave specially
 | |
| // for a path segment string of "*" (but you may wish to see 'Selectors'
 | |
| // for globbing-like features that traverse over IPLD data).
 | |
| //
 | |
| // An empty string is a valid PathSegment.
 | |
| // (This leads to some unfortunate complications when wishing to represent
 | |
| // paths in a simple string format; however, consider that maps do exist
 | |
| // in serialized data in the wild where an empty string is used as the key:
 | |
| // it is important we be able to correctly describe and address this!)
 | |
| //
 | |
| // A string containing "/" (or even being simply "/"!) is a valid PathSegment.
 | |
| // (As with empty strings, this is unfortunate (in particular, because it
 | |
| // very much doesn't match up well with expectations popularized by UNIX-like
 | |
| // filesystems); but, as with empty strings, maps which contain such a key
 | |
| // certainly exist, and it is important that we be able to regard them!)
 | |
| //
 | |
| // A string starting, ending, or otherwise containing the NUL (\x00) byte
 | |
| // is also a valid PathSegment.  This follows from the rule of "a string is
 | |
| // regarded as its constituent eight-bit bytes": an all-zero byte is not exceptional.
 | |
| // In golang, this doesn't pose particular difficulty, but note this would be
 | |
| // of marked concern for languages which have "C-style nul-terminated strings".
 | |
| //
 | |
| // For an IPLD Path to be represented as a string, an encoding system
 | |
| // including escaping is necessary.  At present, there is not a single
 | |
| // canonical specification for such an escaping; we expect to decide one
 | |
| // in the future, but this is not yet settled and done.
 | |
| // (This implementation has a 'String' method, but it contains caveats
 | |
| // and may be ambiguous for some content.  This may be fixed in the future.)
 | |
| type Path struct {
 | |
| 	segments []PathSegment
 | |
| }
 | |
| 
 | |
| // NewPath returns a Path composed of the given segments.
 | |
| //
 | |
| // This constructor function does a defensive copy,
 | |
| // in case your segments slice should mutate in the future.
 | |
| // (Use NewPathNocopy if this is a performance concern,
 | |
| // and you're sure you know what you're doing.)
 | |
| func NewPath(segments []PathSegment) Path {
 | |
| 	p := Path{make([]PathSegment, len(segments))}
 | |
| 	copy(p.segments, segments)
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // NewPathNocopy is identical to NewPath but trusts that
 | |
| // the segments slice you provide will not be mutated.
 | |
| func NewPathNocopy(segments []PathSegment) Path {
 | |
| 	return Path{segments}
 | |
| }
 | |
| 
 | |
| // ParsePath converts a string to an IPLD Path, doing a basic parsing of the
 | |
| // string using "/" as a delimiter to produce a segmented Path.
 | |
| // This is a handy, but not a general-purpose nor spec-compliant (!),
 | |
| // way to create a Path: it cannot represent all valid paths.
 | |
| //
 | |
| // Multiple subsequent "/" characters will be silently collapsed.
 | |
| // E.g., `"foo///bar"` will be treated equivalently to `"foo/bar"`.
 | |
| // Prefixed and suffixed extraneous "/" characters are also discarded.
 | |
| // This makes this constructor incapable of handling some possible Path values
 | |
| // (specifically: paths with empty segements cannot be created with this constructor).
 | |
| //
 | |
| // There is no escaping mechanism used by this function.
 | |
| // This makes this constructor incapable of handling some possible Path values
 | |
| // (specifically, a path segment containing "/" cannot be created, because it
 | |
| // will always be intepreted as a segment separator).
 | |
| //
 | |
| // No other "cleaning" of the path occurs.  See the documentation of the Path struct;
 | |
| // in particular, note that ".." does not mean "go up", nor does "." mean "stay here" --
 | |
| // correspondingly, there isn't anything to "clean" in the same sense as
 | |
| // 'filepath.Clean' from the standard library filesystem path packages would.
 | |
| //
 | |
| // If the provided string contains unprintable characters, or non-UTF-8
 | |
| // or non-NFC-canonicalized bytes, no remark will be made about this,
 | |
| // and those bytes will remain part of the PathSegments in the resulting Path.
 | |
| func ParsePath(pth string) Path {
 | |
| 	// FUTURE: we should probably have some escaping mechanism which makes
 | |
| 	//  it possible to encode a slash in a segment.  Specification needed.
 | |
| 	ss := strings.FieldsFunc(pth, func(r rune) bool { return r == '/' })
 | |
| 	ssl := len(ss)
 | |
| 	p := Path{make([]PathSegment, ssl)}
 | |
| 	for i := 0; i < ssl; i++ {
 | |
| 		p.segments[i] = PathSegmentOfString(ss[i])
 | |
| 	}
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // String representation of a Path is simply the join of each segment with '/'.
 | |
| // It does not include a leading nor trailing slash.
 | |
| //
 | |
| // This is a handy, but not a general-purpose nor spec-compliant (!),
 | |
| // way to reduce a Path to a string.
 | |
| // There is no escaping mechanism used by this function,
 | |
| // and as a result, not all possible valid Path values (such as those with
 | |
| // empty segments or with segments containing "/") can be encoded unambiguously.
 | |
| // For Path values containing these problematic segments, ParsePath applied
 | |
| // to the string returned from this function may return a nonequal Path value.
 | |
| //
 | |
| // No escaping for unprintable characters is provided.
 | |
| // No guarantee that the resulting string is UTF-8 nor NFC canonicalized
 | |
| // is provided unless all the constituent PathSegment had those properties.
 | |
| func (p Path) String() string {
 | |
| 	l := len(p.segments)
 | |
| 	if l == 0 {
 | |
| 		return ""
 | |
| 	}
 | |
| 	sb := strings.Builder{}
 | |
| 	for i := 0; i < l-1; i++ {
 | |
| 		sb.WriteString(p.segments[i].String())
 | |
| 		sb.WriteByte('/')
 | |
| 	}
 | |
| 	sb.WriteString(p.segments[l-1].String())
 | |
| 	return sb.String()
 | |
| }
 | |
| 
 | |
| // Segments returns a slice of the path segment strings.
 | |
| //
 | |
| // It is not lawful to mutate nor append the returned slice.
 | |
| func (p Path) Segments() []PathSegment {
 | |
| 	return p.segments
 | |
| }
 | |
| 
 | |
| // Len returns the number of segments in this path.
 | |
| //
 | |
| // Zero segments means the path refers to "the current node".
 | |
| // One segment means it refers to a child of the current node; etc.
 | |
| func (p Path) Len() int {
 | |
| 	return len(p.segments)
 | |
| }
 | |
| 
 | |
| // Join creates a new path composed of the concatenation of this and the given path's segments.
 | |
| func (p Path) Join(p2 Path) Path {
 | |
| 	combinedSegments := make([]PathSegment, len(p.segments)+len(p2.segments))
 | |
| 	copy(combinedSegments, p.segments)
 | |
| 	copy(combinedSegments[len(p.segments):], p2.segments)
 | |
| 	p.segments = combinedSegments
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // AppendSegment is as per Join, but a shortcut when appending single segments.
 | |
| func (p Path) AppendSegment(ps PathSegment) Path {
 | |
| 	l := len(p.segments)
 | |
| 	combinedSegments := make([]PathSegment, l+1)
 | |
| 	copy(combinedSegments, p.segments)
 | |
| 	combinedSegments[l] = ps
 | |
| 	p.segments = combinedSegments
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // AppendSegmentString is as per AppendSegment, but a shortcut when the segment is a string.
 | |
| func (p Path) AppendSegmentString(ps string) Path {
 | |
| 	return p.AppendSegment(PathSegmentOfString(ps))
 | |
| }
 | |
| 
 | |
| // AppendSegmentInt is as per AppendSegment, but a shortcut when the segment is an int.
 | |
| func (p Path) AppendSegmentInt(ps int64) Path {
 | |
| 	return p.AppendSegment(PathSegmentOfInt(ps))
 | |
| }
 | |
| 
 | |
| // Parent returns a path with the last of its segments popped off (or
 | |
| // the zero path if it's already empty).
 | |
| func (p Path) Parent() Path {
 | |
| 	if len(p.segments) == 0 {
 | |
| 		return Path{}
 | |
| 	}
 | |
| 	return Path{p.segments[0 : len(p.segments)-1]}
 | |
| }
 | |
| 
 | |
| // Truncate returns a path with only as many segments remaining as requested.
 | |
| func (p Path) Truncate(i int) Path {
 | |
| 	return Path{p.segments[0:i]}
 | |
| }
 | |
| 
 | |
| // Last returns the trailing segment of the path.
 | |
| func (p Path) Last() PathSegment {
 | |
| 	if len(p.segments) < 1 {
 | |
| 		return PathSegment{}
 | |
| 	}
 | |
| 	return p.segments[len(p.segments)-1]
 | |
| }
 | |
| 
 | |
| // Pop returns a path with all segments except the last.
 | |
| func (p Path) Pop() Path {
 | |
| 	if len(p.segments) < 1 {
 | |
| 		return Path{}
 | |
| 	}
 | |
| 	return Path{p.segments[0 : len(p.segments)-1]}
 | |
| }
 | |
| 
 | |
| // Shift returns the first segment of the path together with the remaining path after that first segment.
 | |
| // If applied to a zero-length path, it returns an empty segment and the same zero-length path.
 | |
| func (p Path) Shift() (PathSegment, Path) {
 | |
| 	if len(p.segments) < 1 {
 | |
| 		return PathSegment{}, Path{}
 | |
| 	}
 | |
| 	return p.segments[0], Path{p.segments[1:]}
 | |
| }
 |