diff --git a/src/Network/GemServ.hs b/src/Network/GemServ.hs index 6f9946d..9ab6455 100644 --- a/src/Network/GemServ.hs +++ b/src/Network/GemServ.hs @@ -25,9 +25,11 @@ License along with this program. If not, see -} module Network.GemServ ( - encodeRequest + encodeRequest, + escapeString ) where +import Data.Char (ord) import Data.List (intercalate) import Network.GemServ.Types @@ -45,4 +47,21 @@ encodeRequest req = "" -> "" q -> '?' : q +-- | add required escape sequences to a string +escapeString :: String -> String +escapeString = concatMap $ \ch -> + if ch `elem` unescaped + then [ch] + else '%' : toHex ch + where + unescaped = ['0'..'9'] ++ ['A'..'Z'] ++ ['a'..'z'] ++ "~-_." + toHex ch = let + n = ord ch + high = n `div` 16 + low = n `mod` 16 + in [hexDigits !! high, hexDigits !! low] + +hexDigits :: String +hexDigits = ['0'..'9'] ++ ['a'..'f'] + --jl diff --git a/test/Network/GemServSpec.hs b/test/Network/GemServSpec.hs index 4cde415..7657075 100644 --- a/test/Network/GemServSpec.hs +++ b/test/Network/GemServSpec.hs @@ -28,8 +28,9 @@ import Network.GemServ import Network.GemServ.Types spec :: Spec -spec = describe "Network.GemServ" +spec = describe "Network.GemServ" $ do encodeRequestSpec + escapeStringSpec encodeRequestSpec :: Spec encodeRequestSpec = describe "encodeRequest" $ mapM_ @@ -55,4 +56,16 @@ encodeRequestSpec = describe "encodeRequest" $ mapM_ withQueryReq = simpleReq { reqQuery = "foo" } withQueryExp = "gemini://example.com/?foo" +escapeStringSpec :: Spec +escapeStringSpec = describe "escapeString" $ mapM_ + ( \(input, expected) -> context (show input) $ + it ("should be " ++ show expected) $ + escapeString input `shouldBe` expected + ) + + -- input, expected + [ ( "~foo-bar_baz.quux", "~foo-bar_baz.quux" ) + , ( "foo:/?=&#%", "foo%3a%2f%3f%3d%26%23%25" ) + ] + --jl