Skip to content

Commit

Permalink
Merge pull request #19 from zostay/many-date-fields
Browse files Browse the repository at this point in the history
More parsing options for date and address fields, better Get error handling
  • Loading branch information
zostay authored Jan 30, 2023
2 parents d7adacb + 9238908 commit 5ba4b1d
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 99 deletions.
78 changes: 0 additions & 78 deletions Changes

This file was deleted.

67 changes: 67 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
WIP TBD

* On `header.ErrManyFields` failure, `(*header.Header).Get()` now returns the value of the first field found with the error.
* Add a `header.ParseTime()` function which provides the same time parsing facilities that `(*header.Header).GetTime()` and `(*header.Header).GetDate()` use.
* Add a `header.ParseAddressList()`function which provides the same `addr.AddressList` parsing facilities that are built in to the various address parsing methods of `header.Header`.

v2.2.1 2023-01-30

* Bugfix: Fixed a typo in `header.ErrNoSuchField` error message.

v2.2.0 2023-01-30

* Deprecation: The contents of the `walker` package are deprecated and will be removed in a future release. Use the features of the `walk` package instead.
* Add the `walk` package.
* Add `walk.AndProcess` and `walk.Processor` to handle walking a message part tree with the goal of generic message processing.
* Add `walk.AndTransform` and `walk.Transformer` to handle walking a message part tree, which is a process tool that aids with message transformation.
* Add `walk.AndProcessSingle` to process single parts of a message.
* Add `walk.AndProcessMultipart` to process multipart parts of a message.
* Add `message.NewBuffer` to copy message.Part into a message.Buffer.
* Add `message.NewBlankBuffer` to copy `message.Part` into a `message.Buffer`, but without body content or sub-parts.
* Add `(*message.Buffer).AddBuffers` method for convenience.

v2.1.0 2023-01-27

* Deprecation: The `OpaqueAlreadyEncoded()` method should no longer be used. Instead, call `SetEncoded(true)` on a mesage.Buffer and then call `Opaque()` to archive the same result.
* Calls to `message.Buffer`'s `Opaque()` and `Multipart()` methods are now guaranteed to work if called repeatedly.
* Add the `Buffer.SetMultipart` method for setting the `message.BufferMode` of a `message.Buffer` to `message.ModeMultipart` and setting the capacity of the internal parts slice.
* It is now possible to use message.Buffer as a `message.Part` directly.
* Add the `Buffer.SetOpaque` method for setting the `message.BufferMode` of a `message.Buffer` to `message.ModeOpaque`.
* Add a linter to check changelog correctness.
* Add a linter to check for bad patterns in golang.
* Add tools to manage the release process.

v2.0.3 2023-01-22

* Bugfix: Base64 transfer encoding inserts line breaks at column 76 now.
* Bugfix: `AttachmentFile` now sets `Content-disposition` to `attachment` and sets the `filename` parameter correctly.
* Bugfix: Junk at the start of the message is more recoverable than it was. The message parsed without junk will be returned with the `field.BadStartError`.
* Add `Clone()` method to `field.Field`, `header.Base`, and `header.Header`
* More test coverage for transfer encoding support.
* More test coverage for `message.Opaque` features.
* More test coverage for `message.Parse` features.

v2.0.2 2023-01-19

* Docfix: Made some additional correction to examples in the docs.

v2.0.1 2023-01-18

* Docfix: Fixed a missing return in an example in the docs.

v2.0.0 2023-01-18

* Major rewrite of the original. This is the first official, tagged release.
* Provides support for parsing via `message.Parse()`
* Containers for messages are `message.Opaque` for any kind of message or message part and `message.Multipart` for MIME multipart messages and parts.
* Provide `message.Buffer` for creating new messages.
* The header package provides high- and mid-level access to email message headers.
* The field package provides low-level access to email message header fields.
* The transfer package provides transfer encoding support.
* The encoding package enables full charset support (at the cost of a much larger compiled binary).
* The param package provides support for parameterized header field bodies as are used for `Content-type` and `Content-disposition` headers.
* The walker package provides experimental support for iterating through message parts.

v1.0.0 2022-12-05

