From c4da4badc6a04cb582966280c409d74f07c2d852 Mon Sep 17 00:00:00 2001 From: Nick Saunders Date: Thu, 8 Aug 2019 17:54:05 -0700 Subject: [PATCH] Add safe versions of Global.Unsafe functions. --- bower.json | 4 +++ src/Global.js | 31 +++++++++++++++++++ src/Global.purs | 74 ++++++++++++++++++++++++++++++++++++++++++++- test/Test/Main.js | 1 + test/Test/Main.purs | 59 +++++++++++++++++++++++++++++++++++- 5 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 test/Test/Main.js diff --git a/bower.json b/bower.json index 787643f..5bf4aab 100644 --- a/bower.json +++ b/bower.json @@ -19,5 +19,9 @@ "devDependencies": { "purescript-assert": "^4.0.0", "purescript-console": "^4.0.0" + }, + "dependencies": { + "purescript-maybe": "^4.0.1", + "purescript-functions": "^4.0.0" } } diff --git a/src/Global.js b/src/Global.js index e86d8ab..d941da0 100644 --- a/src/Global.js +++ b/src/Global.js @@ -16,3 +16,34 @@ exports.readInt = function (radix) { }; exports.readFloat = parseFloat; + +var formatNumber = function (format) { + return function (fail, succ, digits, n) { + try { + return succ(n[format](digits)); + } + catch (e) { + return fail(e.message); + } + }; +}; + +exports._toFixed = formatNumber("toFixed"); +exports._toExponential = formatNumber("toExponential"); +exports._toPrecision = formatNumber("toPrecision"); + +var encdecURI = function (encdec) { + return function (fail, succ, s) { + try { + return succ(encdec(s)); + } + catch (e) { + return fail(e.message); + } + }; +}; + +exports._decodeURI = encdecURI(decodeURI); +exports._encodeURI = encdecURI(encodeURI); +exports._decodeURIComponent = encdecURI(decodeURIComponent); +exports._encodeURIComponent = encdecURI(encodeURIComponent); diff --git a/src/Global.purs b/src/Global.purs index 2f39be6..dea9a85 100644 --- a/src/Global.purs +++ b/src/Global.purs @@ -1,6 +1,24 @@ -- | This module defines types for some global Javascript functions -- | and values. -module Global where +module Global + ( nan + , isNaN + , infinity + , isFinite + , readInt + , readFloat + , toFixed + , toExponential + , toPrecision + , decodeURI + , encodeURI + , decodeURIComponent + , encodeURIComponent + ) where + +import Prelude +import Data.Function.Uncurried (Fn3, Fn4, runFn3, runFn4) +import Data.Maybe (Maybe(..)) -- | Not a number (NaN) foreign import nan :: Number @@ -19,3 +37,57 @@ foreign import readInt :: Int -> String -> Number -- | Parse a floating point value from a `String` foreign import readFloat :: String -> Number + +foreign import _toFixed :: forall a. Fn4 (String -> a) (String -> a) Int Number a + +foreign import _toExponential :: forall a. Fn4 (String -> a) (String -> a) Int Number a + +foreign import _toPrecision :: forall a. Fn4 (String -> a) (String -> a) Int Number a + +-- | Formats Number as a String with limited number of digits after the dot. +-- | May return `Nothing` when specified number of digits is less than 0 or +-- | greater than 20. See ECMA-262 for more information. +toFixed :: Int -> Number -> Maybe String +toFixed digits n = runFn4 _toFixed (const Nothing) Just digits n + +-- | Formats Number as String in exponential notation limiting number of digits +-- | after the decimal dot. May return `Nothing` when specified number of +-- | digits is less than 0 or greater than 20 depending on the implementation. +-- | See ECMA-262 for more information. +toExponential :: Int -> Number -> Maybe String +toExponential digits n = runFn4 _toExponential (const Nothing) Just digits n + +-- | Formats Number as String in fixed-point or exponential notation rounded +-- | to specified number of significant digits. May return `Nothing` when +-- | precision is less than 1 or greater than 21 depending on the +-- | implementation. See ECMA-262 for more information. +toPrecision :: Int -> Number -> Maybe String +toPrecision digits n = runFn4 _toPrecision (const Nothing) Just digits n + +foreign import _decodeURI :: forall a. Fn3 (String -> a) (String -> a) String a + +foreign import _encodeURI :: forall a. Fn3 (String -> a) (String -> a) String a + +foreign import _decodeURIComponent :: forall a. Fn3 (String -> a) (String -> a) String a + +foreign import _encodeURIComponent :: forall a. Fn3 (String -> a) (String -> a) String a + +-- | URI decoding. Returns `Nothing` when given a value with undecodeable +-- | escape sequences. +decodeURI :: String -> Maybe String +decodeURI s = runFn3 _decodeURI (const Nothing) Just s + +-- | URI encoding. Returns `Nothing` when given a value with unencodeable +-- | characters. +encodeURI :: String -> Maybe String +encodeURI s = runFn3 _encodeURI (const Nothing) Just s + +-- | URI component decoding. Returns `Nothing` when given a value with +-- | undecodeable escape sequences. +decodeURIComponent :: String -> Maybe String +decodeURIComponent s = runFn3 _decodeURIComponent (const Nothing) Just s + +-- | URI component encoding. Returns `Nothing` when given a value with +-- | unencodeable characters. +encodeURIComponent :: String -> Maybe String +encodeURIComponent s = runFn3 _encodeURIComponent (const Nothing) Just s diff --git a/test/Test/Main.js b/test/Test/Main.js new file mode 100644 index 0000000..be3f735 --- /dev/null +++ b/test/Test/Main.js @@ -0,0 +1 @@ +exports.unencodable = "\uDFFF"; diff --git a/test/Test/Main.purs b/test/Test/Main.purs index c058ca6..685ecb4 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -2,10 +2,25 @@ module Test.Main where import Prelude +import Data.Maybe (Maybe(..)) import Effect (Effect) import Effect.Console (log) -import Global (readFloat, readInt, isFinite, infinity, nan, isNaN) +import Global + ( readFloat + , readInt + , isFinite + , infinity + , nan + , isNaN + , toPrecision + , toExponential + , toFixed + , decodeURI + , encodeURI + , decodeURIComponent + , encodeURIComponent + ) import Global.Unsafe (unsafeToPrecision, unsafeToExponential, unsafeToFixed) import Test.Assert (assert) @@ -61,3 +76,45 @@ main = do log $ "unsafeToPrecision 6" <> (show num) <> " == \"12345.7\"" assert $ unsafeToPrecision 6 num == "12345.7" + -- note the rounding + log $ "toFixed 1" <> (show num) <> " == (Just \"12345.7\")" + assert $ toFixed 1 num == Just "12345.7" + + -- padded with zeros + log $ "toFixed 6" <> (show num) <> " == (Just \"12345.678900\")" + assert $ toFixed 6 num == Just "12345.678900" + + log $ "toExponential 4" <> (show num) <> " == (Just \"1.2346e+4\")" + assert $ toExponential 4 num == Just "1.2346e+4" + + log $ "toPrecision 3" <> (show num) <> " == (Just \"1.23e+4\")" + assert $ toPrecision 3 num == Just "1.23e+4" + + log $ "toPrecision 6" <> (show num) <> " == (Just \"12345.7\")" + assert $ toPrecision 6 num == Just "12345.7" + + log $ "decodeURI \"http://test/api?q=hello%20world\" == Just \"http://test/api?q=hello world\"" + assert $ decodeURI "http://test/api?q=hello%20world" == Just "http://test/api?q=hello world" + + log $ "decodeURI \"http://test/api?q=hello%8\" == Nothing\"" + assert $ decodeURI "http://test/api?q=hello%8" == Nothing + + log $ "encodeURI \"http://test/api?q=hello world\" == Just \"http://test/api?q=hello%20world\"" + assert $ encodeURI "http://test/api?q=hello world" == Just "http://test/api?q=hello%20world" + + log $ "encodeURI \"http://test/api?q=" <> unencodable <> "\" == Nothing" + assert $ encodeURI ("http://test/api?q=" <> unencodable) == Nothing + + log $ "decodeURIComponent \"hello%20world\" == Just \"hello world\"" + assert $ decodeURIComponent "hello%20world" == Just "hello world" + + log $ "decodeURIComponent \"hello%8\" == Nothing" + assert $ decodeURIComponent "hello%8" == Nothing + + log $ "encodeURIComponent \"hello world\" == Just \"hello%20world\"" + assert $ encodeURIComponent "hello world" == Just "hello%20world" + + log $ "encodeURIComponent \"" <> unencodable <> "\" == Nothing" + assert $ encodeURIComponent unencodable == Nothing + +foreign import unencodable :: String