-- Copyright (C) 2006 David Roundy -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2, or (at your option) -- any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; see the file COPYING. If not, write to -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. module Darcs.Patch.PatchInfoAnd ( Hopefully , PatchInfoAnd , PatchInfoAndG , piap , n2pia , patchInfoAndPatch , fmapPIAP , fmapFLPIAP , hopefully , info , hopefullyM , createHashed , extractHash , actually , unavailable , patchDesc ) where import Darcs.Prelude import Control.Exception ( Exception, throw ) import System.IO.Unsafe ( unsafeInterleaveIO ) import Data.Typeable ( Typeable ) import Darcs.Patch.Apply ( Apply(..) ) import Darcs.Patch.Commute ( Commute(..) ) import Darcs.Patch.Conflict ( Conflict(..) ) import Darcs.Patch.Debug ( PatchDebug(..) ) import Darcs.Patch.Effect ( Effect(..) ) import Darcs.Patch.FileHunk ( IsHunk(..) ) import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.FromPrim ( PrimPatchBase(..) ) import Darcs.Patch.Ident ( Ident(..), PatchId ) import Darcs.Patch.Info ( PatchInfo, displayPatchInfo, justName, showPatchInfo ) import Darcs.Patch.Inspect ( PatchInspect(..) ) import Darcs.Patch.Merge ( CleanMerge(..), Merge(..) ) import Darcs.Patch.Named ( Named, fmapFL_Named ) import Darcs.Patch.Read ( ReadPatch(..) ) import Darcs.Patch.Repair ( Repair(..), RepairToFL ) import Darcs.Patch.Show ( ShowPatch(..) ) import Darcs.Patch.Show ( ShowContextPatch(..), ShowPatchBasic(..) ) import Darcs.Patch.Summary ( Summary ) import Darcs.Patch.Witnesses.Eq ( Eq2(..) ) import Darcs.Patch.Witnesses.Ordered ( (:/\:)(..) , (:>)(..) , (:\/:)(..) , FL , mapFL , mapRL_RL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), mapSeal, seal ) import Darcs.Patch.Witnesses.Show ( Show1, Show2 ) import Darcs.Util.Exception ( prettyException ) import Darcs.Util.Printer ( Doc, renderString, text, vcat, ($$) ) import Darcs.Util.SignalHandler ( catchNonSignal ) import Darcs.Util.ValidHash ( PatchHash ) -- | @'Hopefully' p C@ @(x y)@ is @'Either' String (p C@ @(x y))@ in a -- form adapted to darcs patches. The @C@ @(x y)@ represents the type -- witness for the patch that should be there. The @Hopefully@ type -- just tells whether we expect the patch to be hashed or not, and -- 'SimpleHopefully' does the real work of emulating -- 'Either'. @Hopefully sh@ represents an expected unhashed patch, and -- @Hashed hash sh@ represents an expected hashed patch with its hash. data Hopefully a wX wY = Hopefully (SimpleHopefully a wX wY) | Hashed PatchHash (SimpleHopefully a wX wY) deriving Show -- | @SimpleHopefully@ is a variant of @Either String@ adapted for -- type witnesses. @Actually@ is the equivalent of @Right@, while -- @Unavailable@ is @Left@. data SimpleHopefully a wX wY = Actually (a wX wY) | Unavailable String deriving Show type PatchInfoAnd p = PatchInfoAndG (Named p) -- | @'PatchInfoAnd' p wA wB@ represents a hope we have to get a -- patch through its info. We're not sure we have the patch, but we -- know its info. data PatchInfoAndG p wA wB = PIAP !PatchInfo (Hopefully p wA wB) deriving (Show) fmapH :: (a wX wY -> b wW wZ) -> Hopefully a wX wY -> Hopefully b wW wZ fmapH f (Hopefully sh) = Hopefully (ff sh) where ff (Actually a) = Actually (f a) ff (Unavailable e) = Unavailable e fmapH f (Hashed _ sh) = Hopefully (ff sh) where ff (Actually a) = Actually (f a) ff (Unavailable e) = Unavailable e info :: PatchInfoAndG p wA wB -> PatchInfo info (PIAP i _) = i patchDesc :: forall p wX wY . PatchInfoAnd p wX wY -> String patchDesc p = justName $ info p -- | @'piap' i p@ creates a PatchInfoAnd containing p with info i. piap :: PatchInfo -> p wA wB -> PatchInfoAndG p wA wB piap i p = PIAP i (Hopefully $ Actually p) -- | @n2pia@ creates a PatchInfoAnd representing a @Named@ patch. n2pia :: (Ident p, PatchId p ~ PatchInfo) => p wX wY -> PatchInfoAndG p wX wY n2pia x = ident x `piap` x patchInfoAndPatch :: PatchInfo -> Hopefully p wA wB -> PatchInfoAndG p wA wB patchInfoAndPatch = PIAP fmapFLPIAP :: (FL p wX wY -> FL q wX wY) -> PatchInfoAnd p wX wY -> PatchInfoAnd q wX wY fmapFLPIAP f (PIAP i hp) = PIAP i (fmapH (fmapFL_Named f) hp) fmapPIAP :: (p wX wY -> q wX wY) -> PatchInfoAndG p wX wY -> PatchInfoAndG q wX wY fmapPIAP f (PIAP i hp) = PIAP i (fmapH f hp) -- | @'hopefully' hp@ tries to get a patch from a 'PatchInfoAnd' -- value. If it fails, it outputs an error \"failed to read patch: -- \\". We get the description of the patch -- from the info part of 'hp' hopefully :: PatchInfoAndG p wA wB -> p wA wB hopefully = conscientiously $ \e -> text "failed to read patch:" $$ e -- | Using a special exception type here means that is is treated as -- regular failure, and not as a bug in Darcs. data PatchNotAvailable = PatchNotAvailable Doc deriving Typeable instance Exception PatchNotAvailable instance Show PatchNotAvailable where show (PatchNotAvailable e) = renderString e -- | @'conscientiously' er hp@ tries to extract a patch from a 'PatchInfoAnd'. -- If it fails, it applies the error handling function @er@ to a description -- of the patch info component of @hp@. -- Note: this function must be lazy in its second argument, which is why we -- use a lazy pattern match. conscientiously :: (Doc -> Doc) -> PatchInfoAndG p wA wB -> p wA wB conscientiously er ~(PIAP pinf hp) = case hopefully2either hp of Right p -> p Left e -> throw $ PatchNotAvailable $ er (displayPatchInfo pinf $$ text e) -- | Return 'Just' the patch content or 'Nothing' if it is unavailable. hopefullyM :: PatchInfoAndG p wA wB -> Maybe (p wA wB) hopefullyM (PIAP _ hp) = case hopefully2either hp of Right p -> return p Left _ -> Nothing -- Any recommendations for a nice adverb to name the below? hopefully2either :: Hopefully a wX wY -> Either String (a wX wY) hopefully2either (Hopefully (Actually p)) = Right p hopefully2either (Hashed _ (Actually p)) = Right p hopefully2either (Hopefully (Unavailable e)) = Left e hopefully2either (Hashed _ (Unavailable e)) = Left e actually :: a wX wY -> Hopefully a wX wY actually = Hopefully . Actually createHashed :: PatchHash -> (PatchHash -> IO (Sealed (a wX))) -> IO (Sealed (Hopefully a wX)) createHashed h f = mapSeal (Hashed h) `fmap` unsafeInterleaveIO (f' `catchNonSignal` handler) where f' = do Sealed x <- f h return (Sealed (Actually x)) handler e = return $ seal $ Unavailable $ prettyException e extractHash :: PatchInfoAndG p wA wB -> Either (p wA wB) PatchHash extractHash (PIAP _ (Hashed sh _)) = Right sh extractHash hp = Left $ conscientiously (\e -> text "unable to read patch:" $$ e) hp unavailable :: String -> Hopefully a wX wY unavailable = Hopefully . Unavailable -- * Instances defined only for PatchInfoAnd instance Show2 p => Show1 (PatchInfoAnd p wX) instance Show2 p => Show2 (PatchInfoAnd p) instance RepairToFL p => Repair (PatchInfoAnd p) where applyAndTryToFix p = do mp' <- applyAndTryToFix $ hopefully p case mp' of Nothing -> return Nothing Just (e,p') -> return $ Just (e, n2pia p') -- * Instances defined for PatchInfoAndG instance PrimPatchBase p => PrimPatchBase (PatchInfoAndG p) where type PrimOf (PatchInfoAndG p) = PrimOf p getHopefully :: Hopefully p wX wY -> SimpleHopefully p wX wY getHopefully (Hashed _ x) = x getHopefully (Hopefully x) = x instance Eq2 p => Eq2 (SimpleHopefully p) where Actually p1 `unsafeCompare` Actually p2 = p1 `unsafeCompare` p2 _ `unsafeCompare` _ = error "cannot compare unavailable patches" instance Eq2 p => Eq2 (Hopefully p) where Hashed h1 _ `unsafeCompare` Hashed h2 _ = h1 == h2 hp1 `unsafeCompare` hp2 = getHopefully hp1 `unsafeCompare` getHopefully hp2 instance Eq2 p => Eq2 (PatchInfoAndG p) where PIAP i1 p1 `unsafeCompare` PIAP i2 p2 = i1 == i2 && p1 `unsafeCompare` p2 type instance PatchId (PatchInfoAndG p) = PatchInfo instance Ident (PatchInfoAndG p) where ident (PIAP i _) = i instance PatchListFormat (PatchInfoAndG p) instance ShowPatchBasic p => ShowPatchBasic (PatchInfoAndG p) where showPatch f (PIAP n p) = case hopefully2either p of Right x -> showPatch f x Left _ -> showPatchInfo f n instance ShowContextPatch p => ShowContextPatch (PatchInfoAndG p) where showPatchWithContextAndApply f (PIAP n p) = case hopefully2either p of Right x -> showPatchWithContextAndApply f x Left _ -> return $ showPatchInfo f n instance (Summary p, PatchListFormat p, ShowPatch p) => ShowPatch (PatchInfoAndG p) where description (PIAP n _) = displayPatchInfo n summary (PIAP _ p) = case hopefully2either p of Right x -> summary x Left _ -> text $ "[patch summary is unavailable]" summaryFL = vcat . mapFL summary content (PIAP _ p) = case hopefully2either p of Right x -> content x Left _ -> text $ "[patch content is unavailable]" instance (PatchId p ~ PatchInfo, Commute p) => Commute (PatchInfoAndG p) where commute (x :> y) = do y' :> x' <- commute (hopefully x :> hopefully y) return $ (ident y `piap` y') :> (ident x `piap` x') instance (PatchId p ~ PatchInfo, CleanMerge p) => CleanMerge (PatchInfoAndG p) where cleanMerge (x :\/: y) | ident x == ident y = error "cannot cleanMerge identical PatchInfoAndG" | otherwise = do y' :/\: x' <- cleanMerge (hopefully x :\/: hopefully y) return $ (ident y `piap` y') :/\: (ident x `piap` x') instance (PatchId p ~ PatchInfo, Merge p) => Merge (PatchInfoAndG p) where merge (x :\/: y) | ident x == ident y = error "cannot merge identical PatchInfoAndG" | otherwise = case merge (hopefully x :\/: hopefully y) of y' :/\: x' -> (ident y `piap` y') :/\: (ident x `piap` x') instance PatchInspect p => PatchInspect (PatchInfoAndG p) where listTouchedFiles = listTouchedFiles . hopefully hunkMatches f = hunkMatches f . hopefully instance Apply p => Apply (PatchInfoAndG p) where type ApplyState (PatchInfoAndG p) = ApplyState p apply = apply . hopefully unapply = unapply . hopefully instance ( ReadPatch p, Ident p, PatchId p ~ PatchInfo ) => ReadPatch (PatchInfoAndG p) where readPatch' = mapSeal n2pia <$> readPatch' instance Effect p => Effect (PatchInfoAndG p) where effect = effect . hopefully instance IsHunk (PatchInfoAndG p) where isHunk _ = Nothing instance PatchDebug p => PatchDebug (PatchInfoAndG p) instance (Commute p, Conflict p, Summary p, PrimPatchBase p, PatchListFormat p, ShowPatch p) => Conflict (PatchInfoAnd p) where isConflicted = isConflicted . hopefully -- Note: this relies on the laziness of 'hopefully' for efficiency -- and correctness in the face of lazy repositories resolveConflicts context patches = resolveConflicts (mapRL_RL hopefully context) (mapRL_RL hopefully patches)