app "roctorrent" packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br" } imports [pf.Stdout] provides [main] to pf BVal: [ BStr Str, BNum I64, BList (List BVal), # TODO add dict! ] # Decodes an exact string, errors if there is remaining unused data bDecodeStr: List U8 -> Result (List U8) [Malformatted] bDecodeStr = \i -> when bDecodeStrAndRemainder i is Ok {before, others} -> if List.len others == 0 then Ok before else Err Malformatted Err _ -> Err Malformatted # Decodes a string and returns the remainder of the input as well if there was any bDecodeStrAndRemainder: List U8 -> Result {before: List U8, others: List U8} [Malformatted] bDecodeStrAndRemainder = \i -> when (List.splitFirst i ':') is Ok {before, after} -> when (Str.fromUtf8 before) is Ok ls -> when (Str.toNat ls) is Ok l -> if List.len after >= l then Ok (List.split after l) else Err Malformatted Err _err -> Err Malformatted Err _err -> Err Malformatted Err _err -> Err Malformatted expect res = bDecodeStr (Str.toUtf8 "3:foo") res == Ok (Str.toUtf8 "foo") expect errCases = [ "", "foo", "1:foo", "4:foo", ":", "foo:", "1:", "::", ] List.all errCases \t -> res = bDecodeStr (Str.toUtf8 t) res == Err Malformatted # Decodes an exact number, exta input causes an error bDecodeNum: List U8 -> Result I64 [Malformatted Str] bDecodeNum = \i -> when bDecodeNumAndRem i is Ok {num, rem} -> if (List.len rem) == 0 then Ok num else Err (Malformatted "remaining characters after number") Err e -> Err e # Decodes a number and returns the number and any unused input bDecodeNumAndRem: List U8 -> Result {num: I64, rem: (List U8)} [Malformatted Str] bDecodeNumAndRem = \i -> when (List.first i) is Ok f -> if f == 'i' then when List.findFirstIndex i (\j -> j == 'e') is Ok ix -> {before, others} = List.split i (ix+1) numl = List.sublist before {start: 1, len: ix-1} when (Str.fromUtf8 numl) is Ok nums -> when (Str.toI64 nums) is Ok num -> Ok {num: num, rem: others} Err _e -> Err (Malformatted "failed to parse number from \(nums)") Err _e -> Err (Malformatted "failed to parse utf8") Err _e -> Err (Malformatted "failed to find number end char 'e'") else Err (Malformatted "number didn't start with 'i'") Err _e -> Err (Malformatted "number string was empty") expect res = bDecodeNum (Str.toUtf8 "i32e") res == Ok 32i64 expect errCases = [ {case: "", msg: "number string was empty"}, {case: "foo", msg: "number didn't start with 'i'"}, {case: "i32", msg: "failed to find number end char 'e'"}, {case: "32e", msg: "number didn't start with 'i'"}, {case: "ifooe", msg: "failed to parse number from foo"}, {case: "ie", msg: "failed to parse number from "}, {case: "i", msg: "failed to find number end char 'e'"}, {case: "e", msg: "number didn't start with 'i'"}, {case: "i522222222222222222343423432434322e", msg: "failed to parse number from 522222222222222222343423432434322"}, ] List.all errCases \{case, msg} -> res = bDecodeNum (Str.toUtf8 case) res == Err (Malformatted msg) bDecodeList: (List U8) -> Result (List BVal) [Malformatted] bDecodeList = \_i -> Ok [BNum 64, BStr "foo"] #when i is # ['l', .., 'e'] -> # _ -> Err Malformatted #bDecodeListInner: (List U8, List BVal) -> Result {res: (List BVal), others: (List U8)} [Malformatted] #bDecodeListInner = \i, o -> # when (List.first i) is # Ok first -> # when first is # '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' -> # when bDecodeStringAndRemainder first is # Ok {before, others} -> # Err _err -> Err Malformatted # 'i' -> # bDecode # Err _err -> Err Malformatted expect res = bDecodeList (Str.toUtf8 "li64e3:fooe") res == Ok [BNum 64, BStr "foo"] main = Stdout.line "Hello, World"