@@ -4,44 +4,34 @@ module Elm.Package
5 , Canonical(..)
5 , Canonical(..)
7− , toString, toText, toUrl, toFilePath
10+ , toFilePath
8− , fromText
11+ , BadName(..)
9 , dummyName, kernel, core
13 , dummyName, kernel, core
10 , browser, virtualDom, html
14 , browser, virtualDom, html
11 , json, http, url
15 , json, http, url
12 , webgl, linearAlgebra
16 , webgl, linearAlgebra
13 , suggestions
17 , suggestions
15− , initialVersion, dummyVersion
16− , bumpPatch, bumpMinor, bumpMajor
17− , filterLatest, majorAndMinor
18− , versionToString, versionToText, versionFromText
27−import Control.Monad (liftM, liftM2, liftM3)
24+import Control.Monad (liftM2)
28−import Data.Binary (Binary, get, getWord8, put, putWord8)
25+import Data.Binary (Binary, get, put)
29import qualified Data.Char as Char
26import qualified Data.Char as Char
30−import Data.Function (on)
31−import qualified Data.List as List
32import qualified Data.Name as Name
27import qualified Data.Name as Name
33import qualified Data.Map as Map
28import qualified Data.Map as Map
34import Data.Monoid ((<>))
29import Data.Monoid ((<>))
35−import qualified Data.Text as Text
30+import qualified Data.Utf8 as Utf8
36−import qualified Data.Text.Read as Text
37−import qualified Data.Text.Lazy as B (toStrict)
38−import qualified Data.Text.Lazy.Builder as B (singleton, toLazyText)
39−import qualified Data.Text.Lazy.Builder.Int as B (decimal)
40−import Data.Text (Text)
41−import Data.Word (Word16)
42import System.FilePath ((</>))
31import System.FilePath ((</>))
33+import qualified Elm.Version as V
44−import qualified Json.Decode.Internals as Decode
34+import qualified Json.Decode as Decode
45import qualified Json.Encode as Encode
35import qualified Json.Encode as Encode
@@ -60,7 +50,7 @@ data Name =
60data Canonical =
50data Canonical =
62 { _name :: !Name
52 { _name :: !Name
63− , _version :: !Version
53+ , _version :: !V.Version
65 deriving (Eq, Ord)
55 deriving (Eq, Ord)
@@ -74,96 +64,54 @@ isKernel (Name author _) =
74 author == "elm" || author == "elm-explorations"
64 author == "elm" || author == "elm-explorations"
77−toString :: Name -> String
67+toString :: Name -> Utf8.String
78toString (Name author project) =
68toString (Name author project) =
79− Name.toString author <> "/" <> Name.toString project
69+ Utf8.join 0x2F [ Name.toUtf8 author, Name.toUtf8 project ]
82−toText :: Name -> Text
72+toChars :: Name -> String
83−toText name =
73+toChars (Name author project) =
84− Text.pack (toString name)
74+ Name.toChars author <> "/" <> Name.toChars project
87toUrl :: Name -> String
77toUrl :: Name -> String
88toUrl (Name author project) =
78toUrl (Name author project) =
89− Name.toString author ++ "/" ++ Name.toString project
79+ Name.toChars author ++ "/" ++ Name.toChars project
92toFilePath :: Name -> FilePath
82toFilePath :: Name -> FilePath
93toFilePath (Name author project) =
83toFilePath (Name author project) =
94− Name.toString author </> Name.toString project
84+ Name.toChars author </> Name.toChars project
97−fromText :: Text -> Either (String, [String]) Name
87+fromString :: Utf8.String -> Either BadName Name
98−fromText text =
88+fromString str =
99− case Text.splitOn "/" text of
89+ case Utf8.split 0x2F {- / -} str of
100− [ author, project ] | not (Text.null author || Text.null project) ->
90+ [author, project]
101− Name (Name.fromString (Text.unpack author)) <$> validateProjectName project
91+ | Utf8.isEmpty author -> Left BadStructure
92+ | Utf8.isEmpty project -> Left BadStructure
93+ | Utf8.size project >= 256 -> Left TooLong
94+ | Utf8.containsDouble 0x2D {- - -} project -> Left HasDoubleDash
95+ | Utf8.contains 0x5F {- _ -} project -> Left HasUnderscore
96+ | Utf8.contains 0x2E {- . -} project -> Left HasDot
97+ | Utf8.any Char.isUpper project -> Left HasUppercase
98+ | Utf8.endsWithWord 0x2D {- - -} project -> Left HasDashEnd
99+ | Utf8.startsWithChar Char.isLower project -> Right (Name (Name.fromUtf8 author) (Name.fromUtf8 project))
100+ | True -> Left HasNonLowercaseStart
104− Left
103+ Left BadStructure
105− ( "A valid package name looks like \"author/project\" requiring the author and project name together."
110−validateProjectName :: Text -> Either (String, [String]) Name.Name
111−validateProjectName text =
112− if Text.isInfixOf "--" text then
113− Left $ thisCannot "have two dashes in a row" text (Text.replace "--" "-" text)
115− else if Text.isInfixOf "_" text then
116− Left $ thisCannot "have underscores" text (Text.replace "_" "-" text)
118− else if Text.isInfixOf "." text then
119− Left $ thisCannot "have dots" text (Text.replace "." "-" text)
121− else if Text.any Char.isUpper text then
122− Left $ thisCannot "have upper case letters" text (decap text)
124− else if not (Char.isLetter (Text.head text)) then
126− ( "The project name \"" ++ Text.unpack text ++ "\" must start with a letter. Is there another name that captures what your package is for?"
130− else if Text.isSuffixOf "-" text then
131− Left $ thisCannot "end with a dash" text (Text.dropEnd 1 text)
133− else
106+data BadName
134− Right (Name.fromString (Text.unpack text))
107+ = BadStructure
137−thisCannot :: String -> Text -> Text -> (String, [String])
110+ | HasUnderscore
138−thisCannot beLikeThis problemName suggestedName =
111+ | HasDot
139− let name = Text.unpack suggestedName in
112+ | HasUppercase
141− "The project name \"" ++ Text.unpack problemName ++ "\" cannot "
114+ | HasNonLowercaseStart
142− ++ beLikeThis ++ ". Try something like \"" ++ name ++ "\" instead?"
148−decap :: Text -> Text
152− uncurry (:) (Text.foldr gatherCaps ([],[]) text)
155− if any (\c -> length c == 1) chunks then
158− List.intercalate "-" chunks
161−gatherCaps :: Char -> (String, [String]) -> (String, [String])
162−gatherCaps char (buffer, chunks) =
163− if Char.isUpper char then
164− ( [], (Char.toLower char : buffer) : chunks )
166− (char:buffer, chunks)
@@ -254,99 +202,6 @@ suggestions =
262− { _major :: {-# UNPACK #-} !Word16
263− , _minor :: {-# UNPACK #-} !Word16
264− , _patch :: {-# UNPACK #-} !Word16
269−initialVersion :: Version
273−dummyVersion :: Version
278−bumpPatch :: Version -> Version
279−bumpPatch (Version major minor patch) =
280− Version major minor (patch + 1)
283−bumpMinor :: Version -> Version
284−bumpMinor (Version major minor _patch) =
285− Version major (minor + 1) 0
288−bumpMajor :: Version -> Version
289−bumpMajor (Version major _minor _patch) =
290− Version (major + 1) 0 0
297−filterLatest :: (Ord a) => (Version -> a) -> [Version] -> [Version]
298−filterLatest characteristic versions =
299− map last (List.groupBy ((==) `on` characteristic) (List.sort versions))
302−majorAndMinor :: Version -> ( Int, Int )
303−majorAndMinor (Version major minor _patch) =
304− ( fromIntegral major, fromIntegral minor )
311−versionToString :: Version -> String
312−versionToString version =
313− Text.unpack (versionToText version)
316−versionToText :: Version -> Text
317−versionToText (Version major minor patch) =
318− B.toStrict $ B.toLazyText $
326−versionFromText :: Text -> Maybe Version
327−versionFromText text =
328− case Text.splitOn "." text of
329− [major, minor, patch] ->
339−toNumber :: Text -> Maybe Word16
341− case Text.decimal txt of
@@ -368,61 +223,21 @@ instance Binary Canonical where
368 put version
223 put version
371−instance Binary Version where
375− then liftM3 Version get get get
377− do minor <- liftM fromIntegral getWord8
378− patch <- liftM fromIntegral getWord8
379− return (Version (fromIntegral word) minor patch)
381− put (Version major minor patch) =
382− if major < 256 && minor < 256 && patch < 256 then
383− do putWord8 (fromIntegral major)
384− putWord8 (fromIntegral minor)
385− putWord8 (fromIntegral patch)
397−decoder :: Decode.Decoder String Name
230+decoder :: Decode.Decoder BadName Name
399− do txt <- Decode.text
232+ do str <- Decode.string
400− case fromText txt of
233+ case fromString str of
401 Right name ->
234 Right name ->
402− Decode.succeed name
235+ return name
409encode :: Name -> Encode.Value
241encode :: Name -> Encode.Value
410encode name =
242encode name =
411− Encode.text (toText name)
243+ Encode.string (toString name)
415−versionDecoder :: Decode.Decoder Text Version
417− do txt <- Decode.text
418− case versionFromText txt of
420− Decode.succeed version
426−encodeVersion :: Version -> Encode.Value
427−encodeVersion version =
428− Encode.text (versionToText version)