Skip to content
This repository was archived by the owner on Jul 10, 2025. It is now read-only.

Commit 31aadc7

Browse files
committed
cleaned code, improved docs and tests
Signed-off-by: Matt Butcher <matt.butcher@microsoft.com>
1 parent 582861a commit 31aadc7

File tree

5 files changed

+53
-8
lines changed

5 files changed

+53
-8
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ build:
1515
.PHONY: test-unit
1616
test-unit:
1717
grain tests.gr
18-
18+
1919
.PHONY: test
2020
test:build
2121
test: test-unit
2222
test:
23+
@echo EXPECT: Loading file fileserver.gr
2324
wasmtime --dir . --env PATH_INFO=${PATH_INFO} \
2425
--env X_MATCHED_ROUTE=${X_MATCHED_ROUTE} \
2526
fileserver.gr.wasm > /dev/null

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,26 @@ To compile:
2727
$ make build
2828
```
2929

30-
To run test (Wasmtime required):
30+
You can run unit tests with `make test-unit` or full tests with `make test`:
3131

3232
```
3333
$ make test
34+
grain compile fileserver.gr
35+
grain tests.gr
36+
✅ PASS Env.splitEnvVar should parse
37+
✅ PASS Util.reverse should reverse string
38+
✅ PASS Util.lastIndexOf should find Some
39+
===== Expected: =====
40+
Some(19)
41+
======= Got: ========
42+
Some(18)
43+
=====================
44+
⛔️ FAIL UtillastIndexOf should find Some
45+
✅ PASS Util.lastIndexOf should find None
46+
✅ PASS Mediatype.guess should find text/plain
47+
✅ PASS Mediatype.guess should find default type
48+
❌ Total failed tests: 1❌
49+
make: *** [test-unit] Error 1
3450
```
3551

3652
## Running in Wagi

fileserver.gr

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Option from "option"
66
import File from "sys/file"
77
import String from "string"
88
import Mediatype from "./lib/mediatype"
9+
import Stringutil from "./lib/stringutil"
910

1011
let serve = (path) => {
1112
File.fdWrite(File.stderr, "Fileserver: Loading file ")
@@ -45,11 +46,8 @@ let serve = (path) => {
4546

4647
let guestpath = (env) => {
4748
let req = Option.unwrap(Map.get("PATH_INFO", env))
48-
let mut base = Option.unwrap(Map.get("X_MATCHED_ROUTE", env))
49-
let dots = String.indexOf("/...", base)
50-
if (Option.isSome(dots) ) {
51-
base = String.slice(0, Option.unwrap(dots), base)
52-
}
49+
let matched = Option.unwrap(Map.get("X_MATCHED_ROUTE", env))
50+
let base = Stringutil.beforeLast("/...", matched)
5351
String.slice(String.length(base) + 1, String.length(req), req)
5452
}
5553

lib/stringutil.gr

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,35 @@ export let reverse = (str: String) => {
1616
}
1717

1818
// Get the index of the last appearance of needle in the haystack.
19+
//
20+
// For a multi-character needle, this will return the end of that sequence,
21+
// not the beginning (as indexOf does).
22+
//
1923
// @param needle: The string to search for
2024
// @param haystack: The string to be searched
2125
// @return Option<Number> The offset, if found, or a number
2226
export let lastIndexOf = (needle: String, haystack: String) => {
2327
let rev = reverse(haystack)
2428
let revNeedle = reverse(needle)
29+
let nlen = String.length(needle)
2530
let i = String.indexOf(revNeedle, rev)
2631
match (i) {
2732
Some(offset) => Some(String.length(haystack) - 1 - offset),
2833
None => None,
2934
}
3035
}
36+
37+
export let afterLast = (needle: String, haystack: String) => {
38+
match (lastIndexOf(needle, haystack)) {
39+
Some(index) => String.slice(index + 1, String.length(haystack), haystack),
40+
None => haystack
41+
}
42+
}
43+
44+
export let beforeLast = (needle: String, haystack: String) => {
45+
let nlen = String.length(needle)
46+
match (lastIndexOf(needle, haystack)) {
47+
Some(index) => String.slice(0, index + 1 - nlen, haystack),
48+
None => haystack
49+
}
50+
}

tests.gr

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ let check = (a, b, msg: String) => {
1212
true => Ok(String.concat("✅ PASS\t\t", msg)),
1313
_ => {
1414
totalErr += 1
15+
print("===== Expected: =====")
16+
print(a)
17+
print("======= Got: ========")
18+
print(b)
19+
print("=====================")
1520
Err(String.concat("⛔️ FAIL\t\t", msg))
1621
}
1722
}
@@ -35,8 +40,13 @@ let report = () => {
3540

3641
expect(("a", "b"), Env.splitEnvVar("a=b"), "Env.splitEnvVar should parse")
3742
expect("gfedcba", Util.reverse("abcdefg"), "Util.reverse should reverse string")
38-
expect(Some(5), Util.lastIndexOf("/.", "aaaa/."), "UtillastIndexOf should find Some")
43+
expect(Some(5), Util.lastIndexOf("/.", "aaaa/."), "Util.lastIndexOf should find Some")
44+
expect(Some(18), Util.lastIndexOf(".", "aaaa/fileserver.gr.wasm"), "Util.lastIndexOf should find last dot, not first dot")
45+
expect(Some(12), Util.lastIndexOf(".", "/.../aaaa/..."), "Util.lastIndexOf should find last set of three dots")
3946
expect(None, Util.lastIndexOf("??", "aaaa.."), "Util.lastIndexOf should find None")
47+
expect("test", Util.afterLast("$.$", "foo$.$bar$.$test"), "Util.afterLast should find last match")
48+
expect("/prefix/../path", Util.beforeLast("/..", "/prefix/../path/.."), "Util.beforeLast should return first part")
49+
expect("/prefix/../path/..", Util.beforeLast("/$$", "/prefix/../path/.."), "Util.beforeLast should return entire string when no match")
4050
expect("text/plain", Mediatype.guess("foo.txt"), "Mediatype.guess should find text/plain")
4151
expect("application/octet-stream", Mediatype.guess("foo.MADEUP"), "Mediatype.guess should find default type")
4252

0 commit comments

Comments
 (0)