prefix.ha (2504B)
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 buffer. The buffer will be modified, and it will 8 // remain normalized, so any ".." components in the original buffer may be 9 // collapsed. 10 export fn prepend(buf: *buffer, prefix: str...) (str | error) = { 11 static let tmp = buffer { ... }; 12 tmp = *buf; 13 set(buf, prefix...)?; 14 return push(buf, string(&tmp)); 15 }; 16 17 // Returns a buffer 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 buffer. See [[popprefix]]. 23 export fn trimprefix(buf: *buffer, prefix: str) (str | error) = { 24 const start = splitprefix(buf, prefix)?; 25 if (start == buf.end) return "."; 26 return strings::fromutf8_unsafe(buf.buf[start..buf.end]); 27 }; 28 29 // Equivalent to [[trimprefix]], but modifies the buffer in the process. 30 export fn popprefix(buf: *buffer, prefix: str) (str | error) = { 31 const start = splitprefix(buf, prefix)?; 32 static delete(buf.buf[..][..start]); 33 buf.end -= start; 34 return string(buf); 35 }; 36 37 // helper function for trimprefix and popprefix, returns the new 38 // start of the buffer, or an error. 39 fn splitprefix(buf: *buffer, prefix: str) (size | error) = { 40 let pref = init(prefix)?; 41 if (pref.end == 0) { 42 if (abs(buf)) return not_prefix; 43 } else if (pref.end < buf.end && pref.buf[pref.end-1] != SEP) { 44 pref.buf[pref.end] = SEP; 45 pref.end += 1; 46 }; 47 if (bytes::hasprefix(buf.buf[..buf.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 buf = init("a")!; 56 57 // relative 58 assert(prepend(&buf, "apple")! == local("apple/a")); 59 assert(popprefix(&buf, "b") is error); 60 assert(popprefix(&buf, "appl") is error); 61 assert(popprefix(&buf, local("/")) is error); 62 assert(popprefix(&buf, ".")! == local("apple/a")); 63 assert(popprefix(&buf, "apple")! == "a"); 64 assert(popprefix(&buf, "a")! == "."); 65 66 // absolute 67 assert(prepend(&buf, local("/apple/a"))! == local("/apple/a")); 68 assert(popprefix(&buf, local("/b")) is error); 69 assert(popprefix(&buf, local("/appl")) is error); 70 assert(popprefix(&buf, ".") is error); 71 assert(popprefix(&buf, local("/"))! == local("apple/a")); 72 assert(prepend(&buf, local("/"))! == local("/apple/a")); 73 assert(popprefix(&buf, local("/apple/a"))! == "."); 74 };