hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };