-- | Module providing pattern matching and capturing on 'Identifier's.-- 'Pattern's come in two kinds:---- * Simple glob patterns, like @foo\/*@;---- * Custom, arbitrary predicates of the type @Identifier -> Bool@.---- They both have advantages and disadvantages. By default, globs are used,-- unless you construct your 'Pattern' using the 'predicate' function.---- A very simple pattern could be, for example, @foo\/bar@. This pattern will-- only match the exact @foo\/bar@ identifier.---- To match more than one identifier, there are different captures that one can-- use:---- * @*@: matches at most one element of an identifier;---- * @**@: matches one or more elements of an identifier.---- Some examples:---- * @foo\/*@ will match @foo\/bar@ and @foo\/foo@, but not @foo\/bar\/qux@;---- * @**@ will match any identifier;---- * @foo\/**@ will match @foo\/bar@ and @foo\/bar\/qux@, but not @bar\/foo@;---- * @foo\/*.html@ will match all HTML files in the @foo\/@ directory.---- The 'capture' function allows the user to get access to the elements captured-- by the capture elements in the pattern.---- Like an 'Identifier', a 'Pattern' also has a type parameter. This is simply-- an extra layer of safety, and can be discarded using the 'castPattern'-- function.--moduleHakyll.Core.Identifier.Pattern(Pattern,castPattern,parseGlob,predicate,list,regex,inGroup,matches,filterMatches,capture,fromCapture,fromCaptures)whereimportData.List(isPrefixOf,inits,tails)importControl.Arrow((&&&),(>>>))importControl.Monad(msum)importData.Maybe(isJust,fromMaybe)importData.Monoid(Monoid,mempty,mappend)importGHC.Exts(IsString,fromString)importText.Regex.PCRE((=~~))importHakyll.Core.Identifier-- | One base element of a pattern--dataGlobComponent=Capture|CaptureMany|LiteralStringderiving(Eq,Show)-- | Type that allows matching on identifiers--dataPatterna=Glob[GlobComponent]|Predicate(Identifiera->Bool)|List[Identifiera]instanceIsString(Patterna)wherefromString=parseGlobinstanceMonoid(Patterna)wheremempty=Predicate(constTrue)p1`mappend`p2=Predicate$\i->matchesp1i&&matchesp2i-- | Discard the phantom type parameter--castPattern::Patterna->PatternbcastPattern(Globg)=GlobgcastPattern(Predicatep)=Predicate$p.castIdentifiercastPattern(Listl)=List$mapcastIdentifierl{-# INLINE castPattern #-}-- | Parse a pattern from a string--parseGlob::String->PatternaparseGlob=Glob.parse'whereparse'str=let(chunk,rest)=break(`elem`"\\*")strincaserestof('\\':x:xs)->Literal(chunk++[x]):parse'xs('*':'*':xs)->Literalchunk:CaptureMany:parse'xs('*':xs)->Literalchunk:Capture:parse'xsxs->Literalchunk:Literalxs:[]-- | Create a 'Pattern' from an arbitrary predicate---- Example:---- > predicate (\i -> matches "foo/*" i && not (matches "foo/bar" i))--predicate::(Identifiera->Bool)->Patternapredicate=Predicate-- | Create a 'Pattern' from a list of 'Identifier's it should match--list::[Identifiera]->Patternalist=List-- | Create a 'Pattern' from a regex---- Example:---- > regex "^foo/[^x]*$--regex::String->Patternaregexstr=predicate$fromMaybeFalse.(=~~str).toFilePath-- | Create a 'Pattern' which matches if the identifier is in a certain group-- (or in no group)--inGroup::MaybeString->PatternainGroupgroup=predicate$(==group).identifierGroup-- | Check if an identifier matches a pattern--matches::Patterna->Identifiera->Boolmatches(Globp)=isJust.capture(Globp)matches(Predicatep)=(p$)matches(Listl)=(`elem`l)-- | Given a list of identifiers, retain only those who match the given pattern--filterMatches::Patterna->[Identifiera]->[Identifiera]filterMatches=filter.matches-- | Split a list at every possible point, generate a list of (init, tail)-- cases. The result is sorted with inits decreasing in length.--splits::[a]->[([a],[a])]splits=inits&&&tails>>>uncurryzip>>>reverse-- | Match a glob against a pattern, generating a list of captures--capture::Patterna->Identifiera->Maybe[String]capture(Globp)(Identifier_i)=capture'picapture__=Nothing-- | Internal verion of 'capture'--capture'::[GlobComponent]->String->Maybe[String]capture'[][]=Just[]-- An empty matchcapture'[]_=Nothing-- No matchcapture'(Literall:ms)str-- Match the literal against the string|l`isPrefixOf`str=capture'ms$drop(lengthl)str|otherwise=Nothingcapture'(Capture:ms)str=-- Match until the next /let(chunk,rest)=break(=='/')strinmsum$[fmap(i:)(capture'ms(t++rest))|(i,t)<-splitschunk]capture'(CaptureMany:ms)str=-- Match everythingmsum$[fmap(i:)(capture'mst)|(i,t)<-splitsstr]-- | Create an identifier from a pattern by filling in the captures with a given-- string---- Example:---- > fromCapture (parseGlob "tags/*") "foo"---- Result:---- > "tags/foo"--fromCapture::Patterna->String->IdentifierafromCapturepattern=fromCapturespattern.repeat-- | Create an identifier from a pattern by filling in the captures with the-- given list of strings--fromCaptures::Patterna->[String]->IdentifierafromCaptures(Globp)=IdentifierNothing.fromCaptures'pfromCaptures_=error$"Hakyll.Core.Identifier.Pattern.fromCaptures: fromCaptures only works "++"on simple globs!"-- | Internally used version of 'fromCaptures'--fromCaptures'::[GlobComponent]->[String]->StringfromCaptures'[]_=memptyfromCaptures'(m:ms)[]=casemofLiterall->l`mappend`fromCaptures'ms[]_->error$"Hakyll.Core.Identifier.Pattern.fromCaptures': "++"identifier list exhausted"fromCaptures'(m:ms)ids@(i:is)=casemofLiterall->l`mappend`fromCaptures'msids_->i`mappend`fromCaptures'msis