packages feed

ldif 0.0.6 → 0.0.7

raw patch · 13 files changed

+213/−88 lines, 13 filesdep ~HUnitnew-component:exe:ldifparsePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: HUnit

API changes (from Hackage documentation)

- Text.LDIF.Types: LDIFChanges :: Maybe String -> [LDIFRecord] -> LDIF
- Text.LDIF.Types: LDIFContent :: Maybe String -> [LDIFRecord] -> LDIF
- Text.LDIF.Types: lcChanges :: LDIF -> [LDIFRecord]
+ Text.LDIF.Types: LDIF :: Maybe String -> [LDIFRecord] -> LDIF
+ Text.LDIF.Types: LDIFMixedType :: LDIFType
+ Text.LDIF.Utils: filterAttr :: String -> [AttrValue] -> [AttrValue]
+ Text.LDIF.Utils: getLDIFType :: LDIF -> LDIFType
+ Text.LDIF.Utils: isChangeRecord :: LDIFRecord -> Bool
+ Text.LDIF.Utils: isContentRecord :: LDIFRecord -> Bool
+ Text.LDIF.Utils: ldif2tree :: LDIF -> Tree LDIFRecord
+ Text.LDIF.Utils: lookupAttr :: String -> [AttrValue] -> Maybe Value
- Text.LDIF.Diff: diffLDIF :: LDIF -> LDIF -> Maybe LDIF
+ Text.LDIF.Diff: diffLDIF :: LDIF -> LDIF -> Either String LDIF

Files

