packages feed

casing 0.1.2.1 → 0.1.3.0

raw patch · 4 files changed

+447/−166 lines, 4 filesdep +casingdep +tastydep +tasty-hunitPVP ok

version bump matches the API change (PVP)

Dependencies added: casing, tasty, tasty-hunit

API changes (from Hackage documentation)

+ Text.Casing: instance GHC.Classes.Eq a => GHC.Classes.Eq (Text.Casing.Identifier a)

Files

− Text/Casing.hs
@@ -1,144 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE DeriveTraversable #-}--- | Conversions between several common identifier casing conventions:------ - @PascalCase@ - no spacing between words, first letter in word is--- uppercase, all others are lowercase.--- - @camelCase@ - like @PascalCase@, but the very first letter is lowercase.--- - @kebab-case@ - everything lowercase, dash delimits words.--- - @snake_Case@ - underscores delimit words, case is unrestricted.--- - @quiet_snake_case@ - underscores delimit words, everything lowercase.--- - @SCREAMING_SNAKE_CASE@ - underscores delimit words, everything uppercase.-module Text.Casing-(--- * Types-Identifier (..)--- * Parsing-, fromHumps-, fromKebab-, fromSnake-, fromWords-, fromAny--- * Generating-, toCamel-, toPascal-, toSnake-, toQuietSnake-, toScreamingSnake-, toKebab-, toWords--- * Shorthand functions-, pascal-, camel-, snake-, quietSnake-, screamingSnake-, kebab-, wordify--- * Miscellaneous-, dropPrefix-)-where--import Data.Char-import Data.List (intersperse)-import Data.List.Split (wordsBy)-import Control.Applicative---- | An opaque type that represents a parsed identifier.-newtype Identifier a = Identifier { unIdentifier :: [a] }-    deriving (Monad, Functor, Applicative, Show, Foldable, Traversable)--wordCase :: String -> String-wordCase "" = ""-wordCase (x:xs) = toUpper x : map toLower xs---- | Convert from "humped" casing (@camelCase@ or @PascalCase@)-fromHumps :: String -> Identifier String-fromHumps = Identifier . go-    where-        go "" = [""]-        go (x:[]) = [x:[]]-        go (x:y:xs)-            | (not $ isUpper x) && (isUpper y) =-                let (z:zs) = go (y:xs)-                in [x]:(z:zs)-            | otherwise =-                let (z:zs) = go (y:xs)-                in (x:z):zs--fromWords :: String -> Identifier String-fromWords = Identifier . words---- | Convert from @kebab-cased-identifiers@-fromKebab :: String -> Identifier String-fromKebab = Identifier . wordsBy (== '-')---- | Convert from @snake_cased@ (either flavor)-fromSnake :: String -> Identifier String-fromSnake = Identifier . wordsBy (== '_')---- | Convert from anything, including mixed casing.-fromAny :: String -> Identifier String-fromAny str = fromHumps str >>= fromKebab >>= fromSnake >>= fromWords---- | To @PascalCase@-toPascal :: Identifier String -> String-toPascal = concat . map wordCase . unIdentifier---- | To @camelCase@-toCamel :: Identifier String -> String-toCamel (Identifier (x:xs)) = concat $ map toLower x:map wordCase xs---- | To @kebab-case@-toKebab :: Identifier String -> String-toKebab = concat . intersperse "-" . map (map toLower) . unIdentifier---- | To @snake_Case@-toSnake :: Identifier String -> String-toSnake = concat . intersperse "_" . unIdentifier---- | To @quiet_snake_case@-toQuietSnake :: Identifier String -> String-toQuietSnake = map toLower . toSnake---- | To @SCREAMING_SNAKE_CASE@-toScreamingSnake :: Identifier String -> String-toScreamingSnake = map toUpper . toSnake---- | To @word Case@-toWords :: Identifier String -> String-toWords = unwords . unIdentifier---- | Directly convert to @PascalCase@ through 'fromAny'-pascal :: String -> String-pascal = toPascal . fromAny---- | Directly convert to @camelCase@ through 'fromAny'-camel :: String -> String-camel = toCamel . fromAny---- | Directly convert to @snake_Case@ through 'fromAny'-snake :: String -> String-snake = toSnake . fromAny---- | Directly convert to @quiet_snake_case@ through 'fromAny'-quietSnake :: String -> String-quietSnake = toQuietSnake . fromAny---- | Directly convert to @SCREAMING_SNAKE_CASE@ through 'fromAny'-screamingSnake :: String -> String-screamingSnake = toScreamingSnake . fromAny---- | Directly convert to @kebab-case@ through 'fromAny'-kebab :: String -> String-kebab = toKebab . fromAny---- | Directly convert to @word Case@ through 'fromAny'-wordify :: String -> String-wordify = toWords . fromAny---- | Drop the first word from a parsed identifier. Typical usage is between--- parsing and writing, e.g.: @toKebab . dropPrefix . fromAny $ "strHelloWorld" == "hello-world"@-dropPrefix :: Identifier String -> Identifier String-dropPrefix = Identifier . drop 1 . unIdentifier
casing.cabal view
@@ -1,26 +1,40 @@--- Initial casing.cabal generated by cabal init.  For further +-- Initial casing.cabal generated by cabal init. For further  -- documentation, see http://haskell.org/cabal/users-guide/ -name:                casing-version:             0.1.2.1-synopsis:            Convert between various source code casing conventions-description:         Converts between camelCase, PascalCase, kebab-case, and-                     three flavors of snake_case.-license:             MIT-license-file:        LICENSE-author:              Tobias Dammers-maintainer:          tdammers@gmail.com--- copyright:           -category:            Text-build-type:          Simple--- extra-source-files:  -cabal-version:       >=1.10+name: casing+version: 0.1.3.0+synopsis: Convert between various source code casing conventions+description: Converts between camelCase, PascalCase, kebab-case, and three+             flavors of snake_case.+license: MIT+license-file: LICENSE+author: Tobias Dammers+maintainer: tdammers@gmail.com+-- copyright: +category: Text+build-type: Simple+-- extra-source-files: +cabal-version: >=1.10 +source-repository head+    type: git+    location: https://bitbucket.org/tdammers/casing+ library-  exposed-modules:     Text.Casing-  -- other-modules:       -  -- other-extensions:    -  build-depends:       base >=4.8 && <5-               ,       split-  -- hs-source-dirs:      -  default-language:    Haskell2010+  exposed-modules: Text.Casing+  -- other-modules: +  -- other-extensions: +  build-depends: base >=4.8 && <5+               , split+  hs-source-dirs: src+  default-language: Haskell2010++test-suite tests+  main-is: Spec.hs+  hs-source-dirs: tests+  type: exitcode-stdio-1.0+  default-language: Haskell2010+  build-depends: base >=4.8 && <5+               , casing+               , tasty+               , tasty-hunit
+ src/Text/Casing.hs view
@@ -0,0 +1,161 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveTraversable #-}+-- | Conversions between several common identifier casing conventions:+--+-- - @PascalCase@ - no spacing between words, first letter in word is+-- uppercase, all others are lowercase.+-- - @camelCase@ - like @PascalCase@, but the very first letter is lowercase.+-- - @kebab-case@ - everything lowercase, dash delimits words.+-- - @snake_Case@ - underscores delimit words, case is unrestricted.+-- - @quiet_snake_case@ - underscores delimit words, everything lowercase.+-- - @SCREAMING_SNAKE_CASE@ - underscores delimit words, everything uppercase.+module Text.Casing+(+-- * Types+Identifier (..)+-- * Parsing+, fromHumps+, fromKebab+, fromSnake+, fromWords+, fromAny+-- * Generating+, toCamel+, toPascal+, toSnake+, toQuietSnake+, toScreamingSnake+, toKebab+, toWords+-- * Shorthand functions+, pascal+, camel+, snake+, quietSnake+, screamingSnake+, kebab+, wordify+-- * Miscellaneous+, dropPrefix+)+where++import Data.Char+import Data.List (intersperse)+import Data.List.Split (wordsBy)+import Control.Applicative++-- | An opaque type that represents a parsed identifier.+newtype Identifier a = Identifier { unIdentifier :: [a] }+    deriving (Monad, Functor, Applicative, Show, Foldable, Traversable, Eq)++wordCase :: String -> String+wordCase "" = ""+wordCase (x:xs) = toUpper x : map toLower xs++-- | Convert from "humped" casing (@camelCase@ or @PascalCase@)+fromHumps :: String -> Identifier String+fromHumps = Identifier . go+    where+        go "" = [""]+        go (x:[]) = [x:[]]+        go xxs@(x:xs)+          | isUpper x =+              case xs of+                "" ->+                  [[x]]+                (y:_) ->+                  if isUpper y then+                    [x]:go xs+                  else+                    let cur = x:takeWhile (not . isUpper) xs+                        rem = dropWhile (not . isUpper) xs+                    in +                    if null rem then+                      [cur]+                    else+                      cur:go rem+          | otherwise =+              let cur = takeWhile (not . isUpper) xxs+                  rem = dropWhile (not . isUpper) xxs+              in+              if null rem then+                [cur]+              else+                cur:go rem++fromWords :: String -> Identifier String+fromWords = Identifier . words++-- | Convert from @kebab-cased-identifiers@+fromKebab :: String -> Identifier String+fromKebab = Identifier . wordsBy (== '-')++-- | Convert from @snake_cased@ (either flavor)+fromSnake :: String -> Identifier String+fromSnake = Identifier . wordsBy (== '_')++-- | Convert from anything, including mixed casing.+fromAny :: String -> Identifier String+fromAny str = fromHumps str >>= fromKebab >>= fromSnake >>= fromWords++-- | To @PascalCase@+toPascal :: Identifier String -> String+toPascal = concat . map wordCase . unIdentifier++-- | To @camelCase@+toCamel :: Identifier String -> String+toCamel (Identifier (x:xs)) = concat $ map toLower x:map wordCase xs++-- | To @kebab-case@+toKebab :: Identifier String -> String+toKebab = concat . intersperse "-" . map (map toLower) . unIdentifier++-- | To @snake_Case@+toSnake :: Identifier String -> String+toSnake = concat . intersperse "_" . unIdentifier++-- | To @quiet_snake_case@+toQuietSnake :: Identifier String -> String+toQuietSnake = map toLower . toSnake++-- | To @SCREAMING_SNAKE_CASE@+toScreamingSnake :: Identifier String -> String+toScreamingSnake = map toUpper . toSnake++-- | To @word Case@+toWords :: Identifier String -> String+toWords = unwords . unIdentifier++-- | Directly convert to @PascalCase@ through 'fromAny'+pascal :: String -> String+pascal = toPascal . fromAny++-- | Directly convert to @camelCase@ through 'fromAny'+camel :: String -> String+camel = toCamel . fromAny++-- | Directly convert to @snake_Case@ through 'fromAny'+snake :: String -> String+snake = toSnake . fromAny++-- | Directly convert to @quiet_snake_case@ through 'fromAny'+quietSnake :: String -> String+quietSnake = toQuietSnake . fromAny++-- | Directly convert to @SCREAMING_SNAKE_CASE@ through 'fromAny'+screamingSnake :: String -> String+screamingSnake = toScreamingSnake . fromAny++-- | Directly convert to @kebab-case@ through 'fromAny'+kebab :: String -> String+kebab = toKebab . fromAny++-- | Directly convert to @word Case@ through 'fromAny'+wordify :: String -> String+wordify = toWords . fromAny++-- | Drop the first word from a parsed identifier. Typical usage is between+-- parsing and writing, e.g.: @toKebab . dropPrefix . fromAny $ "strHelloWorld" == "hello-world"@+dropPrefix :: Identifier String -> Identifier String+dropPrefix = Identifier . drop 1 . unIdentifier
+ tests/Spec.hs view
@@ -0,0 +1,250 @@+module Main where++import Test.Tasty+import Test.Tasty.HUnit+import Text.Casing++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "tests"+  [ testGroup "parsing"+    [ testGroup "fromHumps"+      [ testCase "no splits 1" $ do+          assertEqual ""+            (Identifier ["hello"])+            (fromHumps "hello")+      , testCase "no splits 2" $ do+          assertEqual ""+            (Identifier ["Hello"])+            (fromHumps "Hello")+      , testCase "non-alpha, no split" $ do+          assertEqual ""+            (Identifier ["-hello"])+            (fromHumps "-hello")+      , testCase "non-alpha, split" $ do+          assertEqual ""+            (Identifier ["-hello-", "World"])+            (fromHumps "-hello-World")+      , testCase "simple split camel" $ do+          assertEqual ""+            (Identifier ["hello", "World"])+            (fromHumps "helloWorld")+      , testCase "simple split pascal" $ do+          assertEqual ""+            (Identifier ["Hello", "World"])+            (fromHumps "HelloWorld")+      , testCase "single letter abbrev split" $ do+          assertEqual ""+            (Identifier ["Hello", "A", "World"])+            (fromHumps "HelloAWorld")+      , testCase "multi letter abbrev split" $ do+          assertEqual ""+            (Identifier ["Hello", "X", "M", "L", "World"])+            (fromHumps "HelloXMLWorld")+      , testCase "single letter upper" $ do+          assertEqual ""+            (Identifier ["A"])+            (fromHumps "A")+      , testCase "single letter lower" $ do+          assertEqual ""+            (Identifier ["a"])+            (fromHumps "a")+      ]+    , testGroup "fromKebab"+      [ testCase "no splits" $ do+          assertEqual ""+            (Identifier ["hello"])+            (fromKebab "hello")+      , testCase "single split" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromKebab "hello-world")+      , testCase "leading split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromKebab "-world")+      , testCase "trailing split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromKebab "world-")+      , testCase "multiple dashes" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromKebab "hello---world")+      ]+    , testGroup "fromSnake"+      [ testCase "no splits" $ do+          assertEqual ""+            (Identifier ["hello"])+            (fromSnake "hello")+      , testCase "single split" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromSnake "hello_world")+      , testCase "leading split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromSnake "_world")+      , testCase "trailing split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromSnake "world_")+      , testCase "multiple dashes" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromSnake "hello___world")+      ]+    , testGroup "fromWords"+      [ testCase "no splits" $ do+          assertEqual ""+            (Identifier ["hello"])+            (fromWords "hello")+      , testCase "single split" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromWords "hello world")+      , testCase "leading split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromWords " world")+      , testCase "trailing split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromWords "world ")+      , testCase "multiple dashes" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromWords "hello   world")+      ]+    , testGroup "fromAny"+      [ testCase "no splits 1" $ do+          assertEqual ""+            (Identifier ["hello"])+            (fromAny "hello")+      , testCase "no splits 2" $ do+          assertEqual ""+            (Identifier ["Hello"])+            (fromAny "Hello")+      , testCase "simple split camel" $ do+          assertEqual ""+            (Identifier ["hello", "World"])+            (fromAny "helloWorld")+      , testCase "simple split pascal" $ do+          assertEqual ""+            (Identifier ["Hello", "World"])+            (fromAny "HelloWorld")+      , testCase "single letter abbrev split" $ do+          assertEqual ""+            (Identifier ["Hello", "A", "World"])+            (fromAny "HelloAWorld")+      , testCase "multi letter abbrev split" $ do+          assertEqual ""+            (Identifier ["Hello", "X", "M", "L", "World"])+            (fromAny "HelloXMLWorld")+      , testCase "single letter upper" $ do+          assertEqual ""+            (Identifier ["A"])+            (fromAny "A")+      , testCase "single letter lower" $ do+          assertEqual ""+            (Identifier ["a"])+            (fromAny "a")+      , testCase "no splits" $ do+          assertEqual ""+            (Identifier ["hello"])+            (fromAny "hello")+      , testCase "single split" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromAny "hello-world")+      , testCase "leading split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromAny "-world")+      , testCase "trailing split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromAny "world-")+      , testCase "multiple dashes" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromAny "hello---world")+      , testCase "no splits" $ do+          assertEqual ""+            (Identifier ["hello"])+            (fromAny "hello")+      , testCase "single split" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromAny "hello_world")+      , testCase "leading split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromAny "_world")+      , testCase "trailing split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromAny "world_")+      , testCase "multiple dashes" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromAny "hello___world")+      , testCase "no splits" $ do+          assertEqual ""+            (Identifier ["hello"])+            (fromAny "hello")+      , testCase "single split" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromAny "hello world")+      , testCase "leading split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromAny " world")+      , testCase "trailing split" $ do+          assertEqual ""+            (Identifier ["world"])+            (fromAny "world ")+      , testCase "multiple dashes" $ do+          assertEqual ""+            (Identifier ["hello", "world"])+            (fromAny "hello   world")+      ]+    ]+  , testGroup "writing"+    [ testGroup "toPascal"+      [ testCase "toPascal simple" $ do+          assertEqual ""+            "HelloWorld"+            (toPascal $ Identifier ["hello", "world"])+      ]+    , testGroup "toCamel"+      [ testCase "toCamel simple" $ do+          assertEqual ""+            "helloWorld"+            (toCamel $ Identifier ["hello", "world"])+      ]+    , testGroup "toKebab"+      [ testCase "toKebab simple" $ do+          assertEqual ""+            "hello-world"+            (toKebab $ Identifier ["hello", "world"])+      ]+    , testGroup "toSnake"+      [ testCase "toSnake simple" $ do+          assertEqual ""+            "hello_world"+            (toSnake $ Identifier ["hello", "world"])+      , testCase "toScreamingSnake simple" $ do+          assertEqual ""+            "HELLO_WORLD"+            (toScreamingSnake $ Identifier ["hello", "world"])+      , testCase "toQuietSnake simple" $ do+          assertEqual ""+            "hello_world"+            (toQuietSnake $ Identifier ["Hello", "World"])+      ]+    ]+  ]