* Unofficial tagged release.
61 changes: 43 additions & 18 deletions message/header/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,36 @@ func (h *Header) setValue(name string, value any) {
//
// If the named field is not set in the header, it will return an empty string
// with ErrNoSuchField. If there are multiple headers for the given named field,
// it will return ErrManyFields.
// it will return the first value found and return ErrManyFields.
func (h *Header) Get(name string) (string, error) {
ixs := h.GetIndexesNamed(name)
if len(ixs) == 0 {
return "", ErrNoSuchField
}

b := h.GetField(ixs[0]).Body()
if len(ixs) > 1 {
return "", ErrManyFields
return b, ErrManyFields
}

return h.GetField(ixs[0]).Body(), nil
return b, nil
}

// ParseTime is a function that provides the time parsing used by GetTime() and
// GetDate() to parse dates to be used on any field body. This will attempt to
// parse the date using the format specified by RFC 5322 first and fallback to
// parsing it in many other formats.
//
// It either returns a parsed time or the parse error.
func ParseTime(body string) (time.Time, error) {
t, err := mail.ParseDate(body)
if err != nil {
t, err = dateparse.ParseAny(body)
if err != nil {
return t, err
}
}
return t, nil
}

// getTime parses the header body as a date and caches the result.
Expand All @@ -137,12 +155,9 @@ func (h *Header) getTime(name string) (time.Time, error) {
return time.Time{}, err
}

t, err := mail.ParseDate(body)
t, err := ParseTime(body)
if err != nil {
t, err = dateparse.ParseAny(body)
if err != nil {
return t, err
}
return t, err
}

h.setValue(name, t)
Expand Down Expand Up @@ -172,6 +187,24 @@ func (h *Header) GetTime(name string) (time.Time, error) {
return t, nil
}

// ParseAddressList provides the same address parsing functionality build into
// the GetAddressList() and GetAllAddressLists() and can be used to parse any
// field body. It will attempt a strict parse of the email address list.
// However, if that fails, an extremely lenient parsing will be attempted, which
// might result in results that can only be described as "weird" in the effort
// to provide some kind of result. It is so forgiving, it will return some kind
// of value for any input.
//
// It will either return an addr.AddressList or an error describing the parse error.
func ParseAddressList(body string) addr.AddressList {
al, err := addr.ParseEmailAddressList(body)
if err != nil {
al = parseEmailAddressList(body)
}

return al
}

// getAddressList will parse an addr.AddressList out of the field or return an
// error. This falls back onto parseEmailAddressList() if
// addr.ParseEmailAddrList() lets us down.
Expand All @@ -181,11 +214,7 @@ func (h *Header) getAddressList(name string) (addr.AddressList, error) {
return nil, err
}

al, err := addr.ParseEmailAddressList(body)
if err != nil {
al = parseEmailAddressList(body)
}

al := ParseAddressList(body)
h.setValue(name, al)

return al, nil
Expand Down Expand Up @@ -222,11 +251,7 @@ func (h *Header) getAllAddressLists(name string) ([]addr.AddressList, error) {

allAl := make([]addr.AddressList, 0, 10)
for _, b := range bs {
al, err := addr.ParseEmailAddressList(b)
if err != nil {
al = parseEmailAddressList(b)
}

al := ParseAddressList(b)
allAl = append(allAl, al)
}

Expand Down
3 changes: 2 additions & 1 deletion tools/pm/cmd/extract-changelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"

"github.com/zostay/go-email/v2/tools/pm/changes"
"github.com/zostay/go-email/v2/tools/pm/release"
)

var (
Expand All @@ -20,7 +21,7 @@ var (
)

func ExtractChangelog(_ *cobra.Command, args []string) {
r, err := changes.ExtractSection("Changes", args[0])
r, err := changes.ExtractSection(release.GoEmailConfig.Changelog, args[0])
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to read changelog section: %v\n", err)
os.Exit(1)
Expand Down
3 changes: 2 additions & 1 deletion tools/pm/cmd/lint-changelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra"

"github.com/zostay/go-email/v2/tools/pm/changes"
"github.com/zostay/go-email/v2/tools/pm/release"
)

var (
Expand All @@ -27,7 +28,7 @@ func init() {
}

func LintChangelog(_ *cobra.Command, _ []string) {
changelog, err := os.Open("Changes")
changelog, err := os.Open(release.GoEmailConfig.Changelog)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "unable to open Changes file: %v", err)
os.Exit(1)
Expand Down
2 changes: 1 addition & 1 deletion tools/pm/release/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Config struct {
}

var GoEmailConfig = Config{
Changelog: "Changes",
Changelog: "Changes.md",
Owner: "zostay",
Project: "go-email",

Expand Down

0 comments on commit 5ba4b1d

Please sign in to comment.