MIME for MoonBit: media types, Content-Disposition and multipart bodies.
moon add moonbitstack/moonmimeimport {
"moonbitstack/moonmime",
}let mt = @moonmime.media_type("Text/HTML; charset=UTF-8")
println(mt.essence) // text/html
println(mt.type_()) // text
println(mt.subtype()) // html
println(mt.param("charset").unwrap_or("")) // UTF-8
println(mt.to_string()) // text/html; charset=UTF-8println(@moonmime.content_disposition("menu.pdf"))
// attachment; filename="menu.pdf"
println(@moonmime.content_disposition("receta \"de la abuela\".pdf"))
// attachment; filename*=utf-8''receta%20%22de%20la%20abuela%22.pdflet d = @moonmime.Disposition::parse(
"attachment; filename=\"fallback.txt\"; filename*=utf-8''r%C3%A9sum%C3%A9.txt",
)
println(d.filename().unwrap_or("")) // résumé.txtprintln(@moonmime.guess_type("photo.jpg")) // image/jpeg
println(@moonmime.guess_type("archive.tar.gz")) // application/gzip
println(@moonmime.guess_extension("image/jpeg").unwrap_or("")) // .jpg// `body` is the raw bytes; `boundary` is the delimiter without its leading dashes.
let parts = @moonmime.multipart(body, boundary)
for part in parts {
println(part.header("content-type").unwrap_or(""))
}
let form = @moonmime.formdata(body, boundary)
let title = form.field("title") // String?
for tag in form.values("tags") { // every value under one name
println(tag)
}
let upload = form.file("photo") // Upload?let subject = @moonmime.encode_words("Grüße aus München") // =?utf-8?B?...?=
let back = @moonmime.decode_words(subject) // Grüße aus München
let body = @qp.encode(b"caf\xc3\xa9 = coffee") // caf=C3=A9 =3D coffee
let text = @qp.decode(body)let charsets : Map[String, &@moonmime.Charset] = { "windows-1252": cp1252 }
let name = @moonmime.decode_words(header, charsets~)| Item | What it does |
|---|---|
| media_type(s) | Parse a Content-Type header into a MediaType. |
| MediaType | type_, subtype, param(name), to_string. |
| guess_type(filename) | The media type a filename's extension implies. |
| guess_extension(type) | The extension people use for a media type. |
| fallback | application/octet-stream. |
| content_disposition(name, inline?) | Build a Content-Disposition header. |
| Disposition | parse, param(name), filename(). |
| multipart(body, boundary, limits?, strict?) | Split a multipart body into Parts. |
| Part | header(name), content_type(), disposition(). |
| formdata(body, boundary, limits?, strict?) | Read multipart/form-data into a Form. |
| Form | field, values, file, entries, uploads, is_empty, new. |
| Upload | size(), header(name). |
| Limits / limits | Bounds on part count and part size. |
| encode_word(text, charset?, encoding?, charsets?) | One RFC 2047 encoded word. |
| encode_words(text, ...) | A header value as encoded words, split to fit; ASCII stays as it is. |
| decode_words(value, charsets?) | Read encoded words back, dropping the folding between them. |
| WordEncoding | Base64 or QuotedPrintable, the B and Q of RFC 2047. |
| Charset | The trait a further charset implements: decode, encode. |
| decode_charset(name~, data~, charsets?) / encode_charset(...) | Bytes to text in a named charset, and back. |
| Refused | Why a body was not read: Parts, Part, Malformed, NotAForm, Charset. |
| @qp.encode(bytes) / @qp.decode(bytes, strict?) | Quoted-printable, from moonbitstack/moonmime/qp. |
let small = @moonmime.Limits::new(parts=8, part_size=64 * 1024)
let form = @moonmime.formdata(body, boundary, limits=small)moon run src/examples/lookup
moon run src/examples/form
moon run src/examples/tourtype Formfn content_disposition(filename : StringView, inline? : Bool) -> Stringtest {
let text = @moonmime.decode_charset(name="iso-8859-1", data=b"\xe9") catch {
_ => ""
}
inspect(text, content="é")
}test {
inspect(@moonmime.decode_words("=?utf-8?B?Q2Fmw6k=?="), content="Café")
}test {
let bytes = @moonmime.encode_charset(name="iso-8859-1", text="é") catch {
_ => b""
}
inspect(bytes == b"\xe9", content="true")
}fn encode_word(text : StringView, charset? : String, encoding? : WordEncoding, charsets? : Map[String, &Charset]) -> String raise Refusedtest {
let word = @moonmime.encode_word("Café") catch { _ => "" }
inspect(@moonmime.decode_words(word[:]), content="Café")
}fn encode_words(text : StringView, charset? : String, encoding? : WordEncoding, column? : Int, max? : Int, charsets? : Map[String, &Charset]) -> String raise Refusedfn guess_extension(value : StringView) -> String?fn guess_type(filename : StringView) -> StringInstall
Download zipMIME for MoonBit: media types, Content-Disposition and multipart bodies.