TODO.org view
@@ -7,8 +7,8 @@ ** update serializers back to LDIF for base64 & all DN notations ** add module tests for versioned LDIFs ** add module tests for base64 parsing-** Use getopt for input parameters on diffLDIF cmd-** TODO Implement ldifmodify (like ldapmodify)+** DONE Use getopt for input parameters on diffLDIF cmd+** DONE Implement ldifmodify (like ldapmodify)     offline applying of Change LDIF on Content LDIF     ldifmodify -f <contentLDIF> -o <outputLDIF> [<modifyLDIF> ... ]  * Archive
ldif.cabal view
@@ -1,5 +1,5 @@ Name:            ldif-Version:         0.0.6+Version:         0.0.7 License:         BSD3 License-File:    LICENSE Synopsis:        The LDAP Data Interchange Format (LDIF) parser @@ -82,9 +82,13 @@   Hs-Source-Dirs:  src   Main-Is:         ldifmodify.hs +Executable ldifparse+  Hs-Source-Dirs:  src+  Main-Is:         ldifparse.hs+ Executable test   Hs-Source-Dirs:  src, tests   Main-Is:         TestMain.hs-  Build-Depends:   base, HUnit+  Build-Depends:   base, HUnit == 1.2.*   if !flag(test)     Buildable:     False
src/Text/LDIF/Apply.hs view
@@ -3,52 +3,50 @@ ) where import Text.LDIF.Types-import Text.LDIF.Printer import Text.LDIF.Utils-import Data.Maybe-import Data.Either+import Text.LDIF.Printer import Data.List (nub)  -- | Apply one LDIF to another LDIF. The destination LDIF has -- | to be Content LDIF applyLDIF :: LDIF -> LDIF -> LDIF-applyLDIF dst@(LDIFContent _ _) (LDIFChanges _ xs) = foldr (applyRecord2LDIF) dst xs-applyLDIF dst@(LDIFContent _ _) (LDIFContent _ xs) = foldr (applyRecord2LDIF) dst xs-applyLDIF _ _ = error "Destination LDIF has to be Content LDIF and not Change LDIF"+applyLDIF dst (LDIF _ xs) = foldr (applyRecord2LDIF) dst xs  -- | Apply one LDIF Content/Change Record into LDIF and produce Changed LDIF applyRecord2LDIF :: LDIFRecord -> LDIF -> LDIF-applyRecord2LDIF rec@(ContentRecord dn vals) dst = applyRecord2LDIF (ChangeRecord dn (ChangeAdd vals)) dst-applyRecord2LDIF rec@(ChangeRecord  dn op)   dst = applyChange2Record op dn dst (findRecordByDN dst dn)+applyRecord2LDIF (ContentRecord dn vals) dst = applyRecord2LDIF (ChangeRecord dn (ChangeAdd vals)) dst+applyRecord2LDIF (ChangeRecord  dn op)   dst = applyChange2Record op dn dst (findRecordByDN dst dn)  -- | Apply one LDIF Change (add/del/modf) for given DN within LDIF Content  applyChange2Record :: Change -> DN -> LDIF -> Maybe LDIFRecord -> LDIF-applyChange2Record (ChangeAdd vals)   dn (LDIFContent v xs) Nothing  = LDIFContent v (xs++[ContentRecord dn vals]) -applyChange2Record (ChangeAdd vals)   dn (LDIFContent v xs) (Just _) = error ("ADD: Already exists: "++(show dn))-applyChange2Record ChangeDelete       dn (LDIFContent v xs) (Just _) = let rn = filter (\x -> dn /= (reDN x)) xs-                                                                       in LDIFContent v rn-applyChange2Record ChangeDelete       dn (LDIFContent v xs) Nothing  = error ("DELETE: Entry not found: "++(show dn))-applyChange2Record (ChangeModify ops) dn (LDIFContent v xs) (Just r) = let pre  = takeWhile (\x -> dn /= (reDN x)) xs-                                                                           post = filter (\x -> dn /= (reDN x)) $ dropWhile (\x -> dn /= (reDN x)) xs-                                                                           rn   = foldr applyMod2Record r ops-                                                                       in LDIFContent v (pre++[rn]++post)-applyChange2Record ChangeModDN      _ _ _  = error "Operation ModDN is not supported"+applyChange2Record (ChangeAdd vals)   dn ld Nothing  = LDIF (lcVersion ld) ((lcEntries ld)++[ContentRecord dn vals]) +applyChange2Record (ChangeAdd _)      dn _  (Just _) = error ("ADD: Already exists: "++(dn2str dn))+applyChange2Record ChangeDelete       dn ld (Just _) = LDIF (lcVersion ld) (filter (\x -> dn /= (reDN x)) (lcEntries ld))+applyChange2Record ChangeDelete       dn _  Nothing  = error ("DELETE: Entry not found: "++(dn2str dn))+applyChange2Record (ChangeModify _)   dn _  Nothing  = error ("MODIFY: Entry not found: "++(dn2str dn))+applyChange2Record (ChangeModify ops) dn ld (Just r) = let pre  = takeWhile (\x -> dn /= (reDN x)) (lcEntries ld)+                                                           post = filter (\x -> dn /= (reDN x)) $ dropWhile (\x -> dn /= (reDN x)) (lcEntries ld)+                                                           rn   = foldr applyMod2Record r ops+                                                       in LDIF (lcVersion ld) (pre++[rn]++post)+applyChange2Record ChangeModDN      _ _ _  = error "ModDN: Operation is not supported yet."+--applyChange2Record x _ y _                 = error $ "Unexpected LDIF Content: " ++ (show x) ++ (show y)  -- | Apply Attribute Modification (Add/Del/Replace) to ContentRecord and produce changed ContentRecord applyMod2Record :: Modify -> LDIFRecord -> LDIFRecord applyMod2Record (ModAdd      name vals) (ContentRecord dn av) = let nav = map (\v -> (name,v)) vals  -- new attr/values                                                                     mav = av ++ nav                  -- merged attr/values                                                                     verified = if (length $ nub mav) == (length mav) then mav-                                                                               else error ("ModAdd: Values already exists: "++(show av)++" vs "++(show vals)++" DN:"++(show dn))+                                                                               else error ("ModAdd: Values already exists: "++(show av)++" vs "++(show vals)++" DN:"++(dn2str dn))                                                                     in ContentRecord dn verified-applyMod2Record (ModDelete   name [])   (ContentRecord dn av) = let nav = filter (\(n,v) -> n /= name) av -- new attr/values+applyMod2Record (ModDelete   name [])   (ContentRecord dn av) = let nav = filter (\(n,_) -> n /= name) av -- new attr/values                                                                     verified = if (length $ nub nav) /= (length av) then nav-                                                                               else error ("ModDel: Attribute not found: "++(show name)++" DN:"++(show dn))+                                                                               else error ("ModDel: Attribute not found: "++(show name)++" DN:"++(dn2str dn))                                                                 in ContentRecord dn verified applyMod2Record (ModDelete   name vals) (ContentRecord dn av) = let mav = filter (\(n,v) -> n /= name || v `notElem` vals) av                                                                     verified = if (length av) - (length mav) == (length vals) then mav-                                                                               else error ("ModDel: Attribute/Value not found: "++(show name)++"vals"++(show vals)++" DN:"++(show dn))+                                                                               else error ("ModDel: Attribute/Value not found: "++(show name)++"vals"++(show vals)++" DN:"++(dn2str dn))                                                                 in ContentRecord dn verified applyMod2Record (ModReplace  name vals) (ContentRecord dn av) = let nav = map (\v -> (name,v)) vals -- new attr/values-                                                                    mav = (filter (\(n,v) -> n /= name) av) ++ nav -- merged+                                                                    mav = (filter (\(n,_) -> n /= name) av) ++ nav -- merged                                                                 in ContentRecord dn mav+applyMod2Record _ x = error $ "Unexpected LDIF Record:" ++ (show x)
src/Text/LDIF/Diff.hs view
@@ -5,10 +5,7 @@ where import Text.LDIF.Types import Text.LDIF.Utils-import Text.LDIF.Printer import Data.Maybe-import Data.Either-import Data.List (nub)  -- | Create Change LDIF between to LDIF contents. If any -- | of input argument is not LDIFContent it returns Nothing. @@ -18,8 +15,14 @@ -- | Unsing following strategy:  -- | 1. Iterate over L1 DN's and Modify / Remove Content  -- | 2. Iterate over L2 and Add Content not in L1-diffLDIF :: LDIF -> LDIF -> Maybe LDIF-diffLDIF l1@(LDIFContent _ c1) l2@(LDIFContent v2 c2) = Just (LDIFChanges v2 (changes ++ adds))+diffLDIF :: LDIF -> LDIF -> Either String LDIF+diffLDIF l1 l2 | getLDIFType l1 == LDIFContentType && getLDIFType l2 == LDIFContentType = Right (diffLDIF' l1 l2)+               | otherwise = Left ("Diff supported only on Content LDIFs but SRC="+                                         ++(show $ getLDIFType l1)++" and DST="+                                         ++(show $ getLDIFType l2))++diffLDIF' :: LDIF -> LDIF -> LDIF+diffLDIF' l1@(LDIF _ c1) l2@(LDIF v2 c2) = LDIF v2 (changes ++ adds)    where        adds = map (content2add) $ filter (not . isEntryIn l1) c2       changes = filter (not . isDummyRecord) $ foldl (processEntry) [] c1@@ -33,7 +36,7 @@                           Nothing -> False                           Just _  -> True       content2add (ContentRecord dn vals) = ChangeRecord dn (ChangeAdd vals)-diffLDIF _ _ = Nothing+      content2add (ChangeRecord _ _)      = error "Unexpected record type"  -- | Diff two AttrVal Records if any of provided.  -- | Implementation uses inefficient algorithm for large count of attributes within ContentRecord.
src/Text/LDIF/Parser.hs view
@@ -7,9 +7,8 @@ where import Text.LDIF.Types import Text.ParserCombinators.Parsec-import Data.Either import Data.Char-import Data.List (isPrefixOf, foldl')+import Data.List (isPrefixOf)  -- | Parse string as LDIF content and return LDIF or ParseError parseLDIFStr :: String -> Either ParseError LDIF@@ -26,6 +25,7 @@ -- | and mismatch generates ParseError parseLDIFStrAs' :: String -> Maybe LDIFType -> String -> Either ParseError LDIF parseLDIFStrAs' nm Nothing                = parse pLdif        nm . preproc+parseLDIFStrAs' nm (Just LDIFMixedType)   = parse pLdif        nm . preproc parseLDIFStrAs' nm (Just LDIFContentType) = parse pLdifContent nm . preproc parseLDIFStrAs' nm (Just LDIFChangesType) = parse pLdifChanges nm . preproc @@ -56,12 +56,12 @@ takeLine :: [String] -> (String, [String]) takeLine []     = ([],[]) takeLine (x:[]) = (x,[])-takeLine (x:xs) = let isCont x = " " `isPrefixOf` x  +takeLine (x:xs) = let isCont z = " " `isPrefixOf` z                   in (x ++ (concat $ map (tail) $ takeWhile (isCont) xs), dropWhile (isCont) xs)   -- | Parsec ldif parser pLdif :: CharParser st LDIF-pLdif = try pLdifChanges <|> pLdifContent+pLdif = try pLdifChanges <|> try pLdifMixed  pLdifChanges :: CharParser st LDIF pLdifChanges = do@@ -69,15 +69,26 @@     ver <- optionMaybe pVersionSpec     pSEPs     recs <- sepEndBy1 pChangeRec pSEPs-    return $ LDIFChanges ver recs+    eof+    return $ LDIF ver recs +pLdifMixed:: CharParser st LDIF+pLdifMixed = do+    pSEPs+    ver <- optionMaybe pVersionSpec+    pSEPs+    recs <- sepEndBy1 pRec pSEPs+    eof+    return $ LDIF ver recs+ pLdifContent :: CharParser st LDIF pLdifContent = do     pSEPs     ver <- optionMaybe pVersionSpec     pSEPs     recs <- sepEndBy1 pAttrValRec pSEPs-    return $ LDIFContent ver recs+    eof+    return $ LDIF ver recs  pAttrValRec ::  CharParser st LDIFRecord pAttrValRec = do@@ -86,6 +97,9 @@     attrVals <- sepEndBy1 pAttrValSpec pSEP     return $ ContentRecord dn attrVals +pRec :: CharParser st LDIFRecord+pRec = try pChangeRec <|> pAttrValRec+ pChangeRec :: CharParser st LDIFRecord pChangeRec = try pChangeAdd          <|> try pChangeDel@@ -96,9 +110,9 @@ pChangeAdd = do     dn <- pDNSpec     pSEP-    string "changetype:"+    _ <- string "changetype:"     pFILL-    string "add"+    _ <- string "add"     pSEP     vals <- sepEndBy1 pAttrValSpec pSEP     return $ ChangeRecord dn (ChangeAdd vals)@@ -107,9 +121,9 @@ pChangeDel = do     dn <- pDNSpec     pSEP-    string "changetype:"+    _ <- string "changetype:"     pFILL-    string "delete"+    _ <- string "delete"     pSEP     return $ ChangeRecord dn ChangeDelete @@ -117,9 +131,9 @@ pChangeMod = do     dn <- pDNSpec     pSEP-    string "changetype:"+    _ <- string "changetype:"     pFILL-    string "modify"+    _ <- string "modify"     pSEP     mods <- sepEndBy1 pModSpec (char '-' >> pSEP)     return $ ChangeRecord dn (ChangeModify mods)@@ -128,17 +142,17 @@ pChangeModDN = do     dn <- pDNSpec     pSEP-    string "changetype:"+    _ <- string "changetype:"     pFILL-    string "modrdn" +    _ <- string "modrdn"      pSEP-    string "newrdn:"+    _ <- string "newrdn:"     pFILL -    pRDN+    _ <- pRDN     pSEP-    string "deleteoldrdn:"+    _ <- string "deleteoldrdn:"     pFILL-    oneOf "01"+    _ <- oneOf "01"     pSEP     return $ ChangeRecord dn ChangeModDN @@ -147,7 +161,7 @@  pDNSpec :: CharParser st DN pDNSpec = do-    string "dn:"+    _ <- string "dn:"     pDN  pDN :: CharParser st DN@@ -160,7 +174,7 @@ pAttrEqValue = do    pFILL    att <- pAttributeType-   char '='+   _ <- char '='    val <- pAttrValueDN    return (att,val) @@ -168,12 +182,13 @@ pAttrValueDN = do    many (noneOf stringChars)    where -     specialChars = [',','=','+','<','>','#',';','\n','\r']+     specialChars = [',','=','+','#',';','\n','\r']+     -- specialChars = [',','=','+','<','>','#',';','\n','\r']      stringChars  = '\\':'"':specialChars   pVersionSpec :: CharParser st String pVersionSpec = do-   string "version:"+   _ <- string "version:"    pFILL    many1 digit @@ -213,7 +228,7 @@  pValueSpec :: CharParser st Value pValueSpec = try (char ':' >> char ':' >> pFILL >> pBase64String)-         <|> try (char ':' >> pFILL >> pSafeString) +         <|> try (char ':' >> pFILL >> pSafeString')           <|> (char ':' >> char '<' >> pFILL >> pURL)  pURL :: CharParser st String@@ -224,6 +239,11 @@    c <- noneOf "\n\r :<"    r <- many (noneOf "\n\r")    return $ c:r++pSafeString' :: CharParser st String+pSafeString' = do+   r <- many (noneOf "\n\r")+   return r   pBase64String :: CharParser st String pBase64String = pSafeString@@ -234,11 +254,11 @@ pLdapOid :: CharParser st Attribute pLdapOid = do    num <- many1 digit-   rest <- many (do { string "."; n <- many1 digit; return $ '.':n})+   rest <- many (do { _ <- string "."; n <- many1 digit; return $ '.':n})    return (Attribute $ num ++ concat rest)  pFILL :: CharParser st ()-pFILL = spaces+pFILL = skipMany (oneOf [' ', '\t'])  pSEP :: CharParser st () pSEP = try (char '\r' >> char '\n' >> return () )
src/Text/LDIF/Printer.hs view
@@ -10,8 +10,7 @@  -- | Serialize LDIF in LDIF Format ldif2str :: LDIF -> String-ldif2str (LDIFContent v xs) = unlines $ (ver2str v) ++ (map (record2str) xs)-ldif2str (LDIFChanges v xs) = unlines $ (ver2str v) ++ (map (record2str) xs)+ldif2str (LDIF v xs) = unlines $ (ver2str v) ++ (map (record2str) xs)  -- | Serialize version to LDIF Format Lines ver2str :: Maybe String -> [String]
src/Text/LDIF/Types.hs view
@@ -23,12 +23,18 @@ type AttrValue = (Attribute, Value)  -- | Type of LDIF Files (Content, Changes)-data LDIFType = LDIFContentType | LDIFChangesType deriving (Show, Eq) -- Maybe LDIFMixedType +data LDIFType = LDIFContentType +              | LDIFChangesType +              | LDIFMixedType deriving Eq +instance Show LDIFType where+    show LDIFChangesType = "Delta"+    show LDIFContentType = "Content"+    show LDIFMixedType   = "Mixed"+ -- | Represents LDIF structure, it can be either simply LDIF data dump or -- | changes LDIF with LDAP operations -data LDIF = LDIFContent { lcVersion :: Maybe String, lcEntries :: [LDIFRecord] }-          | LDIFChanges { lcVersion :: Maybe String, lcChanges :: [LDIFRecord] } deriving (Show, Eq)+data LDIF = LDIF { lcVersion :: Maybe String, lcEntries :: [LDIFRecord] } deriving (Show, Eq)  -- | Represents one data record within LDIF file with DN and content -- | Represents one change record within LDIF file with DN and content
src/Text/LDIF/Utils.hs view
@@ -7,19 +7,21 @@         takeDNPrefix,         leafOfDN,         rootOfDN,-        isDummyRecord+        lookupAttr,+        filterAttr,+        isDummyRecord,+        ldif2tree,+        getLDIFType,+        isContentRecord,+        isChangeRecord ) where import Text.LDIF.Types-import Text.LDIF.Printer-import Data.Maybe-import Data.Either-import Data.List (nub)+import Data.Tree  -- | Find all Contents with given DN findRecordsByDN :: LDIF -> DN -> [LDIFRecord]-findRecordsByDN (LDIFContent _ entries) dn = filter (\x -> (reDN x) == dn) entries-findRecordsByDN (LDIFChanges _ entries) dn = filter (\x -> (reDN x) == dn) entries+findRecordsByDN (LDIF _ entries) dn = filter (\x -> (reDN x) == dn) entries  -- | Find first Content with given DN findRecordByDN :: LDIF -> DN -> Maybe LDIFRecord@@ -28,6 +30,14 @@                                  xs   -> Just (head xs)  +-- | Find fist Attribute within attributes pairs list+lookupAttr :: String -> [AttrValue] -> Maybe Value+lookupAttr attr xs = lookup (Attribute attr) xs++-- | Filter Attribute Value list according Attribute name+filterAttr :: String -> [AttrValue] -> [AttrValue]+filterAttr attr xs  = filter (\x -> (Attribute attr) == fst x) xs+ -- | Change record without any impact isDummyRecord :: LDIFRecord -> Bool isDummyRecord (ChangeRecord _ (ChangeModify [])) = True @@ -46,11 +56,47 @@ getDNValue (DN vals) idx = vals !! idx  takeDNPrefix :: DN -> Int -> DN-takeDNPrefix (DN vals) n = (DN (take n vals))+takeDNPrefix (DN vals) n = (DN (reverse $ take n (reverse vals)))  -- | Check if the dn1 is prefix of dn2 isDNPrefixOf :: DN -> DN -> Bool isDNPrefixOf dn1 dn2 | (sizeOfDN dn1) >= (sizeOfDN dn2) = False-                     | otherwise = let n = (sizeOfDN dn2)-                                   in (takeDNPrefix dn1 n) == dn2+                     | otherwise = let n = (sizeOfDN dn1)+                                   in (takeDNPrefix dn2 n) == dn1 +dummyRootDN :: DN+dummyRootDN = DN [(Attribute "dc", "root")]++ldif2tree :: LDIF -> Tree LDIFRecord+ldif2tree (LDIF _ entries) = Node (ChangeRecord dummyRootDN (ChangeAdd [])) (ldifRecs2tree entries)++isParentRecordOf :: LDIFRecord -> LDIFRecord -> Bool+isParentRecordOf a b = isDNPrefixOf (reDN a) (reDN b)++ldifRoots :: [LDIFRecord] -> [LDIFRecord]+ldifRoots xs = let isRoot x = all (\y -> not $ isParentRecordOf y x) xs+               in filter (isRoot) xs++ldifRecs2tree :: [LDIFRecord] -> [Tree LDIFRecord]+ldifRecs2tree xs = let roots = (ldifRoots xs)+                       subtr x = ldifRecs2tree $ filter (isParentRecordOf x) xs+                   in map (\x -> Node x (subtr x)) roots++isContentRecord :: LDIFRecord -> Bool+isContentRecord (ContentRecord _ _) = True+isContentRecord _ = False++isChangeRecord :: LDIFRecord -> Bool+isChangeRecord (ChangeRecord _ _) = True+isChangeRecord _ = False++getLDIFType :: LDIF -> LDIFType+getLDIFType (LDIF _ []) = LDIFContentType+getLDIFType (LDIF _ xs) = getLDIFType' con chg+    where+      con = filter (isContentRecord) xs+      chg = filter (not . isContentRecord) xs+      getLDIFType' [] [] = error "Unexpected"+      getLDIFType' [] _  = LDIFChangesType+      getLDIFType' _  [] = LDIFContentType+      getLDIFType' _  _  = LDIFMixedType
src/diffLDIF.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveDataTypeable #-} -- | Make Change LDIF based on two Content LDIFs import Data.List import Data.Either@@ -6,11 +7,31 @@ import System.FilePath  import System.Environment import Text.LDIF+import System.Console.CmdArgs +progDesc = "Create delta LDIF between Source LDIF and Target LDIF"++data DiffLdif = DiffLdif { srcFile :: FilePath+                         , dstFile :: FilePath } deriving (Show, Data, Typeable)++defaultCfg = mode $ DiffLdif { srcFile = def &= typFile & flag "s" & text "Source LDIF File"+                             , dstFile = def &= typFile & flag "t" & text "Target LDIF File" }++verifyCfg :: DiffLdif -> IO ()+verifyCfg (DiffLdif [] []) = do +  msg <- cmdArgsHelp progDesc [defaultCfg] Text+  error msg+verifyCfg (DiffLdif [] _)  = error "Missing Source LDIF File parameter (-s)"+verifyCfg (DiffLdif _ [])  = error "Missing Target LDIF File parameter (-t)"+verifyCfg (DiffLdif _ _ )  = return ()+ main = do-  args <- getArgs-  ml1 <- parseLDIFFile (args !! 0)-  ml2 <- parseLDIFFile (args !! 1)+  cfg <- cmdArgs progDesc [defaultCfg]+  verifyCfg cfg+  ml1 <- parseLDIFFile (srcFile cfg)+  ml2 <- parseLDIFFile (dstFile cfg)   case rights [ml1,ml2] of -       [l1,l2] -> putStrLn $ ldif2str $ fromJust $ diffLDIF l1 l2  +       [l1,l2] -> case diffLDIF l1 l2 of+                    Left err -> putStrLn err+                    Right delta -> putStrLn $ ldif2str delta        _       -> print $ lefts [ml1,ml2] 
src/ldif2html.hs view
@@ -9,8 +9,7 @@ import qualified Data.Set as Set  ldif2html :: Set.Set String -> LDIF -> String-ldif2html idx (LDIFContent v xs) = unlines $ (ver2str v) ++ (map (record2html idx) xs)-ldif2html idx (LDIFChanges v xs) = unlines $ (ver2str v) ++ (map (record2html idx) xs)+ldif2html idx (LDIF v xs) = unlines $ (ver2str v) ++ (map (record2html idx) xs)  -- | Serialize version to LDIF Format Lines ver2str :: Maybe String -> [String]@@ -73,5 +72,4 @@        xs  -> mapM_ print xs  dns :: LDIF -> Set.Set String-dns (LDIFContent _ xs) = Set.fromList $ nub $ map (dn2last . reDN) xs-dns (LDIFChanges _ xs) = Set.fromList $ nub $ map (dn2last . reDN) xs+dns (LDIF _ xs) = Set.fromList $ nub $ map (dn2last . reDN) xs
src/ldifmodify.hs view
@@ -10,6 +10,8 @@ import Text.LDIF import System.Console.CmdArgs +progDesc = "Apply LDAP operations from LDIF to LDIF (like ldapmodify)"+ data LdifModify = LdifModify { baseFile :: FilePath                              , modFiles  :: [FilePath]                              , outFile  :: FilePath } deriving (Show, Data, Typeable)@@ -19,12 +21,15 @@                                , outFile = def &= typFile & flag "o" & text "Output LDIF File" }  verifyCfg :: LdifModify -> IO ()+verifyCfg (LdifModify [] [] [])                       = do+               msg <- cmdArgsHelp progDesc [defaultCfg] Text+               error msg verifyCfg (LdifModify bf mfx ouf) | length bf  == 0   = error "Missing Base LDIF File parameter (-f)"                                   | length mfx == 0   = error "Missing LDIF Files for applying as arguments"                                   | otherwise         = return ()                                                                main = do-  cfg <- cmdArgs "LDIF Modify. Apply LDAP operations from LDIF to LDIF" [defaultCfg]+  cfg <- cmdArgs progDesc [defaultCfg]   verifyCfg cfg   baseLDIF <- safeParseLDIFFile (baseFile cfg)   modLDIFs <- mapM (safeParseLDIFFile) (modFiles cfg)
+ src/ldifparse.hs view
@@ -0,0 +1,18 @@+-- | Make Change LDIF based on two Content LDIFs+import Data.List+import Data.Either+import Data.Maybe+import Control.Monad +import System.FilePath +import System.Environment+import Text.LDIF++main = do+  args <- getArgs+  ml1 <- parseLDIFFile (args !! 0)+  case ml1 of +       Right l1 -> do+             print l1+             putStrLn "========================================================"+             putStrLn $ ldif2str l1+       Left err -> print err
tests/TestMain.hs view
@@ -12,14 +12,21 @@     ls <- getLDIFs ldifDir     runTestTT (tests ls) -tests ls = TestList $ (testCasesParseOK ls) ++ (testCasesDIFF)+tests ls = TestList $ (testCasesParseOK ls) ++ testCasesDIFF ++ testCasesUtils  -- -- Test Cases -- testCasesDIFF = [TestCase (assertEqual "dummy" True True)] testCasesParseOK ls = map (\x -> TestCase (assertParsedOK x)) $ filter (isOK) ls-+testCasesUtils = [ TestCase (assertBool "DN1Root is Prefix of DN2Root" (not $ isDNPrefixOf dn1root dn2root))+                 , TestCase (assertBool "DN1Root is Prefix of DN1Child" (isDNPrefixOf dn1root dn1child))+                 , TestCase (assertBool "DN Size 1" (1 == sizeOfDN dn1root))+                 , TestCase (assertBool "DN Size 2" (2 == sizeOfDN dn1child))]+    where+      dn1root = head $ rights [ parseDNStr "dc=sk" ]+      dn2root = head $ rights [ parseDNStr "dc=de" ]+      dn1child = head $ rights [ parseDNStr "o=green,dc=sk" ] -- -- Support Methods --@@ -38,9 +45,9 @@                            | (isSuffixOf ".content.ldif" name) = assertTypeContent name ldif                            | otherwise = assertFailure $ "Unexpected filename: (not .modify.ldif or .content.ldif " ++ name -assertTypeContent n l@(LDIFContent _ _) = assertBool "Valid Content Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))-assertTypeContent n x = assertFailure $ n ++ " is not type of LDIFContent"+assertTypeContent n l  | (getLDIFType l == LDIFContentType) = assertBool "Valid Content Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))+                       | otherwise          = assertFailure $ n ++ " is not type of LDIFContent" -assertTypeChanges n l@(LDIFChanges _ _) = assertBool "Valid Changes Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))-assertTypeChanges n x = assertFailure $ n ++ " is not type of LDIFChanges"+assertTypeChanges n l | (getLDIFType l /= LDIFContentType) = assertBool "Valid Changes Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))+                      | otherwise          = assertFailure $ n ++ " is not type of LDIFChanges"