F #, FParsec e chamando um analisador de fluxo recursivamente
Estou desenvolvendo um analisador MIME de várias partes usando F # e FParsec. Estou desenvolvendo iterativamente e, portanto, esse código é altamente não refinado e quebradiço - apenas resolve meu primeiro problema imediato. Vermelho, Verde, Refatorador.
Eu sou obrigado a analisar um fluxo em vez de uma string, o que realmente está me dando um loop. Dada essa restrição, na melhor das hipóteses, preciso chamar um analisador recursivamente. Como fazer isso está além do meu conhecimento, pelo menos da maneira que procedi até agora.
namespace MultipartMIMEParser
open FParsec
open System.IO
type private Post = { contentType : string
; boundary : string
; subtype : string
; content : string }
type MParser (s:Stream) =
let ($) f x = f x
let ascii = System.Text.Encoding.ASCII
let str cs = System.String.Concat (cs:char list)
let q = "\""
let qP = pstring q
let pSemicolon = pstring ";"
let manyNoDoubleQuote = many $ noneOf q
let enquoted = between qP qP manyNoDoubleQuote |>> str
let skip = skipStringCI
let pContentType = skip "content-type: "
>>. manyTill anyChar (attempt $ preturn () .>> pSemicolon)
|>> str
let pBoundary = skip " boundary=" >>. enquoted
let pSubtype = opt $ pSemicolon >>. skip " type=" >>. enquoted
let pContent = many anyChar |>> str // TODO: The content parser needs to recurse on the stream.
let pStream = pipe4 pContentType pBoundary pSubtype pContent
$ fun c b t s -> { contentType=c; boundary=b; subtype=t; content=s }
let result s = match runParserOnStream pStream () "" s ascii with
| Success (r,_,_) -> r
| Failure (e,_,_) -> failwith (sprintf "%A" e)
let r = result s
member p.ContentType = r.contentType
member p.Boundary = r.boundary
member p.ContentSubtype = r.subtype
member p.Content = r.content
A primeira linha do exemplo POST segue:
content-type: Multipart/related; boundary="RN-Http-Body-Boundary"; type="multipart/related"
Ele abrange uma única linha no arquivo. Sub-partes adicionais no conteúdo incluemcontent-type
valores que abrangem várias linhas, por isso sei que precisarei refinar meus analisadores para poder reutilizá-los.
De alguma forma eu tenho que ligarpContent
com os resultados (string?) depBoundary
para que eu possa dividir o restante do fluxo nos limites apropriados e, de alguma forma, retornar várias partes para o conteúdo da postagem, cada uma das quais será uma postagem separada, com cabeçalhos e conteúdo (que obviamente terá que ser outra coisa) que uma string). Minha cabeça está girando. Esse código já parece complexo demais para analisar uma única linha.
Muita apreciação pela perspicácia e sabedoria!