@@ -5,27 +5,34 @@ import Data.Csv
55import ProjectM36.Tuple
66import qualified Data.HashSet as HS
77import qualified Data.ByteString.Lazy as BS
8+import qualified Data.ByteString as BSS
89import qualified Data.Vector as V
910import ProjectM36.Error
1011import qualified Data.Text.Encoding as TE
1112import qualified Data.Text as T
1213
13−--maybe support a header in the future
14+--spit out error for relations without attributes (since relTrue and relFalse cannot be distinguished then as CSV) and for relations with relation-valued attributes
1415relationAsCSV :: Relation -> Either RelationalError BS.ByteString
15−relationAsCSV (Relation attrs tupleSet) = case relValAttrs of
16− [] -> Right $ encodeHeader `BS.append` tupleSetAsCSV tupleSet
17− attrs' -> Left $ RelationValuedAttributesNotSupportedError (map attributeName attrs')
16+relationAsCSV (Relation attrs tupleSet) = if relValAttrs /= [] then --check for relvalued attributes
17+ Left $ RelationValuedAttributesNotSupportedError (map attributeName relValAttrs)
18+ else if V.length attrs == 0 then --check that there is at least one attribute
19+ Left $ TupleAttributeCountMismatchError 0
20+ else
21+ Right $ encodeByName bsAttrNames (HS.toList tupleSet)
1822 where
19− --figure out how to make cassava responsible for printing the header even for non-named records or make NamedRecord instance for RelationTuple ?
20− -- this will break on attribute names which need quoting
21− encodeHeader = (BS.intercalate (BS.fromStrict . TE.encodeUtf8 . T.pack $ ",") $ V.toList $ V.map (BS.fromStrict . TE.encodeUtf8 . attributeName) attrs) `BS.append` (BS.fromStrict . TE.encodeUtf8 . T.pack) "\n"
2223 relValAttrs = V.toList $ V.filter (isRelationAtomType . atomType) attrs
23−
24−tupleSetAsCSV :: RelationTupleSet -> BS.ByteString
25−tupleSetAsCSV tupleSet = encode (HS.toList tupleSet)
26−
24+ bsAttrNames = V.map (TE.encodeUtf8 . attributeName) attrs
25+
26+{-
2727instance ToRecord RelationTuple where
2828 toRecord tuple = toRecord $ map toField (V.toList $ tupleAtoms tuple)
29+-}
30+
31+instance ToNamedRecord RelationTuple where
32+ toNamedRecord tuple = namedRecord $ map (\(k,v) -> TE.encodeUtf8 k .= v) (tupleAssocs tuple)
33+
34+instance DefaultOrdered RelationTuple where
35+ headerOrder tuple = V.map (TE.encodeUtf8 . attributeName) (tupleAttributes tuple)
2936
3037instance ToField Atom where
3138 toField (BoolAtom atom) = toField (if atom then "t" else "f")
@@ -34,6 +41,6 @@ instance ToField Atom where
3441 toField (DateTimeAtom atom) = toField (show atom)
3542 toField (DateAtom atom) = toField (show atom)
3643 toField (DoubleAtom atom) = toField atom
37− toField (RelationAtom _) = undefined -- CSV does not support nested relations
44+ toField (RelationAtom _) = error "Relation atoms cannot be used in CSV."
3845
3946