envelope.go
func EnvelopeFromPart(root *Part) (*Envelope, error) {
	e := &Envelope{
		Root:   root,
		header: &root.Header,
	}

	if detectMultipartMessage(root) {
		// Multi-part message (message with attachments, etc)
		if err := parseMultiPartBody(root, e); err != nil {
			return nil, err
		}
	} else {
		if detectBinaryBody(root) {
			// Attachment only, no text
			if root.Disposition == cdInline {
				e.Inlines = append(e.Inlines, root)
			} else {
				e.Attachments = append(e.Attachments, root)
			}
		} else {
			// Only text, no attachments
			if err := parseTextOnlyBody(root, e); err != nil {
				return nil, err
			}
		}
	}

	// Down-convert HTML to text if necessary
	if e.Text == "" && e.HTML != "" {
		// We always warn when this happens
		e.Root.addWarning(
			ErrorPlainTextFromHTML,
			"Message did not contain a text/plain part")
		var err error
		if e.Text, err = html2text.FromString(e.HTML); err != nil {
			// Downcoversion shouldn't fail
			e.Text = ""
			p := e.Root.BreadthMatchFirst(matchHTMLBodyPart)
			p.addError(
				ErrorPlainTextFromHTML,
				"Failed to downconvert HTML: %v",
				err)
		}
	}

	// Copy part errors into Envelope.
	if e.Root != nil {
		_ = e.Root.DepthMatchAll(func(part *Part) bool {
			// Using DepthMatchAll to traverse all parts, don't care about result.
			for i := range part.Errors {
				// Range index is needed to get the correct address, because range value points to
				// a locally scoped variable.
				e.Errors = append(e.Errors, part.Errors[i])
			}
			return false
		})
	}

	return e, nil
}
func EnvelopeFromPart(root *Part) (*Envelope, error) {
	e := &Envelope{
		Root:   root,
		header: &root.Header,
	}

	if detectMultipartMessage(root) {
		// Multi-part message (message with attachments, etc)
		if err := parseMultiPartBody(root, e); err != nil {
			return nil, err
		}
	} else {
		if detectBinaryBody(root) {
			// Attachment only, no text
			if root.Disposition == cdInline {
				e.Inlines = append(e.Inlines, root)
			} else {
				e.Attachments = append(e.Attachments, root)
			}
		} else {
			// Only text, no attachments
			if err := parseTextOnlyBody(root, e); err != nil {
				return nil, err
			}
		}
	}

	// Down-convert HTML to text if necessary
	if e.Text == "" && e.HTML != "" {
		// We always warn when this happens
		e.Root.addWarning(
			ErrorPlainTextFromHTML,
			"Message did not contain a text/plain part")
		var err error
		if e.Text, err = html2text.FromString(e.HTML); err != nil {
			// Downcoversion shouldn't fail
			e.Text = ""
			p := e.Root.BreadthMatchFirst(matchHTMLBodyPart)
			p.addError(
				ErrorPlainTextFromHTML,
				"Failed to downconvert HTML: %v",
				err)
		}
	}

	// Copy part errors into Envelope.
	if e.Root != nil {
		_ = e.Root.DepthMatchAll(func(part *Part) bool {
			// Using DepthMatchAll to traverse all parts, don't care about result.
			for i := range part.Errors {
				// Range index is needed to get the correct address, because range value points to
				// a locally scoped variable.
				e.Errors = append(e.Errors, part.Errors[i])
			}
			return false
		})
	}

	return e, nil
}
// EnvelopeFromPart uses the provided Part tree to build an Envelope, downconverting HTML to plain
// text if needed, and sorting the attachments, inlines and other parts into their respective
// slices.  Errors are collected from all Parts and placed into the Envelopes Errors slice.