packages feed

connection-string (empty) → 0.1.0.0

raw patch · 6 files changed

+250/−0 lines, 6 filesdep +basedep +containersdep +doctestsetup-changed

Dependencies added: base, containers, doctest, megaparsec, parser-combinators, text

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright George Pollard (c) 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of George Pollard nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,7 @@+# connection-string++Easy connection string parsing for Haskell.++[![Travis build](https://img.shields.io/travis/Porges/connection-string-hs.svg)](https://travis-ci.org/Porges/connection-string-hs) [![Hackage](https://img.shields.io/hackage/v/connection-string.svg)](https://hackage.haskell.org/package/connection-string)++[See the documentation on Hackage.](https://hackage.haskell.org/package/connection-string)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ connection-string.cabal view
@@ -0,0 +1,47 @@+name:                connection-string+version:             0.1.0.0+synopsis:            A library for parsing connection strings.+description:+    Parses connection strings in a manner that agrees with+    the format that .NET accepts (e.g. ADO.NET).+homepage:            https://github.com/Porges/connection-string+license:             BSD3+license-file:        LICENSE+author:              George Pollard <porges@porg.es>+maintainer:          George Pollard <porges@porg.es>+copyright:           2018 George Pollard+category:            Text+build-type:          Simple+cabal-version:       >=1.10+extra-source-files:  README.md++source-repository head+    type: git+    location: git://github.com/Porges/connection-string-hs.git++source-repository this+    type: git+    location: git://github.com/Porges/connection-string-hs.git+    tag: v0.1.0.0++library+  exposed-modules:   Data.ConnectionString+  hs-source-dirs:    src+  ghc-options:       -Wall+  default-language:  Haskell2010+  build-depends:     base >= 4.7 && < 5+                   , containers+                   , megaparsec+                   , parser-combinators++test-suite doctests+  type: exitcode-stdio-1.0+  ghc-options: -threaded+  default-language: Haskell2010+  hs-source-dirs: tests+  main-is: doctests.hs+  build-depends:+    base >= 4 && < 5,+    doctest >= 0.8 && < 0.15,+    text+
+ src/Data/ConnectionString.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE GADTs #-}++{-|+Module: Data.ConnectionString++== Introduction++This module is intended for parsing connection strings in a manner+that is consistent with .NET's <https://docs.microsoft.com/en/dotnet/api/system.data.common.dbconnectionstringbuilder DbConnectionStringBuilder>+class.++The syntax of a connection string appears quite simple at first glance,+and consists of a list of key-value pairs separated by semicolons:++>>> parse "key=value; key2 = value2"+Right (fromList [("key","value"),("key2","value2")])++However, the format can be more complicated than expected.++== Examples++A value may be single-quoted (single quotes can be escaped+inside a single-quoted string by doubling them):++>>> parse "squote='value with '' quotes'"+Right (fromList [("squote","value with ' quotes")])++Or double-quoted (double quotes can also be escaped inside +a double-quoted string by doubling them):++>>> parse "dquote=\"value with \"\" quotes\""+Right (fromList [("dquote","value with \" quotes")])++Quotes of both kinds may be present in keys:++>>> parse "'quote\"=value"+Right (fromList [("'quote\"","value")])++Whitespace is ignored everywhere except in quoted strings and inside keys or unquoted values:++>>> parse "; a key = v v\t\n;\t key 2 = \"v v\"\n;\t key 3 = 'v v'; "+Right (fromList [("a key","v v"),("key 2","v v"),("key 3","v v")])++Equals signs may be escaped in keys by doubling them:++>>> parse "1==2=false"+Right (fromList [("1=2","false")])++Later values override earlier ones:++>>> parse "key=value;key=value2"+Right (fromList [("key","value2")])++Assigning a key no value will remove it:++>>> parse "key=value;key="+Right (fromList [])++However, you can assign an empty value by giving it a quoted value:++>>> parse "key=value;key=''"+Right (fromList [("key","")])++On the other hand, not providing a key doesn't make any sense:++>>> parse "key=value;=value"+Left "1:11:\nunexpected '='\nexpecting ';', end of input, or white space\n"++>>> parse "=value"+Left "1:1:\nunexpected '='\nexpecting ';', end of input, or white space\n"++This module implements all of these quirks for you!++-}+module Data.ConnectionString+    ( ConnectionString+    , parse+    , Parseable+    , parser+    )+where++import Data.Char (isSpace)+import Data.List (dropWhileEnd)+import qualified Data.Map as Map+import Data.String (IsString(..))+import Data.Void (Void)++import Control.Applicative.Combinators (many, sepEndBy, skipMany, skipSome, some, (<|>))++import qualified Text.Megaparsec as P+import Text.Megaparsec (Parsec, try, Stream, Token, (<?>))+import Text.Megaparsec.Char (char, notChar, space)++-- | A connection string is a set of keys and values.+type ConnectionString s = Map.Map s s++-- | Parses a connection string, or fails with an error.+--+-- You can parse 'String' inputs:+--+-- >>> parse ("key=value;key2=value2")+-- Right (fromList [("key","value"),("key2","value2")])+--+-- Or you can parse 'Data.Text.Text' inputs:+--+-- >>> :set -XOverloadedStrings+-- >>> import Data.Text+-- >>> parse ("key=value;key2=value2" :: Text)+-- Right (fromList [("key","value"),("key2","value2")])+--+-- In either case, 'parse' will produce a 'ConnectionString' that+-- has values of the same type as the input.+parse :: Parseable s => s -> Either String (ConnectionString s)+parse cs =+    case (P.parse (parser <* P.eof) "" cs) of+        Left err -> Left (P.parseErrorPretty err)+        Right kvs -> Right (Map.mapMaybe id (Map.fromList kvs))++-- | The constraints on things that 'parse' can handle.+-- (Essentially this means "either 'String' or 'Data.Text.Text'".)+type Parseable s = (Stream s, Token s ~ Char, IsString s, Ord s)++-- | A reusable ("Text.Megaparsec") parser for connection strings.+--+-- A 'Nothing' in the output list indicates that the corresponding+-- key should be deleted from the set.+parser :: Parseable s => Parsec Void s [(s, Maybe s)]+parser = skipMany semiColon *> keyValue `sepEndBy` (skipSome semiColon)+    where+    semiColon = P.between space space (char ';')++keyValue :: Parseable s => Parsec Void s (s, Maybe s)+keyValue = (,) <$> (key <?> "key") <* equals <*> (value <?> "value")+    where+    equals = P.between space space (char '=')++    key =+        fromString . dropWhileEnd isSpace <$>+        some (notChar '=' <|> try (char '=' *> char '='))+            +    value = fmap fromString <$> (sQuoted <|> dQuoted <|> unQuoted)++    sQuoted =+        Just <$>+        P.between (char '\'') (char '\'') (many (notChar '\'' <|> try (char '\'' *> char '\'')))++    dQuoted =+        Just <$>+        P.between (char '"') (char '"') (many (notChar '"' <|> try (char '"' *> char '"')))++    unQuoted =+        -- a completely empty value indicates we should remove it (see docs+        -- above), so we return Nothing in this case+        (\s -> if null s then Nothing else Just s) . dropWhileEnd isSpace <$>+        (space *> many (notChar ';') <* space)+
+ tests/doctests.hs view
@@ -0,0 +1,6 @@+import Test.DocTest
+
+main = doctest
+    [ "-isrc"
+    , "src/Data/ConnectionString.hs"
+    ]