content: {-# LANGUAGE BangPatterns, OverloadedStrings #-}
  
  -- |
  -- Module:      Database.MySQL.Simpe.QueryResults
  -- Copyright:   (c) 2011 MailRank, Inc.
  -- License:     BSD3
  -- Maintainer:  Paul Rouse <pyr@doynton.org>
  -- Stability:   experimental
  -- Portability: portable
  --
  -- The 'QueryResults' typeclass, for converting a row of results
  -- returned by a SQL query into a more useful Haskell representation.
  --
  -- Predefined instances are provided for tuples containing up to ten
  -- elements.
  
  module Database.MySQL.Simple.QueryResults
      (
        QueryResults(..)
      , convertError
      ) where
  
  import Control.Exception (throw)
  import Data.ByteString (ByteString)
  import qualified Data.ByteString.Char8 as B
  import Database.MySQL.Base.Types (Field(fieldType, fieldName))
  import Database.MySQL.Simple.Result (ResultError(..), Result(..))
  import Database.MySQL.Simple.Types (Only(..))
  
  -- | A collection type that can be converted from a list of strings.
  --
  -- Instances should use the 'convert' method of the 'Result' class
  -- to perform conversion of each element of the collection.
  --
  -- This example instance demonstrates how to convert a two-column row
  -- into a Haskell pair. Each field in the metadata is paired up with
  -- each value from the row, and the two are passed to 'convert'.
  --
  -- @
  -- instance ('Result' a, 'Result' b) => 'QueryResults' (a,b) where
  --     'convertResults' [fa,fb] [va,vb] = (a,b)
  --         where !a = 'convert' fa va
  --               !b = 'convert' fb vb
  --     'convertResults' fs vs  = 'convertError' fs vs 2
  -- @
  --
  -- Notice that this instance evaluates each element to WHNF before
  -- constructing the pair. By doing this, we guarantee two important
  -- properties:
  --
  -- * Keep resource usage under control by preventing the construction
  --   of potentially long-lived thunks.
  --
  -- * Ensure that any 'ResultError' that might arise is thrown
  --   immediately, rather than some place later in application code
  --   that cannot handle it.
  --
  -- You can also declare Haskell types of your own to be instances of
  -- 'QueryResults'.
  --
  -- @
  --data User = User { firstName :: String, lastName :: String }
  --
  --instance 'QueryResults' User where
  --    'convertResults' [fa,fb] [va,vb] = User <$> a <*> b
  --        where !a = 'convert' fa va
  --              !b = 'convert' fb vb
  --    'convertResults' fs vs  = 'convertError' fs vs 2
  -- @
  
  class QueryResults a where
      convertResults :: [Field] -> [Maybe ByteString] -> a
      -- ^ Convert values from a row into a Haskell collection.
      --
      -- This function will throw a 'ResultError' if conversion of the
      -- collection fails.
  
  instance (Result a) => QueryResults (Only a) where
      convertResults [fa] [va] = Only a
          where !a = convert fa va
      convertResults fs vs  = convertError fs vs 1
  
  instance (Result a, Result b) => QueryResults (a,b) where
      convertResults [fa,fb] [va,vb] = (a,b)
          where !a = convert fa va; !b = convert fb vb
      convertResults fs vs  = convertError fs vs 2
  
  instance (Result a, Result b, Result c) => QueryResults (a,b,c) where
      convertResults [fa,fb,fc] [va,vb,vc] = (a,b,c)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
      convertResults fs vs  = convertError fs vs 3
  
  instance (Result a, Result b, Result c, Result d) =>
      QueryResults (a,b,c,d) where
      convertResults [fa,fb,fc,fd] [va,vb,vc,vd] = (a,b,c,d)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd
      convertResults fs vs  = convertError fs vs 4
  
  instance (Result a, Result b, Result c, Result d, Result e) =>
      QueryResults (a,b,c,d,e) where
      convertResults [fa,fb,fc,fd,fe] [va,vb,vc,vd,ve] = (a,b,c,d,e)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve
      convertResults fs vs  = convertError fs vs 5
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f) =>
      QueryResults (a,b,c,d,e,f) where
      convertResults [fa,fb,fc,fd,fe,ff] [va,vb,vc,vd,ve,vf] = (a,b,c,d,e,f)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
      convertResults fs vs  = convertError fs vs 6
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g) =>
      QueryResults (a,b,c,d,e,f,g) where
      convertResults [fa,fb,fc,fd,fe,ff,fg] [va,vb,vc,vd,ve,vf,vg] =
          (a,b,c,d,e,f,g)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg
      convertResults fs vs  = convertError fs vs 7
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h) =>
      QueryResults (a,b,c,d,e,f,g,h) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh] [va,vb,vc,vd,ve,vf,vg,vh] =
          (a,b,c,d,e,f,g,h)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh
      convertResults fs vs  = convertError fs vs 8
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i) =>
      QueryResults (a,b,c,d,e,f,g,h,i) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi] [va,vb,vc,vd,ve,vf,vg,vh,vi] =
          (a,b,c,d,e,f,g,h,i)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
      convertResults fs vs  = convertError fs vs 9
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj] =
          (a,b,c,d,e,f,g,h,i,j)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj
      convertResults fs vs  = convertError fs vs 10
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk] =
          (a,b,c,d,e,f,g,h,i,j,k)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk
      convertResults fs vs  = convertError fs vs 11
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl] =
          (a,b,c,d,e,f,g,h,i,j,k,l)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
      convertResults fs vs  = convertError fs vs 12
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm] =
          (a,b,c,d,e,f,g,h,i,j,k,l,m)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm
      convertResults fs vs  = convertError fs vs 13
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn] =
          (a,b,c,d,e,f,g,h,i,j,k,l,m,n)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn
      convertResults fs vs  = convertError fs vs 14
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo] =
          (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
      convertResults fs vs  = convertError fs vs 15
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp] =
          (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp;
      convertResults fs vs  = convertError fs vs 16
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p, Result q) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp,fq]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp,vq] =
          (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp; !q = convert fq vq;
      convertResults fs vs  = convertError fs vs 17
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p, Result q, Result r) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp,fq,fr]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp,vq,vr] =
          (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp; !q = convert fq vq; !r = convert fr vr
      convertResults fs vs  = convertError fs vs 18
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p, Result q, Result r,
            Result s) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp,fq,fr,fs]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp,vq,vr,vs]
          = (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp; !q = convert fq vq; !r = convert fr vr
                !s = convert fs vs;
      convertResults fs_ vs_  = convertError fs_ vs_ 19
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p, Result q, Result r,
            Result s, Result t) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp,fq,fr,fs,
                      ft]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp,vq,vr,vs,
                      vt]
          = (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp; !q = convert fq vq; !r = convert fr vr
                !s = convert fs vs; !t = convert ft vt
      convertResults fs_ vs_  = convertError fs_ vs_ 20
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p, Result q, Result r,
            Result s, Result t, Result u) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp,fq,fr,fs,
                      ft,fu]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp,vq,vr,vs,
                      vt,vu]
          = (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp; !q = convert fq vq; !r = convert fr vr
                !s = convert fs vs; !t = convert ft vt; !u = convert fu vu
      convertResults fs_ vs_  = convertError fs_ vs_ 21
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p, Result q, Result r,
            Result s, Result t, Result u, Result v) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp,fq,fr,fs,
                      ft,fu,fv]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp,vq,vr,vs,
                      vt,vu,vv]
          = (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp; !q = convert fq vq; !r = convert fr vr
                !s = convert fs vs; !t = convert ft vt; !u = convert fu vu
                !v = convert fv vv;
      convertResults fs_ vs_  = convertError fs_ vs_ 22
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p, Result q, Result r,
            Result s, Result t, Result u, Result v, Result w) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp,fq,fr,fs,
                      ft,fu,fv,fw]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp,vq,vr,vs,
                      vt,vu,vv,vw]
          = (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp; !q = convert fq vq; !r = convert fr vr
                !s = convert fs vs; !t = convert ft vt; !u = convert fu vu
                !v = convert fv vv; !w = convert fw vw;
      convertResults fs_ vs_  = convertError fs_ vs_ 23
  
  instance (Result a, Result b, Result c, Result d, Result e, Result f,
            Result g, Result h, Result i, Result j, Result k, Result l,
            Result m, Result n, Result o, Result p, Result q, Result r,
            Result s, Result t, Result u, Result v, Result w, Result x) =>
      QueryResults (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x) where
      convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj,fk,fl,fm,fn,fo,fp,fq,fr,fs,
                      ft,fu,fv,fw,fx]
                     [va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl,vm,vn,vo,vp,vq,vr,vs,
                      vt,vu,vv,vw,vx]
          = (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x)
          where !a = convert fa va; !b = convert fb vb; !c = convert fc vc
                !d = convert fd vd; !e = convert fe ve; !f = convert ff vf
                !g = convert fg vg; !h = convert fh vh; !i = convert fi vi
                !j = convert fj vj; !k = convert fk vk; !l = convert fl vl
                !m = convert fm vm; !n = convert fn vn; !o = convert fo vo
                !p = convert fp vp; !q = convert fq vq; !r = convert fr vr
                !s = convert fs vs; !t = convert ft vt; !u = convert fu vu
                !v = convert fv vv; !w = convert fw vw; !x = convert fx vx;
      convertResults fs_ vs_  = convertError fs_ vs_ 24
  
  -- | Throw a 'ConversionFailed' exception, indicating a mismatch
  -- between the number of columns in the 'Field' and row, and the
  -- number in the collection to be converted to.
  convertError :: [Field]
               -- ^ Descriptors of fields to be converted.
               -> [Maybe ByteString]
               -- ^ Contents of the row to be converted.
               -> Int
               -- ^ Number of columns expected for conversion.  For
               -- instance, if converting to a 3-tuple, the number to
               -- provide here would be 3.
               -> a
  convertError fs vs n = throw $ ConversionFailed
      (show (length fs) ++ " values: " ++ show (zip (map fieldType fs)
                                                    (map (fmap ellipsis) vs)))
      (show n ++ " slots in target type")
      (show (map (B.unpack . fieldName) fs))
      "mismatch between number of columns to convert and number in target type"
  
  ellipsis :: ByteString -> ByteString
  ellipsis bs
      | B.length bs > 15 = B.take 10 bs `B.append` "[...]"
      | otherwise        = bs
  