prefix.ha (2440B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bytes; 5 use strings; 6 7 // Add a prefix to a [[path]]. The [[path]] will be modified, and it will 8 // remain normalized, so any ".." components in the original [[path]] may be 9 // collapsed. 10 export fn prepend(p: *path, prefix: str...) (str | error) = { 11 static let tmp = path { ... }; 12 tmp = *p; 13 set(p, prefix...)?; 14 return push(p, string(&tmp)); 15 }; 16 17 // Returns a [[path]] without a prefix. The prefix is normalized before 18 // processing, and this function will return [[too_long]] if the prefix is 19 // longer than [[MAX]]. If the prefix is not present, returns [[not_prefix]]. 20 // The resulting path will always be relative. 21 // 22 // This function does not modify the [[path]]. See [[popprefix]]. 23 export fn trimprefix(p: *path, prefix: str) (str | error) = { 24 const start = splitprefix(p, prefix)?; 25 if (start == p.end) return "."; 26 return strings::fromutf8_unsafe(p.buf[start..p.end]); 27 }; 28 29 // Equivalent to [[trimprefix]], but modifies the [[path]] in the process. 30 export fn popprefix(p: *path, prefix: str) (str | error) = { 31 const start = splitprefix(p, prefix)?; 32 static delete(p.buf[..][..start]); 33 p.end -= start; 34 return string(p); 35 }; 36 37 // helper function for trimprefix and popprefix, returns the new 38 // start of the [[path]], or an error. 39 fn splitprefix(p: *path, prefix: str) (size | error) = { 40 let pref = init(prefix)?; 41 if (pref.end == 0) { 42 if (abs(p)) return not_prefix; 43 } else if (pref.end < p.end && pref.buf[pref.end-1] != SEP) { 44 pref.buf[pref.end] = SEP; 45 pref.end += 1; 46 }; 47 if (bytes::hasprefix(p.buf[..p.end], pref.buf[..pref.end])) { 48 return pref.end; 49 } else { 50 return not_prefix; 51 }; 52 }; 53 54 @test fn prepend() void = { 55 const p = init("a")!; 56 57 // relative 58 assert(prepend(&p, "apple")! == local("apple/a")); 59 assert(popprefix(&p, "b") is error); 60 assert(popprefix(&p, "appl") is error); 61 assert(popprefix(&p, local("/")) is error); 62 assert(popprefix(&p, ".")! == local("apple/a")); 63 assert(popprefix(&p, "apple")! == "a"); 64 assert(popprefix(&p, "a")! == "."); 65 66 // absolute 67 assert(prepend(&p, local("/apple/a"))! == local("/apple/a")); 68 assert(popprefix(&p, local("/b")) is error); 69 assert(popprefix(&p, local("/appl")) is error); 70 assert(popprefix(&p, ".") is error); 71 assert(popprefix(&p, local("/"))! == local("apple/a")); 72 assert(prepend(&p, local("/"))! == local("/apple/a")); 73 assert(popprefix(&p, local("/apple/a"))! == "."); 74 };