hare

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

names.ha (5146B)


      1 // License: MPL-2.0
      2 // (c) 2022 Alexey Yerin <yyp@disroot.org>
      3 // (c) 2021-2022 Drew DeVault <sir@cmpwn.com>
      4 // (c) 2021 Eyal Sawady <ecs@d2evs.net>
      5 use bytes;
      6 use strings;
      7 
      8 // Returns the directory name for a given path. For a path to a file name, this
      9 // returns the directory in which that file resides. For a path to a directory,
     10 // this returns the path to its parent directory. If the path consists solely of
     11 // the target's path separator, a string to the path is returned unmodified. If
     12 // the path is empty, "." is returned. The return value is either borrowed from
     13 // the input or statically allocated; use [[strings::dup]] to extend its
     14 // lifetime or modify it.
     15 export fn dirname(path: (str | *buffer)) const str = {
     16 	let path = getstring(path);
     17 	if (path == "") {
     18 		return ".";
     19 	};
     20 	let trimmed = strings::rtrim(path, PATHSEP: u32: rune);
     21 	if (trimmed == "") {
     22 		return pathsepstr;
     23 	};
     24 	let b = strings::toutf8(trimmed);
     25 	let i = match (bytes::rindex(b, PATHSEP)) {
     26 	case void =>
     27 		return ".";
     28 	case let z: size =>
     29 		yield z;
     30 	};
     31 	if (i == 0) {
     32 		i += 1;
     33 	};
     34 	path = strings::fromutf8_unsafe(b[..i]);
     35 	path = strings::rtrim(path, PATHSEP: u32: rune);
     36 	if (path == "") {
     37 		return pathsepstr;
     38 	};
     39 	return path;
     40 };
     41 
     42 @test fn dirname() void = {
     43 	assertpatheql(&dirname, pathsepstr, "", "foo");
     44 	assertpatheql(&dirname, pathsepstr, pathsepstr);
     45 	assertpatheql(&dirname, pathsepstr, "", "", "");
     46 	assertpatheql(&dirname, pathsepstr, "", "", "", "");
     47 	assertpatheql(&dirname, "foo", "foo", "bar");
     48 	assertpatheql(&dirname, ".", "");
     49 	assertpatheql(&dirname, ".", "foo");
     50 	assertpatheql(&dirname, ".", "foo", "");
     51 	assertpatheql(&dirname, ".", "foo", "", "");
     52 	assertpatheql(&dirname, pathsepstr, "", "", "", "foo");
     53 	assertpatheql(&dirname, pathsepstr, "", "", "", "foo", "", "");
     54 	let expected = strings::concat(pathsepstr, "foo");
     55 	assertpatheql(&dirname, expected, "", "foo", "bar");
     56 	free(expected);
     57 	expected = strings::concat(pathsepstr, pathsepstr, "foo");
     58 	assertpatheql(&dirname, expected, "", "", "foo", "", "", "bar", "", "");
     59 	free(expected);
     60 };
     61 
     62 // Returns the final component of a given path. For a path to a file name, this
     63 // returns the file name. For a path to a directory, this returns the directory
     64 // name. If the path consists solely of the target's path separator, a string of
     65 // the path is returned unmodified. If the path is empty, "." is returned. The
     66 // return value is either borrowed from the input or statically allocated; use
     67 // [[strings::dup]] to extend its lifetime or modify it.
     68 export fn basename(path: (str | *buffer)) const str = {
     69 	let path = getstring(path);
     70 	if (path == "") {
     71 		return ".";
     72 	};
     73 	let trimmed = strings::rtrim(path, PATHSEP: u32: rune);
     74 	if (trimmed == "") {
     75 		return pathsepstr;
     76 	};
     77 	let b = strings::toutf8(trimmed);
     78 	let i = match (bytes::rindex(b, PATHSEP)) {
     79 	case void =>
     80 		return trimmed;
     81 	case let z: size =>
     82 		yield if (z + 1 < len(b)) z + 1z else 0z;
     83 	};
     84 	return strings::fromutf8_unsafe(b[i..]);
     85 };
     86 
     87 @test fn basename() void = {
     88 	assertpatheql(&basename, "bar", "", "foo", "bar");
     89 	assertpatheql(&basename, "foo", "", "foo");
     90 	assertpatheql(&basename, pathsepstr, pathsepstr);
     91 	assertpatheql(&basename, pathsepstr, "", "", "");
     92 	assertpatheql(&basename, pathsepstr, "", "", "", "");
     93 	assertpatheql(&basename, "bar", "foo", "bar");
     94 	assertpatheql(&basename, "bar", "foo", "bar", "", "");
     95 	assertpatheql(&basename, "foo", "foo");
     96 	assertpatheql(&basename, "foo", "foo", "");
     97 	assertpatheql(&basename, "bar", "foo", "bar", "");
     98 	assertpatheql(&basename, ".", "");
     99 };
    100 
    101 // Returns the file name and extension for a path. The return value is borrowed
    102 // from the input, see [[strings::dup]] to extend its lifetime.
    103 //
    104 // The extension includes the '.' character.
    105 //
    106 // 	extension("foo/example") => ("example", "")
    107 // 	extension("foo/example.txt") => ("example", ".txt")
    108 // 	extension("foo/example.tar.gz") => ("example.tar", ".gz")
    109 export fn extension(p: (str | *buffer)) (str, str) = {
    110 	let p = getstring(p);
    111 	if (p == "") {
    112 		return ("", "");
    113 	};
    114 	let p = basename(p);
    115 	let b = strings::toutf8(p);
    116 	if (len(b) == 0 || b[len(b) - 1] == PATHSEP) {
    117 		return (p, "");
    118 	};
    119 	let i = match (bytes::rindex(b, '.')) {
    120 	case void =>
    121 		return (p, "");
    122 	case let z: size =>
    123 		yield z;
    124 	};
    125 	let e = b[i..];
    126 	let n = b[..i];
    127 	return (strings::fromutf8_unsafe(n), strings::fromutf8_unsafe(e));
    128 };
    129 
    130 @test fn extension() void = {
    131 	assertpatheql(&ext0, "", "");
    132 	assertpatheql(&ext1, "", "");
    133 	assertpatheql(&ext0, "bar", "foo", "bar");
    134 	assertpatheql(&ext1, "", "foo", "bar");
    135 	assertpatheql(&ext0, "bar", "foo", "bar.txt");
    136 	assertpatheql(&ext1, ".txt", "foo", "bar.txt");
    137 	assertpatheql(&ext0, "bar.tar", "foo", "bar.tar.gz");
    138 	assertpatheql(&ext1, ".gz", "foo", "bar.tar.gz");
    139 	assertpatheql(&ext0, "baz", "foo.bar", "baz.ha");
    140 	assertpatheql(&ext1, ".ha", "foo.bar", "baz.ha");
    141 };
    142 
    143 fn assertpatheql(
    144 	func: *fn(path: (str | *buffer)) const str,
    145 	expected: str,
    146 	path: str...
    147 ) void = {
    148 	const s = strings::join(pathsepstr, path...);
    149 	assert(func(s) == expected);
    150 	free(s);
    151 };
    152 
    153 fn ext0(p: (str | *buffer)) const str = extension(p).0;
    154 fn ext1(p: (str | *buffer)) const str = extension(p).1;