basex-client (empty) → 0.1.0.0
raw patch · 12 files changed
+281/−0 lines, 12 filesdep +basedep +networkdep +pureMD5setup-changed
Dependencies added: base, network, pureMD5, utf8-string
Files
- LICENSE +20/−0
- Setup.hs +2/−0
- basex-client.cabal +24/−0
- src/AddExample.hs +11/−0
- src/BaseXClient.hs +22/−0
- src/BaseXClient/Query.hs +41/−0
- src/BaseXClient/Session.hs +61/−0
- src/BaseXClient/Utils.hs +55/−0
- src/CreateExample.hs +11/−0
- src/Example.hs +14/−0
- src/QueryBindExample.hs +12/−0
- src/QueryExample.hs +8/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Matthijs Steen++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ basex-client.cabal view
@@ -0,0 +1,24 @@+-- Initial basex-client.cabal generated by cabal init. For further+-- documentation, see http://haskell.org/cabal/users-guide/++name: basex-client+version: 0.1.0.0+synopsis: A BaseX client for Haskell+description: A BaseX client library for Haskell that connects to the BaseX server using a socket. BaseX is a light-weight, high-performance and scalable XML Database written in Java.+license: MIT+license-file: LICENSE+author: Matthijs Steen+maintainer: matthijssteen1990@gmail.com+-- copyright:+category: Database+build-type: Simple+-- extra-source-files:+cabal-version: >=1.10++library+ exposed-modules: Example, AddExample, BaseXClient, CreateExample, QueryBindExample, QueryExample, BaseXClient.Session, BaseXClient.Query, BaseXClient.Utils+ -- other-modules:+ other-extensions: NamedFieldPuns, LambdaCase+ build-depends: base >=4 && <5, network >=2.5 && <3, pureMD5 >=2 && <3, utf8-string >=1 && <2+ hs-source-dirs: src+ default-language: Haskell2010
+ src/AddExample.hs view
@@ -0,0 +1,11 @@+module AddExample where++import BaseXClient++main :: IO ()+main = withSession "localhost" 1984 "admin" "admin" $ \session -> do+ execute session "create db database" >>= print+ add session "world/World.xml" "<x>Hello World!</x>" >>= print+ add session "Universe.xml" "<x>Hello Universe!</x>" >>= print+ execute session "xquery /" >>= print+ execute session "drop db database" >>= print
+ src/BaseXClient.hs view
@@ -0,0 +1,22 @@+module BaseXClient (+ module BaseXClient,+ module BaseXClient.Query,+ module BaseXClient.Session+) where++import qualified BaseXClient.Query as Query+import BaseXClient.Query hiding (info, execute, close, sendCode, exec)+import BaseXClient.Session hiding (sendInput)+import Control.Exception+import Network+import System.IO++executeQuery = Query.execute+closeQuery = Query.close+queryInfo = Query.info++withSession :: String -> PortNumber -> String -> String -> (Handle -> IO a) -> IO a+withSession host port user pass = bracket (connect host port user pass) close++withQuery :: Handle -> String -> (Query -> IO a) -> IO a+withQuery session q = bracket (query session q) closeQuery
+ src/BaseXClient/Query.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE NamedFieldPuns #-}+module BaseXClient.Query where++import BaseXClient.Utils hiding (exec)+import qualified BaseXClient.Utils as Utils+import Control.Applicative+import System.IO++data Query = Query {+ session :: Handle,+ ident :: String+ }+ deriving Show++bind :: Query -> String -> String -> String -> IO String+bind query name val ty = exec query 3 [name, val, ty]++context :: Query -> String -> String -> IO String+context query val ty = exec query 14 [val, ty]++results :: Query -> IO [String]+results Query{session, ident} = do+ writeCode session 4+ writeString session ident+ result <- untilM (ok session) (readString session)+ ok session >>= \b -> if b+ then return result+ else readString session >>= error++execute, info, options, updating, close :: Query -> IO String+execute = sendCode 5+info = sendCode 6+options = sendCode 7+updating = sendCode 30+close = sendCode 2++sendCode :: Int -> Query -> IO String+sendCode code query = exec query code []++exec :: Query -> Int -> [String] -> IO String+exec Query{session, ident} code strs = Utils.exec session code $ ident : strs
+ src/BaseXClient/Session.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE LambdaCase #-}+module BaseXClient.Session where++import BaseXClient.Utils+import BaseXClient.Query (Query(..))+import Control.Applicative+import qualified Data.Digest.Pure.MD5 as MD5+import Data.ByteString.Lazy.UTF8 (fromString)+import Data.List+import Network+import System.IO++data Result = Result {+ content :: String,+ info :: String+ }+ deriving Show++connect :: String -> PortNumber -> String -> String -> IO Handle+connect host port user pass = do+ session <- connectTo host $ PortNumber port+ hSetBuffering session $ BlockBuffering $ Just 4096+ resp <- readString session+ let (code, nonce) = case elemIndex ':' resp of+ Just i -> let (realm, ':' : nonce') = splitAt i resp in+ (intercalate ":" [user, realm, pass], nonce')+ Nothing -> (pass, nonce)+ writeStrings session [user, md5 $ md5 code ++ nonce]+ ok session <$$> \b -> if b+ then session+ else error "Access denied."+ where md5 = show . MD5.md5 . fromString++execute :: Handle -> String -> IO Result+execute session cmd = do+ writeString session cmd+ content <- readString session+ info <- readString session+ ok session <$$> \b -> if b+ then Result{content, info}+ else error info++query :: Handle -> String -> IO Query+query session q = do+ ident <- exec session 0 [q]+ return $ Query session ident++create, add, replace, store :: Handle -> String -> String -> IO String+create = sendInput 8+add = sendInput 9+replace = sendInput 12+store = sendInput 13++sendInput :: Int -> Handle -> String -> String -> IO String+sendInput code session arg input = exec session code [arg, input]++close :: Handle -> IO ()+close session = do+ writeString session "exit"+ hClose session
+ src/BaseXClient/Utils.hs view
@@ -0,0 +1,55 @@+module BaseXClient.Utils where++import Control.Applicative+import Data.Char+import System.IO++exec :: Handle -> Int -> [String] -> IO String+exec h code strs = do+ writeCode h code+ writeStrings h strs+ info <- readString h+ ok h <$$> \b -> if b+ then info+ else error info++readString :: Handle -> IO String+readString h = do+ c <- hGetChar h+ if c /= '\0'+ then (:) <$> (if c == '\255' then hGetChar h else return c) <*> readString h+ else return []++writeCode :: Handle -> Int -> IO ()+writeCode h code = hPutChar h $ chr code++writeString :: Handle -> String -> IO ()+writeString h str = do+ writeString' h str+ hFlush h++writeStrings :: Handle -> [String] -> IO ()+writeStrings h strs = do+ mapM_ (writeString' h) strs+ hFlush h++writeString' :: Handle -> String -> IO ()+writeString' h str = do+ hPutStr h $ foldr (\c cs ->+ if c == '\0' || c == '\255' then '\255' : c : cs else c : cs) [] str+ hPutChar h '\0'++ok :: Handle -> IO Bool+ok h = ('\0' ==) <$> hGetChar h++untilM :: IO Bool -> IO a -> IO [a]+untilM test = whileM (not <$> test)++whileM :: IO Bool -> IO a -> IO [a]+whileM test body = test >>= \succeeds -> if succeeds+ then (:) <$> body <*> whileM test body+ else return []++infixl 4 <$$>+(<$$>) :: Functor f => f a -> (a -> b) -> f b+(<$$>) = flip fmap
+ src/CreateExample.hs view
@@ -0,0 +1,11 @@+module CreateExample where++import BaseXClient+import System.CPUTime+import Text.Printf++main :: IO ()+main = withSession "localhost" 1984 "admin" "admin" $ \session -> do+ create session "database" "<x>Hello World!</x>" >>= print+ execute session "xquery /" >>= print+ execute session "drop db database" >>= print
+ src/Example.hs view
@@ -0,0 +1,14 @@+module Example where++import BaseXClient+import System.CPUTime+import Text.Printf++main :: IO ()+main = do+ start <- getCPUTime+ withSession "localhost" 1984 "admin" "admin" $ \session ->+ execute session "xquery 1 to 10" >>= print+ end <- getCPUTime+ let diff = fromIntegral (end - start) / (10 ^ 12)+ printf "Computation time: %0.3f sec\n" (diff :: Double)
+ src/QueryBindExample.hs view
@@ -0,0 +1,12 @@+module QueryBindExample where++import BaseXClient hiding (query)++query :: String+query = "declare variable $name external; for $i in 1 to 10 return element { $name } { $i }"++main :: IO ()+main = withSession "localhost" 1984 "admin" "admin" $ \session ->+ withQuery session query $ \q -> do+ bind q "name" "number" ""+ executeQuery q >>= print
+ src/QueryExample.hs view
@@ -0,0 +1,8 @@+module QueryExample where++import BaseXClient++main :: IO ()+main = withSession "localhost" 1984 "admin" "admin" $ \session ->+ withQuery session "for $i in 1 to 10 return <xml>Text { $i }</xml>" $ \q ->+ results q >>= print