add Writer interface to mime/multipart.go

181 views
Skip to first unread message

caine tighe

unread,
May 13, 2011, 2:36:31 PM5/13/11
to golan...@googlegroups.com
it would be nice to have an interface to Writer from multipart.go.
i'm writing to hear what people want to see in it before I go ahead
and do something that would be otherwise short-sighted / random.

thanks

Brad Fitzpatrick

unread,
May 13, 2011, 2:45:31 PM5/13/11
to caine tighe, golan...@googlegroups.com
How about something like....

package multipart

// Writer creates a new multipart section.
type Writer struct {
    Boundary string

    w io.Writer 
}

// NewWriter returns a new Writer with a random boundary.
func NewWriter(w io.Writer) *Writer {
   ...
}

func (w *Writer) Write(p *Part) os.Error {
...
}

func (w *Writer) Close() os.Error {

}

func NewPart(header textproto.MIMEHeader, body io.Reader) *Part {
}

// NewFilePart is a convenience wrapper around NewPart.
func NewFilePart(formName, fileName string, body io.Reader) *Part {

Kyle Lemons

unread,
May 13, 2011, 4:32:58 PM5/13/11
to caine tighe, golan...@googlegroups.com
I assume you're already looking at making a type that's an io.Writer, but I would suggest making it an io.WriteCloser with the "close" semantics be that it ends the current part.

type PartWriter struct {
  Boundary string
  w io.Writer
}

// for io.Writer
func (pw *PartWriter) Write(bytes []byte) (n int, err os.Error) {...}

// for io.WriteCloser
// Close the current part
func (pw *PartWriter) Close() (err os.Error) {
  // collect written data, w.Write() it, and open a new part
}

Depending on your idea for this, you could also make Flush() have the "close this part, start on the next one" semantics and Close() have the expected semantics of closing the underlying WriteCloser.

~Kyle

On Fri, May 13, 2011 at 11:36 AM, caine tighe <arctanof...@gmail.com> wrote:

--
~Kyle

"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?" 
— Brian Kernighan

Brad Fitzpatrick

unread,
May 13, 2011, 6:16:02 PM5/13/11
to Kyle Lemons, caine tighe, golan...@googlegroups.com
On Fri, May 13, 2011 at 1:32 PM, Kyle Lemons <kev...@google.com> wrote:
I assume you're already looking at making a type that's an io.Writer,

No, that's not a goal.  Why would you want to interject raw bytes into a structured message?
 

Brad Fitzpatrick

unread,
May 13, 2011, 6:18:10 PM5/13/11
to caine tighe, golan...@googlegroups.com

Kyle Lemons

unread,
May 13, 2011, 6:26:05 PM5/13/11
to Brad Fitzpatrick, caine tighe, golan...@googlegroups.com
It seems logical that, when creating a multipart MIME message, I might want to png.Encode(message, image).  There are also the use-cases with encryption, base64-encoding, etc.  Many of these things exist already and most of them can write to an io.Writer.  Also, simply implementing io.Writer doesn't imply that you want to be receiving raw bytes... you also don't necessarily want to write raw bytes out to the console, but os.Stdout still implements it.  Implementing the interface makes it in line with the other data consumers in the standard library, binary or plaintext.  You can have WriteString for people and interfaces which know they'll be writing monolithic strings often, but even simply being able to fmt.Fprintf(message, "Format...", ...) seems like it bight be desirable.

Brad Fitzpatrick

unread,
May 13, 2011, 6:35:30 PM5/13/11
to Kyle Lemons, caine tighe, golan...@googlegroups.com
On Fri, May 13, 2011 at 3:26 PM, Kyle Lemons <kev...@google.com> wrote:
On Fri, May 13, 2011 at 3:16 PM, Brad Fitzpatrick <brad...@golang.org> wrote:
On Fri, May 13, 2011 at 1:32 PM, Kyle Lemons <kev...@google.com> wrote:
I assume you're already looking at making a type that's an io.Writer,

No, that's not a goal.  Why would you want to interject raw bytes into a structured message?
 

It seems logical that, when creating a multipart MIME message, I might want to png.Encode(message, image).

Ah, right.  I see what you're saying now.  Rather than confusing definitions of Flush() and Close(), perhaps:

    func (w *Writer) BeginPart(headers textproto.MIMEHeader) io.WriteCloser

We can just return an interface type there, but internally it'd be a &partWriter{...} and would know if it's closed, its boundary, its parent *multipart.Writer, whatever.

Calling BeginPart() again would Close() the previous Part's writer, if not already closed.

That work?

Naming:  BeginPart... NewPart... maybe CreatePart?  Create is the word we use in os.Create now.  I don't like OpenPart, as that implies a read operation. 

Kyle Lemons

unread,
May 13, 2011, 7:12:58 PM5/13/11
to Brad Fitzpatrick, caine tighe, golan...@googlegroups.com
Ah, right.  I see what you're saying now.  Rather than confusing definitions of Flush() and Close(), perhaps:

I agree Flush() would be weird, especially if you don't know the implementation.  Close makes slightly more sense, though, as each part can sort of be considered a file.  But yeah, multiple closes on the same writer is definitely abnormal.
 
    func (w *Writer) BeginPart(headers textproto.MIMEHeader) io.WriteCloser

I'd personally shy away from taking in the headers up front in favor of setting them individually or as a group with a separate method.  Might just be me.  I can't really imagine a scenario in which I open the part without knowing what type it will be, so either way probably works.
 
We can just return an interface type there, but internally it'd be a &partWriter{...} and would know if it's closed, its boundary, its parent *multipart.Writer, whatever.

Calling BeginPart() again would Close() the previous Part's writer, if not already closed.

To kindof hint at that, NextPart() might be a suitable name, though that has a somewhat "reading" sort of feel.  Create would definitely be the file analogue, so would probably be the POLS-preferred name.

Andrew Gerrand

unread,
May 15, 2011, 7:42:55 PM5/15/11
to Brad Fitzpatrick, Kyle Lemons, caine tighe, golan...@googlegroups.com

For the new archive/zip Writer I'm using a Create method on the Writer
type that returns an io.WriteCloser. I don't like the idea of writing
multiple parts to the same Writer - too much state; seems too easy to
accidentally write something at the wrong time. The File abstraction
is a nice one. Open/Create, Read/Write, Close. Just my 2c.

Andrew

Reply all
Reply to author
Forward
0 new messages