// Copyright (C) 2019 Storj Labs, Inc. // See LICENSE for copying information. package paths import ( "strings" ) // // To avoid confusion about when paths are encrypted, unencrypted, empty or // non existent, we create some wrapper types so that the compiler will complain // if someone attempts to use one in the wrong context. // // Unencrypted is an opaque type representing an unencrypted path. type Unencrypted struct { raw string } // Encrypted is an opaque type representing an encrypted path. type Encrypted struct { raw string } // // unencrypted paths // // NewUnencrypted takes a raw unencrypted path and returns it wrapped. func NewUnencrypted(raw string) Unencrypted { return Unencrypted{raw: raw} } // Valid returns if the unencrypted path is valid, which is the same as not being empty. func (path Unencrypted) Valid() bool { return path.raw != "" } // Raw returns the original raw path for the Unencrypted. func (path Unencrypted) Raw() string { return path.raw } // String returns a human readable form of the Unencrypted. func (path Unencrypted) String() string { return path.Raw() } // Consume attempts to remove the prefix from the Unencrypted path and // reports a boolean indicating if it was able to do so. func (path Unencrypted) Consume(prefix Unencrypted) (Unencrypted, bool) { if len(path.raw) >= len(prefix.raw) && path.raw[:len(prefix.raw)] == prefix.raw { return NewUnencrypted(path.raw[len(prefix.raw):]), true } return Unencrypted{}, false } // Iterator returns an iterator over the components of the Unencrypted. func (path Unencrypted) Iterator() Iterator { return NewIterator(path.raw) } // Less returns true if 'path' should be sorted earlier than 'other'. func (path Unencrypted) Less(other Unencrypted) bool { return path.raw < other.raw } // // encrypted path // // NewEncrypted takes a raw encrypted path and returns it wrapped. func NewEncrypted(raw string) Encrypted { return Encrypted{raw: raw} } // Valid returns if the encrypted path is valid, which is the same as not being empty. func (path Encrypted) Valid() bool { return path.raw != "" } // Raw returns the original path for the Encrypted. func (path Encrypted) Raw() string { return path.raw } // String returns a human readable form of the Encrypted. func (path Encrypted) String() string { return path.Raw() } // Consume attempts to remove the prefix from the Encrypted path and // reports a boolean indicating if it was able to do so. func (path Encrypted) Consume(prefix Encrypted) (Encrypted, bool) { if len(path.raw) >= len(prefix.raw) && path.raw[:len(prefix.raw)] == prefix.raw { return NewEncrypted(path.raw[len(prefix.raw):]), true } return Encrypted{}, false } // Iterator returns an iterator over the components of the Encrypted. func (path Encrypted) Iterator() Iterator { return NewIterator(path.raw) } // Less returns true if 'path' should be sorted earlier than 'other'. func (path Encrypted) Less(other Encrypted) bool { return path.raw < other.raw } // // path component iteration // // Iterator allows one to efficiently iterate over components of a path. type Iterator struct { raw string consumed int lastEmpty bool } // NewIterator returns an Iterator for components of the provided raw path. func NewIterator(raw string) Iterator { return Iterator{raw: raw, lastEmpty: raw != ""} } // Consumed reports how much of the path has been consumed (if any). func (pi Iterator) Consumed() string { return pi.raw[:pi.consumed] } // Remaining reports how much of the path is remaining. func (pi Iterator) Remaining() string { return pi.raw[pi.consumed:] } // Done reports if the path has been fully consumed. func (pi Iterator) Done() bool { return len(pi.raw) == pi.consumed && !pi.lastEmpty } // Next returns the first component of the path, consuming it. func (pi *Iterator) Next() string { if pi.Done() { return "" } rem := pi.Remaining() index := strings.IndexByte(rem, '/') if index == -1 { pi.consumed += len(rem) pi.lastEmpty = false return rem } pi.consumed += index + 1 pi.lastEmpty = index == len(rem)-1 return rem[:index] }