on master help sign in
Make Elm.Package exports explicit, fromString => fromText
0ac5b30Evan Czaplicki
src/Elm/Package.hs+82−105
@@ -1,6 +1,18 @@
1{-# OPTIONS_GHC -Wall #-}
1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3−module Elm.Package where
3+module Elm.Package
4+ ( Name(..)
5+ , Package
6+ , dummyName, core, virtualDom, html
7+ , toString, toText, toUrl, toFilePath
8+ , fromText
9+ , Version(..)
10+ , initialVersion, dummyVersion
11+ , bumpPatch, bumpMinor, bumpMajor
12+ , filterLatest, majorAndMinor
13+ , versionToString, versionFromText
14+ )
15+ where
4
16
5import Data.Aeson
17import Data.Aeson
6import Data.Binary
18import Data.Binary
@@ -9,6 +21,7 @@ import Data.Function (on)
9import qualified Data.List as List
21import qualified Data.List as List
10import Data.Monoid ((<>))
22import Data.Monoid ((<>))
11import qualified Data.Text as Text
23import qualified Data.Text as Text
24+import qualified Data.Text.Read as Text
12import Data.Text (Text)
25import Data.Text (Text)
13import System.FilePath ((</>))
26import System.FilePath ((</>))
14
27
@@ -17,7 +30,8 @@ import System.FilePath ((</>))
17-- PACKGE NAMES
30-- PACKGE NAMES
18
31
19
32
20−data Name = Name
33+data Name =
34+ Name
21 { _user :: Text
35 { _user :: Text
22 , _project :: Text
36 , _project :: Text
23 }
37 }
@@ -67,68 +81,32 @@ toFilePath (Name user project) =
67 Text.unpack user </> Text.unpack project
81 Text.unpack user </> Text.unpack project
68
82
69
83
70−fromString :: String -> Either String Name
84+fromText :: Text -> Either String Name
71−fromString string =
85+fromText text =
72− case break (=='/') string of
86+ case Text.splitOn "/" text of
73− ( user, '/' : project ) ->
87+ [ user, project ] | not (Text.null user || Text.null project) ->
74− if null user then
88+ Name user <$> validateProjectName project
75− Left "You did not provide a user name (user/project)"
76
89
77− else if null project then
90+ _ ->
78− Left "You did not provide a project name (user/project)"
91+ Left "A valid project name looks like `user/project`"
79
92
80− else if all (/='/') project then
81− Name (Text.pack user) <$> validateProjectName project
82
93
83− else
94+validateProjectName :: Text -> Either String Text
84− Left "Expecting only one slash, separating the user and project name (user/project)"
95+validateProjectName text =
96+ if Text.isInfixOf "--" text then
97+ Left "There is a double dash -- in your package name. It must be a single dash."
85
98
86− _ ->
99+ else if Text.isInfixOf "_" text then
87− Left "There should be a slash separating the user and project name (user/project)"
88−
89−
90−whitelistedUppercaseName :: String -> Bool
91−whitelistedUppercaseName name =
92− -- These packages were uploaded to package.elm-lang.org before version 0.16,
93− -- when uppercase letters were disallowed in package names.
94− -- They should be considered deprecated and removed from this list once users
95− -- migrate to using the lowercase versions of the names.
96− List.elem name
97− [ "Easing"
98− , "LoadAssets"
99− , "elm-MultiDimArray"
100− , "elm-SafeLists"
101− , "GraphicsEngine"
102− , "Elm-Css"
103− , "Elm-Test"
104− , "DateOp"
105− , "IO"
106− , "Elm-Align-Distribute"
107− , "Elm-Format-String"
108− , "Elm-Multiset"
109− , "Elm-Random-Sampling"
110− , "ElmCache"
111− , "ExternalStorage"
112− , "IntRange"
113− ]
114−
115−
116−validateProjectName :: String -> Either String Text
117−validateProjectName str =
118− if elem ('-','-') (zip str (tail str)) then
119− Left "There is a double dash -- in your package name. It must be a single dash."
120−
121− else if elem '_' str then
122− Left "Underscores are not allowed in package names."
100+ Left "Underscores are not allowed in package names."
123−
101+
124− else if any Char.isUpper str && not (whitelistedUppercaseName str) then
102+ else if Text.any Char.isUpper text then
125− Left "Upper case characters are not allowed in package names."
103+ Left "Upper case characters are not allowed in package names."
126−
104+
127− else if not (Char.isLetter (head str)) then
105+ else if not (Char.isLetter (Text.head text)) then
128− Left "Package names must start with a letter."
106+ Left "Package names must start with a letter."
129
107
130 else
108 else
131− Right (Text.pack str)
109+ Right text
132
110
133
111
134instance Binary Name where
112instance Binary Name where
@@ -140,15 +118,14 @@ instance Binary Name where
140
118
141instance FromJSON Name where
119instance FromJSON Name where
142 parseJSON (String text) =
120 parseJSON (String text) =
143− let
121+ case fromText text of
144− string = Text.unpack text
145− in
146− case fromString string of
147− Left msg ->
122+ Left msg ->
123+ fail $
148− fail ("Ran into an invalid package name: " ++ string ++ "\n\n" ++ msg)
124+ "Ran into an invalid package name: "
125+ ++ Text.unpack text ++ "\n\n" ++ msg
149
126
150− Right name ->
127+ Right name ->
151− return name
128+ return name
152
129
153 parseJSON _ =
130 parseJSON _ =
154 fail "Project name must be a string."
131 fail "Project name must be a string."
@@ -161,7 +138,8 @@ instance ToJSON Name where
161
138
162-- PACKAGE VERSIONS
139-- PACKAGE VERSIONS
163
140
164−data Version = Version
141+data Version =
142+ Version
165 { _major :: Int
143 { _major :: Int
166 , _minor :: Int
144 , _minor :: Int
167 , _patch :: Int
145 , _patch :: Int
@@ -210,53 +188,52 @@ versionToString (Version major minor patch) =
210 show major ++ "." ++ show minor ++ "." ++ show patch
188 show major ++ "." ++ show minor ++ "." ++ show patch
211
189
212
190
213−versionFromString :: String -> Either String Version
191+versionFromText :: Text -> Either String Version
214−versionFromString string =
192+versionFromText text =
215− case splitNumbers string of
193+ let
216− Just [major, minor, patch] ->
194+ chunks =
217− Right (Version major minor patch)
195+ Text.splitOn "." text
218− _ ->
219− Left "Must have format MAJOR.MINOR.PATCH (e.g. 1.0.2)"
220− where
221− splitNumbers :: String -> Maybe [Int]
222− splitNumbers ns =
223− case span Char.isDigit ns of
224− ("", _) ->
225− Nothing
226
196
227− (numbers, []) ->
197+ toNumber txt =
198+ case Text.decimal txt of
199+ Right (n, "") ->
228− Just [ read numbers ]
200+ Just n
229
201
230− (numbers, '.':rest) ->
202+ _ ->
231− (read numbers :) <$> splitNumbers rest
203+ Nothing
204+ in
205+ case traverse toNumber chunks of
206+ Just [major, minor, patch] ->
207+ Right (Version major minor patch)
232
208
233− _ ->
209+ _ ->
234− Nothing
210+ Left "Must have format MAJOR.MINOR.PATCH (e.g. 1.0.2)"
235
211
236
212
237instance Binary Version where
213instance Binary Version where
214+ get =
238− get = Version <$> get <*> get <*> get
215+ Version <$> get <*> get <*> get
239− put (Version major minor patch) =
240− do put major
241− put minor
242− put patch
243
216
217+ put (Version major minor patch) =
218+ do put major
219+ put minor
220+ put patch
244
221
245−instance FromJSON Version where
246− parseJSON (String text) =
247− let string = Text.unpack text in
248− case versionFromString string of
249− Right v ->
250− return v
251
222
223+instance FromJSON Version where
224+ parseJSON (String text) =
225+ case versionFromText text of
226+ Right version ->
227+ return version
228+
252− Left problem ->
229+ Left problem ->
253− fail $ unlines
230+ fail $ unlines $
254− [ "Ran into an invalid version number: " ++ string
231+ [ "Ran into an invalid version number: " ++ Text.unpack text
255− , problem
232+ , problem
256− ]
233+ ]
257−
234+
258− parseJSON _ =
235+ parseJSON _ =
259− fail "Version number must be stored as a string."
236+ fail "Version number must be stored as a string."
260
237
261
238
262instance ToJSON Version where
239instance ToJSON Version where