I have a protocol where each message in the protocol is composed of several headers followed by newlines, and then an extra newline at the end to signal end of headers. Sort of like headers in email, but goes on forever.
To process message by messages I ended up making my own lens below based on Pipes.Parse.span. It is a bit ugly. I wanted to know if there was a more idiomatic way to do this using Pipes.text.line(s), Pipes.Group.groupBy, something else in Pipes.Parse or something else interesting like that.
endline :: Monad m => Lens' (Producer Text m a) (Producer Text m (Producer Text m a)) endline k p0 = fmap join (k (go p0 "")) where go :: Monad m => Producer Text m a -> Text -> Producer Text m (Producer Text m a) go p accum = do x <- lift (next p) case x of Left r -> return (return r) Right ("", p') -> go p' "" Right (txt, p') -> do let (prefix, suffix) = T.breakOn "\n\n" (T.append accum txt) prefixnotrailing = T.dropWhileEnd (== '\n') prefix trailingeol = T.takeWhileEnd (== '\n') prefix if (not . T.null $ prefixnotrailing) then yield prefixnotrailing else return () if T.null suffix then go p' trailingeol else return (yield (T.drop 2 (T.append trailingeol suffix)) >> p')