postgresql-connection-string 0.1.0.1 → 0.1.0.2
raw patch · 6 files changed
+83/−71 lines, 6 files
Files
- README.md +0/−42
- postgresql-connection-string.cabal +2/−1
- src/library/PostgresqlConnectionString.hs +27/−15
- src/library/PostgresqlConnectionString/Charsets.hs +21/−0
- src/library/PostgresqlConnectionString/Parsers.hs +15/−12
- src/library/PostgresqlConnectionString/Types/Gens.hs +18/−1
README.md view
@@ -83,48 +83,6 @@ toParams :: ConnectionString -> Map Text Text ``` -### Transforming Connection Strings--```haskell--- Intercept and remove a parameter-case interceptParam "application_name" connStr of- Just (value, updatedConnStr) -> - -- value is the parameter value, updatedConnStr has it removed- processAppName value- Nothing -> - -- Parameter not found- useDefault-```--## Installation--Add to your `package.yaml` or `.cabal` file:--```yaml-dependencies:- - postgresql-connection-string-```--Or with cabal:--```cabal-build-depends:- postgresql-connection-string-```--## Requirements--- GHC 8.10 or later-- Standard Haskell dependencies (see cabal file)- ## Related Projects This library was extracted from the [hasql](https://github.com/nikita-volkov/hasql) project to provide a standalone connection string parser and builder that can be used independently of the full hasql ecosystem.--## License--MIT License - see LICENSE file for details.--## Contributing--Contributions are welcome! Please feel free to submit pull requests or open issues on GitHub.
postgresql-connection-string.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: postgresql-connection-string-version: 0.1.0.1+version: 0.1.0.2 category: Database, PostgreSQL synopsis: PostgreSQL connection string type, parser and builder description:@@ -99,6 +99,7 @@ PostgresqlConnectionString other-modules:+ PostgresqlConnectionString.Charsets PostgresqlConnectionString.Parsers PostgresqlConnectionString.Types PostgresqlConnectionString.Types.Gens
src/library/PostgresqlConnectionString.hs view
@@ -368,7 +368,17 @@ -- Returns 'Left' with an error message if parsing fails: -- -- >>> parse "invalid://connection"--- Left "parse error message"+-- Left ...+--+-- The error message is quite detailed (it is produced by Megaparsec):+--+-- >>> parse "invalid://connection=" & either id (const "") & Data.Text.IO.putStrLn+-- 1:8:+-- |+-- 1 | invalid://connection=+-- | ^+-- unexpected ':'+-- expecting '=' or Key parse :: Text -> Either Text ConnectionString parse input = Megaparsec.parse megaparsecOf "" input@@ -392,6 +402,11 @@ -- using the 'Semigroup' instance. -- -- When you need to specify a port, use 'hostAndPort' instead.+--+-- Examples:+--+-- >>> host "localhost"+-- "postgresql://localhost" host :: Text -> ConnectionString host hostname = ConnectionString@@ -408,13 +423,10 @@ -- -- Examples: ----- >>> toUrl (host "localhost")--- "postgresql://localhost"------ >>> toUrl (hostAndPort "localhost" 5432)+-- >>> hostAndPort "localhost" 5432 -- "postgresql://localhost:5432" ----- >>> toUrl (mconcat [hostAndPort "host1" 5432, hostAndPort "host2" 5433])+-- >>> mconcat [hostAndPort "host1" 5432, hostAndPort "host2" 5433] -- "postgresql://host1:5432,host2:5433" hostAndPort :: Text -> Word16 -> ConnectionString hostAndPort host port =@@ -429,10 +441,10 @@ -- -- Examples: ----- >>> toUrl (user "myuser")+-- >>> user "myuser" -- "postgresql://myuser@" ----- >>> toUrl (mconcat [user "myuser", host "localhost"])+-- >>> mconcat [user "myuser", host "localhost"] -- "postgresql://myuser@localhost" user :: Text -> ConnectionString user username =@@ -449,10 +461,10 @@ -- -- Examples: ----- >>> toUrl (mconcat [user "myuser", password "secret"])+-- >>> mconcat [user "myuser", password "secret"] -- "postgresql://myuser:secret@" ----- >>> toUrl (mconcat [user "myuser", password "secret", host "localhost"])+-- >>> mconcat [user "myuser", password "secret", host "localhost"] -- "postgresql://myuser:secret@localhost" password :: Text -> ConnectionString password pwd =@@ -467,10 +479,10 @@ -- -- Examples: ----- >>> toUrl (dbname "mydb")+-- >>> dbname "mydb" -- "postgresql:///mydb" ----- >>> toUrl (mconcat [host "localhost", dbname "mydb"])+-- >>> mconcat [host "localhost", dbname "mydb"] -- "postgresql://localhost/mydb" dbname :: Text -> ConnectionString dbname db =@@ -496,13 +508,13 @@ -- -- Examples: ----- >>> toUrl (param "application_name" "myapp")+-- >>> param "application_name" "myapp" -- "postgresql://?application_name=myapp" ----- >>> toUrl (mconcat [host "localhost", param "connect_timeout" "10"])+-- >>> mconcat [host "localhost", param "connect_timeout" "10"] -- "postgresql://localhost?connect_timeout=10" ----- >>> toUrl (mconcat [param "application_name" "myapp", param "connect_timeout" "10"])+-- >>> mconcat [param "application_name" "myapp", param "connect_timeout" "10"] -- "postgresql://?application_name=myapp&connect_timeout=10" param :: Text -> Text -> ConnectionString param key value =
+ src/library/PostgresqlConnectionString/Charsets.hs view
@@ -0,0 +1,21 @@+module PostgresqlConnectionString.Charsets where++import Data.CharSet+import Platform.Prelude hiding (fromList)++control :: CharSet+control = fromList ":@?/=&,"++paramControl :: CharSet+paramControl = fromList "&"++keyName :: CharSet+keyName =+ fromList+ ( mconcat+ [ ['a' .. 'z'],+ ['A' .. 'Z'],+ ['0' .. '9'],+ "_"+ ]+ )
src/library/PostgresqlConnectionString/Parsers.hs view
@@ -7,6 +7,7 @@ import qualified Data.Text as Text import qualified PercentEncoding import Platform.Prelude hiding (many, some, try)+import qualified PostgresqlConnectionString.Charsets as Charsets import PostgresqlConnectionString.Types import Text.Megaparsec import Text.Megaparsec.Char@@ -115,17 +116,23 @@ getKeyValueParam :: P (Text, Text) getKeyValueParam = do- key <- some (satisfy (\c -> c /= '=' && c /= ' '))+ key <- try do+ getKeyValueKey char '=' value <- getKeyValueParamValue- pure (fromString key, fromString value)+ pure (key, value) -getKeyValueParamValue :: P String+getKeyValueKey :: P Text+getKeyValueKey =+ takeWhile1P (Just "Key") \c -> CharSet.member c Charsets.keyName++getKeyValueParamValue :: P Text getKeyValueParamValue =+ -- TODO: Optimize to avoid intermediate String allocation asum [ do -- Quoted value- char '\''+ try (char '\'') chars <- many do asum [ do@@ -139,20 +146,16 @@ satisfy (/= '\'') ] char '\''- pure chars,+ pure (fromString chars), -- Unquoted value- some (satisfy (\c -> c /= ' ' && c /= '\n'))+ fromString <$> some (satisfy (\c -> c /= ' ' && c /= '\n')) ] getWord :: P Text-getWord = PercentEncoding.parser (flip CharSet.member controlCharset)- where- controlCharset = CharSet.fromList ":@?/=&,"+getWord = PercentEncoding.parser (flip CharSet.member Charsets.control) getParamValue :: P Text-getParamValue = PercentEncoding.parser (flip CharSet.member paramControlCharset)- where- paramControlCharset = CharSet.fromList "&"+getParamValue = PercentEncoding.parser (flip CharSet.member Charsets.paramControl) continueAfterHostspec :: Maybe Text -> Maybe Text -> [Host] -> P ConnectionString continueAfterHostspec user password hosts = do
src/library/PostgresqlConnectionString/Types/Gens.hs view
@@ -54,6 +54,23 @@ pure (Map.fromList pairs) where genParamPair = do- key <- genSafeText (size `div` 2)+ key <- genKey (size `div` 2) value <- genSafeText (size `div` 2) pure (key, value)++genKey :: Int -> Gen Text+genKey size = do+ len <- chooseInt (0, max 1 (size `div` 2))+ head <- elements (['a' .. 'z'] <> ['A' .. 'Z'] <> ['_'])+ tail <- vectorOf len do+ elements (['a' .. 'z'] <> ['A' .. 'Z'] <> ['0' .. '9'] <> ['_'])+ pure (fromString (head : tail))++-- | Generate text with safe characters (letters, numbers, basic punctuation)+genValue :: Int -> Gen Text+genValue size = do+ len <- chooseInt (0, max 1 (size `div` 2))+ chars <- vectorOf len genSafeChar+ pure (fromString chars)+ where+ genSafeChar = elements (['a' .. 'z'] <> ['A' .. 'Z'] <> ['0' .. '9'] <> ['_', '-', '.', ' '])