#lang zuo

(require "harness.zuo")

(alert "strings")

(check (string? "apple"))
(check (string? #"apple"))
(check (string? ""))
(check (not (string? 'apple)))
(check (not (string? 10)))

(check (string 48 97) "0a")
(check (string) "")
(check-fail (string -1) "not an integer in [0, 255]")
(check-fail (string 256) "not an integer in [0, 255]")
(check-fail (string "a") "not an integer in [0, 255]")

(check (string 2 17) "\002\021")
(check (string 2 17) "\2\21")
(check (string 2 17) "\02\21")
(check (string 2 32 17) "\2 \21")
(check (string 2 32 17) "\02 \21")
(check (string 2 32 17 32) "\02 \21 ")
(check (string 34 49) "\421")

(check (string-length "") 0)
(check (string-length "apple") 5)
(check-fail (string-length 'apple) not-string)

(check (string-ref "0123" 0) 48)
(check (string-ref "0123" 2) 50)
(check-fail (string-ref "0123" 4) "out of bounds")
(check-fail (string-ref "0123" -1) "out of bounds")

(check (substring "0123" 0 0) "")
(check (substring "0123" 0 1) "0")
(check (substring "0123" 0 4) "0123")
(check (substring "0123" 4 4) "")
(check-fail (substring "0123" -1 0) "out of bounds")
(check-fail (substring "0123" 5 6) "out of bounds")
(check-fail (substring "0123" -1 5) "out of bounds")
(check-fail (substring "0123" 1 5) "out of bounds")
(check-fail (substring "0123" 1 0) "index less than starting")

(check (string-u32-ref "\000\000\000\000" 0) 0)
(check (string-u32-ref "\000\004\004\000" 0) (+ (* 256 4) (* 256 256 4)))
(check (string-u32-ref "\003\000\000\003" 0) (+ 3 (* 256 256 256 3)))
(check (string-u32-ref "\377\000\000\377" 0) (+ 255 (* 256 256 256 255)))

(check (string-u32-ref "__\000\000\000\000!" 2) 0)
(check (string-u32-ref "__\000\004\004\000!" 2) (+ (* 256 4) (* 256 256 4)))
(check (string-u32-ref "__\003\000\000\003!" 2) (+ 3 (* 256 256 256 3)))
(check (string-u32-ref "__\377\000\000\377!" 2) (+ 255 (* 256 256 256 255)))

(check (char "0") 48)
(check (char "\377") 255)
(check-fail (char) bad-stx)
(check-fail (char "0" "more") bad-stx)
(check-fail (char . "0") bad-stx)

(check (string-split " apple pie  " " ") '("" "apple" "pie" "" ""))
(check (string-split "__apple____pie__" "__") '("" "apple" "" "pie" ""))
(check (string-split " apple pie  ") '("apple" "pie"))
(check-fail (string-split 10) not-string)
(check-fail (string-split "apple" "") "not a nonempty string")

(check (string-join '("a" "b" "c")) "a b c")
(check (string-join '("a" "b" "c") "x") "axbxc")
(check (string-join '("a" "b" "c") "") "abc")
(check (string-join '()) "")
(check (string-join '() "x") "")
(check-fail (string-join 10) "not a list of strings")
(check-fail (string-join '("x") 10) not-string)

(check (string-trim "   a   ") "a")
(check (string-trim "   a  b  c   ") "a  b  c")
(check (string-trim "   a   " "  ") " a ")
(check (string-trim "     a     " "  ") " a ")
(check-fail (string-trim 10) not-string)
(check-fail (string-trim "apple" "") "not a nonempty string")

(let ([s "hello! /  \\  \\\\  // \\\" \"\\ the:re/547\\65\"13\"2-*()*^$*&^'|'&~``'"])
  (let i-loop ([i 0])
    (let j-loop ([j i])
      (let* ([s (substring s i j)])
        (check (shell->strings (string->shell s)) (list s))
        (check (shell->strings (~a "  " (string->shell s) "   ")) (list s)))
      (unless (= j (string-length s)) (j-loop (+ j 1))))
    (unless (= i (string-length s)) (i-loop (+ i 1)))))

(check (string-sha256 "hello\n") "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03")

(check (string->integer "10") 10)
(check (string->integer "-10") -10)
(check (string->integer "-0") 0)
(check (string->integer "-") #f)
(check (string->integer "") #f)
(check (string->integer "12x") #f)
(check (string->integer "9223372036854775807") 9223372036854775807)
(check (string->integer "9223372036854775808") #f)
(check (string->integer "-9223372036854775807") -9223372036854775807)
(check (string->integer "-9223372036854775808") -9223372036854775808)
(check (string->integer "-9223372036854775809") #f)
(check (string->integer "000000000000000000000007") 7)
(check-fail (string->integer 1) not-string)

(check (string<? "a" "b") #t)
(check (string<? "a" "apple") #t)
(check (string<? "b" "apple") #f)
(check (string<? "banana" "a") #f)
(check (string<? "" "") #f)
(check (string<? "" "x") #t)
(check-fail (string<? 1 "") not-string)
(check-fail (string<? "" 1) not-string)

(check (string-tree? "a"))
(check (string-tree? '("a")))
(check (string-tree? '("a" "b" ("c") () (((("d")))))))
(check (string-tree? 'a) #f)
(check (string-tree? '("a" "b" ("c") () ((((d)))))) #f)
(check (string-tree? '("a" "b" ("c") () (((("d" . "c")))))) #f)
