gomail

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2026 License: MIT Imports: 11 Imported by: 0

README

gomail

A simple, modular, and extensible email library for Go.

👨‍💻 For Developers/Contributors: See GETTING-STARTED.md for implementation guide and next steps.
📋 Roadmap: See ROADMAP.md for complete feature roadmap and configuration requirements.

Features

  • 🔌 Multiple Providers: SMTP, SendGrid, AWS SES (pluggable architecture)
  • 📧 Simple API: Send emails with minimal boilerplate
  • 🔄 Queue Support: Optional Redis-based queuing with retries
  • 📝 Template Rendering: HTML templates with data binding
  • Email Validation: RFC 5322 compliant validation
  • 🧪 Testable: Interface-based design for easy mocking

Quick Start

Installation
go get github.com/ftrinciante/gomail
Basic SMTP Example
package main

import (
    "context"
    "log"
    
    "github.com/ftrinciante/gomail"
    "github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
    // Configure SMTP provider
    smtpProvider := smtp.New(smtp.Config{
        Host:     "smtp.gmail.com",
        Port:     587,
        Username: "your-email@gmail.com",
        Password: "your-app-password",
        From:     "your-email@gmail.com",
    })
    
    // Create email client
    client := gomail.NewClient(smtpProvider)
    
    // Send email
    err := client.Send(context.Background(), &gomail.Message{
        To:      []string{"recipient@example.com"},
        Subject: "Hello from gomail!",
        Body:    "This is a test email sent using gomail.",
        IsHTML:  false,
    })
    
    if err != nil {
        log.Fatalf("Failed to send email: %v", err)
    }
    
    log.Println("Email sent successfully!")
}
HTML Email
msg := &gomail.Message{
    To:      []string{"recipient@example.com"},
    Subject: "Welcome!",
    Body:    "<h1>Welcome to our service!</h1><p>Thanks for signing up.</p>",
    IsHTML:  true,
}

err := client.Send(context.Background(), msg)
With Templates
// Load templates
client.WithTemplates("./templates/*.html")

// Send from template
data := map[string]interface{}{
    "Name":  "John Doe",
    "Token": "abc123",
}

err := client.SendFromTemplate(context.Background(), "welcome.html", 
    []string{"recipient@example.com"}, "Welcome!", data)
With Queue (Optional)
import (
    "github.com/ftrinciante/gomail/queue/redis"
    "github.com/redis/go-redis/v9"
)

// Create Redis client
redisClient := redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
})

// Create queue
redisQueue := redis.New(redisClient, redis.QueueConfig{
    QueueKey:   "emails:queue",
    MaxRetries: 3,
    RetryDelay: 5 * time.Minute,
})

// Create client with queue
client := gomail.NewClient(smtpProvider, gomail.WithQueue(redisQueue))

// Send async (queues the email)
err := client.SendAsync(context.Background(), msg)

// Start queue processor
go client.ProcessQueue(ctx)

Architecture

gomail
├── Client          # Main client orchestrator
├── Message         # Email message struct
├── provider/       # Email provider implementations
│   ├── smtp/       # SMTP provider
│   ├── sendgrid/   # SendGrid provider (future)
│   └── ses/        # AWS SES provider (future)
├── queue/          # Optional queue implementations
│   └── redis/      # Redis queue
└── template/       # Template rendering

Configuration

SMTP Configuration
smtp.Config{
    Host:     "smtp.gmail.com",  // SMTP server
    Port:     587,                // SMTP port (587 for TLS, 465 for SSL)
    Username: "user@gmail.com",   // SMTP username
    Password: "app-password",     // SMTP password
    From:     "user@gmail.com",   // From address
    UseTLS:   true,               // Use STARTTLS (default: true)
}
Queue Configuration
redis.QueueConfig{
    QueueKey:   "emails:queue",      // Redis key for queue
    MaxRetries: 3,                    // Max retry attempts
    RetryDelay: 5 * time.Minute,      // Delay between retries
}

Testing

Run tests:

go test ./...

With coverage:

go test -cover ./...

Examples

See the examples directory for complete working examples:

Contributing

Contributions welcome! Please open an issue or submit a PR.

For Contributors: See GETTING-STARTED.md for:

  • Where to start
  • Development workflow
  • Commit guidelines
  • Documentation standards
  • Testing requirements

Documentation

License

MIT License - see LICENSE for details.

Roadmap

  • SMTP provider
  • Core email sending
  • Email validation
  • Redis queue support
  • Template rendering
  • SendGrid provider
  • AWS SES provider
  • Attachment support
  • HTML template helpers
  • Rate limiting
  • Metrics/observability hooks

Credits

Extracted and refactored from the home-bills project.

Documentation

Overview

Package gomail provides a universal email library for Go applications.

gomail is designed to be drop-in ready for any project requiring email functionality, supporting both sending and receiving emails through multiple providers with a consistent, developer-friendly API.

Features

  • Multiple providers: SMTP, SendGrid, AWS SES (planned)
  • Email receiving: IMAP, POP3 with markdown output (planned)
  • Queue support: Redis-backed async sending (planned)
  • Template rendering: HTML email templates
  • Configuration agnostic: Your app manages secrets
  • Type-safe API: Compile-time validation

Quick Start

Basic email sending with SMTP:

package main

import (
    "context"
    "log"

    "github.com/ftrinciante/gomail"
    "github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
    // Configure SMTP provider
    provider := smtp.New(&smtp.Config{
        Host:     "smtp.gmail.com",
        Port:     587,
        Username: "your-email@gmail.com",
        Password: "your-app-password",
        From:     "your-email@gmail.com",
        UseTLS:   true,
    })

    // Create email client
    client := gomail.NewClient(provider)

    // Send email
    err := client.Send(context.Background(), &gomail.Message{
        To:      []string{"recipient@example.com"},
        Subject: "Hello from gomail!",
        Body:    "This is a test email.",
        IsHTML:  false,
    })

    if err != nil {
        log.Fatal(err)
    }
}

HTML Emails

Send HTML-formatted emails:

msg := &gomail.Message{
    To:      []string{"user@example.com"},
    Subject: "Welcome!",
    Body:    "<h1>Welcome</h1><p>Thanks for signing up!</p>",
    IsHTML:  true,
}

err := client.Send(ctx, msg)

CC and BCC

Include CC and BCC recipients:

msg := &gomail.Message{
    To:      []string{"primary@example.com"},
    CC:      []string{"cc@example.com"},
    BCC:     []string{"bcc@example.com"},
    Subject: "Team Update",
    Body:    "Update for the team...",
}

Attachments

Send emails with file attachments from disk or memory:

// Attachment from file
msg := &gomail.Message{
    To:      []string{"user@example.com"},
    Subject: "Invoice Attached",
    Body:    "Please find your invoice attached.",
    Attachments: []gomail.Attachment{
        {
            Filename: "invoice.pdf",
            Path:     "./invoices/invoice_123.pdf",
        },
    },
}

// In-memory attachment
csvData := []byte("Name,Email\nJohn,john@example.com")
msg := &gomail.Message{
    To:      []string{"user@example.com"},
    Subject: "Contact List",
    Body:    "Here's the contact list.",
    Attachments: []gomail.Attachment{
        {
            Filename:    "contacts.csv",
            Content:     csvData,
            ContentType: "text/csv",
        },
    },
}

// Multiple attachments
msg := &gomail.Message{
    To:      []string{"user@example.com"},
    Subject: "Project Documents",
    Body:    "Project files attached.",
    Attachments: []gomail.Attachment{
        {Filename: "proposal.pdf", Path: "./proposal.pdf"},
        {Filename: "budget.xlsx", Path: "./budget.xlsx"},
        {Filename: "timeline.docx", Path: "./timeline.docx"},
    },
}

Inline Images

Embed images in HTML emails using Content-ID (CID):

htmlBody := `
    <html>
        <body>
            <h1>Welcome!</h1>
            <img src="cid:logo" alt="Company Logo" />
            <p>Thanks for joining us!</p>
        </body>
    </html>
`

msg := &gomail.Message{
    To:      []string{"user@example.com"},
    Subject: "Welcome Email",
    Body:    htmlBody,
    IsHTML:  true,
    InlineImages: []gomail.InlineImage{
        {
            CID:  "logo",           // Referenced as "cid:logo" in HTML
            Path: "./logo.png",
        },
    },
}

// In-memory inline image
imageData := []byte{ /* PNG or JPEG bytes */ }
msg := &gomail.Message{
    To:      []string{"user@example.com"},
    Subject: "Dynamic Content",
    Body:    `<img src="cid:chart" />`,
    IsHTML:  true,
    InlineImages: []gomail.InlineImage{
        {
            CID:         "chart",
            Content:     imageData,
            ContentType: "image/png",
        },
    },
}

Attachment Limits

gomail enforces the following size limits:

  • Attachments: 25MB per file
  • Inline images: 10MB per file

MIME types are automatically detected from file extensions, or you can specify ContentType explicitly. Supported formats include PDF, images (PNG, JPEG, GIF), Microsoft Office documents, ZIP archives, and more.

Configuration Management

gomail is configuration-agnostic. Your application is responsible for managing SMTP credentials securely:

  • Use environment variables for development
  • Use Docker secrets for production
  • Store in database with encryption (AES-256-GCM recommended)

Example with environment variables:

import "os"

config := smtp.Config{
    Host:     os.Getenv("SMTP_HOST"),
    Port:     587,
    Username: os.Getenv("SMTP_USERNAME"),
    Password: os.Getenv("SMTP_PASSWORD"),
    From:     os.Getenv("SMTP_FROM"),
    UseTLS:   true,
}

Error Handling

gomail provides typed errors for better error handling:

err := client.Send(ctx, msg)
if err != nil {
    switch e := err.(type) {
    case *gomail.ValidationError:
        log.Printf("Invalid message: %v", e)
    case *gomail.SendError:
        log.Printf("Send failed: %v", e)
    case *gomail.TemplateError:
        log.Printf("Template error: %v", e)
    default:
        log.Printf("Unknown error: %v", e)
    }
}

Providers

gomail supports multiple email providers through a common interface. Current and planned providers:

  • SMTP (v0.1.0) - Universal SMTP server support
  • SendGrid (v0.3.0 planned) - SendGrid API
  • AWS SES (v0.4.0 planned) - Amazon Simple Email Service

Switch providers by changing the provider initialization:

// SMTP
provider := smtp.New(smtpConfig)

// SendGrid (planned v0.3.0)
// provider := sendgrid.New(sendgridConfig)

// AWS SES (planned v0.4.0)
// provider := ses.New(sesConfig)

client := gomail.NewClient(provider)

Async Sending with Queue (Planned v0.2.0)

Queue emails for asynchronous sending with retry logic:

// Configure Redis queue (v0.2.0)
redisQueue := redis.New(redisClient, redis.QueueConfig{
    QueueKey:   "emails:outbox",
    MaxRetries: 3,
    RetryDelay: 5 * time.Minute,
})

// Create client with queue
client := gomail.NewClient(
    provider,
    gomail.WithQueue(redisQueue),
)

// Send async (queues the email)
err := client.SendAsync(ctx, msg)

// Start queue processor
go client.ProcessQueue(ctx)

Template Rendering (Planned v0.2.0)

Render HTML emails from templates:

client := gomail.NewClient(
    provider,
    gomail.WithTemplates("./templates/*.html"),
)

err := client.SendFromTemplate(
    ctx,
    "welcome.html",
    []string{"user@example.com"},
    "Welcome!",
    map[string]interface{}{
        "Name": "John",
        "URL":  "https://example.com",
    },
)

Email Receiving (Planned v0.5.0)

Receive and process emails with markdown output:

// Configure IMAP receiver
receiver := imap.New(&imap.Config{
    Host:     "imap.gmail.com",
    Port:     993,
    Username: "notifications@example.com",
    Password: os.Getenv("EMAIL_PASSWORD"),
    UseTLS:   true,
    Mailbox:  "INBOX",
})

// Fetch new emails as markdown files
emails, err := receiver.FetchNew(ctx, &imap.FetchOptions{
    MarkAsRead: true,
    Limit:      50,
})

for _, email := range emails {
    // Email saved to /data/emails/inbox/{id}.md
    processEmail(email.FilePath)
}

Production Considerations

Security:

  • Always use TLS (UseTLS: true) for SMTP
  • Never commit credentials to version control
  • Use app-specific passwords for Gmail
  • Encrypt passwords before database storage (AES-256-GCM)

Configuration:

  • Use Docker secrets in production
  • Validate SMTP settings before saving
  • Implement test email functionality
  • Document required environment variables

Performance:

  • Use async sending for non-critical emails
  • Implement rate limiting for bulk operations
  • Monitor queue depth and processing time
  • Set appropriate timeouts
  • github.com/ftrinciante/gomail/provider/smtp - SMTP provider
  • github.com/ftrinciante/gomail/queue/redis - Redis queue (planned)
  • github.com/ftrinciante/gomail/provider/sendgrid - SendGrid (planned)
  • github.com/ftrinciante/gomail/provider/ses - AWS SES (planned)
  • github.com/ftrinciante/gomail/receiver/imap - IMAP receiver (planned)

Examples

See the examples directory for complete working examples:

  • examples/basic - Simple SMTP email
  • examples/with_queue - Using Redis queue (planned)
  • examples/with_templates - HTML template rendering (planned)

Support

For issues, questions, or contributions, visit: https://github.com/ftrinciante/gomail

Example

Example demonstrates basic email sending with plain text.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Configure SMTP provider
	config := smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "your-password",
		From:     "sender@example.com",
		UseTLS:   true,
	}

	// Create SMTP provider
	provider := smtp.New(&config)

	// Create email client
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create message
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Hello from gomail!",
		Body:    "This is a plain text email sent using the gomail library.",
		IsHTML:  false,
	}

	// Send email
	err := client.Send(context.Background(), msg)
	if err != nil {
		log.Fatalf("Failed to send email: %v", err)
	}

	fmt.Println("Email sent successfully")
}
Example (AttachmentAndInlineImage)

Example_attachmentAndInlineImage demonstrates sending an email with both attachments and inline images.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Create SMTP provider
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "your-email@example.com",
		Password: "your-password",
		From:     "your-email@example.com",
		UseTLS:   true,
	})

	// Create client
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create HTML body with inline image reference
	htmlBody := `
		<html>
			<body>
				<img src="cid:logo" alt="Company Logo" style="width: 200px;" />
				<h1>Monthly Report</h1>
				<p>Please find the monthly report attached.</p>
				<p>Best regards,<br/>The Team</p>
			</body>
		</html>
	`

	// Create message with both attachment and inline image
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Monthly Report - January 2026",
		Body:    htmlBody,
		IsHTML:  true,
		Attachments: []gomail.Attachment{
			{
				Filename: "report_january_2026.pdf",
				Path:     "./testdata/report.pdf",
			},
		},
		InlineImages: []gomail.InlineImage{
			{
				CID:  "logo",
				Path: "./testdata/logo.png",
			},
		},
	}

	// Send email
	if err := client.Send(context.Background(), msg); err != nil {
		log.Printf("Failed to send email: %v", err)
		return
	}

	fmt.Println("Email sent successfully with attachment and inline image")

}
Example (AttachmentFromFile)

Example_attachmentFromFile demonstrates sending an email with a file attachment. This example requires a valid SMTP server and test file to run.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Create SMTP provider
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "your-email@example.com",
		Password: "your-password",
		From:     "your-email@example.com",
		UseTLS:   true,
	})

	// Create client
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create message with file attachment
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Invoice Attached",
		Body:    "Please find the invoice attached.",
		IsHTML:  false,
		Attachments: []gomail.Attachment{
			{
				Filename: "invoice.pdf",
				Path:     "./testdata/invoice.pdf",
			},
		},
	}

	// Send email
	if err := client.Send(context.Background(), msg); err != nil {
		log.Printf("Failed to send email: %v", err)
		return
	}

	fmt.Println("Email sent successfully with attachment")
}
Example (AttachmentInMemory)

Example_attachmentInMemory demonstrates sending an email with an in-memory attachment. This example requires a valid SMTP server to run.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Create SMTP provider
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "your-email@example.com",
		Password: "your-password",
		From:     "your-email@example.com",
		UseTLS:   true,
	})

	// Create client
	client := gomail.NewClient(provider)
	defer client.Close()

	// Generate CSV data in memory
	csvData := []byte("Name,Email,Phone\nJohn Doe,john@example.com,555-1234\nJane Smith,jane@example.com,555-5678")

	// Create message with in-memory attachment
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Contact List",
		Body:    "Please find the contact list attached.",
		IsHTML:  false,
		Attachments: []gomail.Attachment{
			{
				Filename:    "contacts.csv",
				Content:     csvData,
				ContentType: "text/csv",
			},
		},
	}

	// Send email
	if err := client.Send(context.Background(), msg); err != nil {
		log.Printf("Failed to send email: %v", err)
		return
	}

	fmt.Println("Email sent successfully with in-memory attachment")
}
Example (AttachmentValidation)

Example_attachmentValidation demonstrates attachment validation.

package main

import (
	"fmt"

	"github.com/ftrinciante/gomail"
)

func main() {
	// Create message with invalid attachment (no filename)
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Test",
		Body:    "Test",
		Attachments: []gomail.Attachment{
			{
				// Missing Filename - will fail validation
				Path: "./testdata/file.pdf",
			},
		},
	}

	// Validate will fail
	err := msg.Validate()
	if err != nil {
		fmt.Println("Validation failed: filename is required")
	}

}
Example (BulkEmailWithQueue)

Example_bulkEmailWithQueue demonstrates sending bulk emails with retry logic.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"

	goredis "github.com/ftrinciante/gomail/queue/redis"
	"github.com/redis/go-redis/v9"
)

func main() {
	redisClient := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	defer redisClient.Close()

	queue := goredis.New(redisClient, goredis.Config{
		QueueKey:   "emails:bulk:outbox",
		MaxRetries: 5, // More retries for bulk operations
		RetryDelay: 2 * time.Minute,
	})
	defer queue.Close()

	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "password",
		From:     "noreply@example.com",
		UseTLS:   true,
	})

	client := gomail.NewClient(
		provider,
		gomail.WithQueue(queue),
		gomail.WithTemplates("./templates/*.html"),
	)
	defer client.Close()

	ctx := context.Background()

	// Start multiple queue processors for parallel processing
	for i := 0; i < 3; i++ {
		go client.ProcessQueue(ctx)
	}

	// Bulk send
	users := []string{
		"user1@example.com",
		"user2@example.com",
		"user3@example.com",
		// ... more users
	}

	for _, email := range users {
		client.SendFromTemplateAsync(
			ctx,
			"newsletter.html",
			[]string{email},
			"Monthly Newsletter",
			map[string]string{
				"Month": "June",
				"Year":  "2026",
			},
		)
	}

	fmt.Printf("Bulk newsletter sent to %d recipients\n", len(users))
}
Example (CcAndBcc)

Example_ccAndBcc demonstrates sending email with CC and BCC recipients.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	config := smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "your-password",
		From:     "sender@example.com",
		UseTLS:   true,
	}

	provider := smtp.New(&config)
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create message with CC and BCC
	msg := &gomail.Message{
		To:      []string{"primary@example.com"},
		CC:      []string{"cc1@example.com", "cc2@example.com"},
		BCC:     []string{"bcc@example.com"},
		Subject: "Team Update",
		Body:    "This email is sent to primary recipient with CC and BCC.",
		IsHTML:  false,
	}

	err := client.Send(context.Background(), msg)
	if err != nil {
		log.Fatalf("Failed to send email with CC/BCC: %v", err)
	}

	fmt.Println("Email with CC and BCC sent successfully")
}
Example (CustomHeaders)

Example_customHeaders demonstrates adding custom email headers.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	config := smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "your-password",
		From:     "sender@example.com",
		UseTLS:   true,
	}

	provider := smtp.New(&config)
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create message with custom headers
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Custom Headers Example",
		Body:    "This email includes custom headers.",
		IsHTML:  false,
		ReplyTo: "support@example.com",
		Headers: map[string]string{
			"X-Priority":    "1",
			"X-Mailer":      "gomail/0.1.0",
			"X-Campaign-ID": "spring-2026",
		},
	}

	err := client.Send(context.Background(), msg)
	if err != nil {
		log.Fatalf("Failed to send email with custom headers: %v", err)
	}

	fmt.Println("Email with custom headers sent successfully")
}
Example (Html)

Example_html demonstrates sending an HTML email with formatting.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Configure SMTP provider
	config := smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "your-password",
		From:     "sender@example.com",
		UseTLS:   true,
	}

	provider := smtp.New(&config)
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create HTML message
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Welcome to gomail!",
		Body: `
			<html>
			<body>
				<h1>Welcome!</h1>
				<p>This is an <strong>HTML email</strong> with formatting.</p>
				<ul>
					<li>Rich text support</li>
					<li>Inline styles</li>
					<li>Images and links</li>
				</ul>
			</body>
			</html>
		`,
		IsHTML: true,
	}

	err := client.Send(context.Background(), msg)
	if err != nil {
		log.Fatalf("Failed to send HTML email: %v", err)
	}

	fmt.Println("HTML email sent successfully")
}
Example (InlineImage)

Example_inlineImage demonstrates sending an HTML email with an inline image.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Create SMTP provider
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "your-email@example.com",
		Password: "your-password",
		From:     "your-email@example.com",
		UseTLS:   true,
	})

	// Create client
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create HTML body that references the inline image using cid:logo
	htmlBody := `
		<html>
			<body>
				<h1>Welcome to Our Service</h1>
				<p>Here is our company logo:</p>
				<img src="cid:logo" alt="Company Logo" />
				<p>Thank you for choosing us!</p>
			</body>
		</html>
	`

	// Create message with inline image
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Welcome Email",
		Body:    htmlBody,
		IsHTML:  true,
		InlineImages: []gomail.InlineImage{
			{
				CID:  "logo",
				Path: "./testdata/logo.png",
			},
		},
	}

	// Send email
	if err := client.Send(context.Background(), msg); err != nil {
		log.Printf("Failed to send email: %v", err)
		return
	}

	fmt.Println("Email sent successfully with inline image")

}
Example (InlineImageInMemory)

Example_inlineImageInMemory demonstrates sending an HTML email with an in-memory inline image.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Create SMTP provider
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "your-email@example.com",
		Password: "your-password",
		From:     "your-email@example.com",
		UseTLS:   true,
	})

	// Create client
	client := gomail.NewClient(provider)
	defer client.Close()

	// Generate a simple 1x1 PNG in memory (for demonstration)
	// In real use, you would load or generate actual image data
	pngData := []byte{
		0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature
		// ... (minimal PNG data would go here)
	}

	// Create HTML body that references the inline image
	htmlBody := `
		<html>
			<body>
				<h1>Dynamic Image</h1>
				<p>Here is a dynamically generated image:</p>
				<img src="cid:dynamic" alt="Dynamic Image" />
			</body>
		</html>
	`

	// Create message with in-memory inline image
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Dynamic Content Email",
		Body:    htmlBody,
		IsHTML:  true,
		InlineImages: []gomail.InlineImage{
			{
				CID:         "dynamic",
				Content:     pngData,
				ContentType: "image/png",
			},
		},
	}

	// Send email
	if err := client.Send(context.Background(), msg); err != nil {
		log.Printf("Failed to send email: %v", err)
		return
	}

	fmt.Println("Email sent successfully with in-memory inline image")

}
Example (MonitoringQueueHealth)

Example_monitoringQueueHealth demonstrates monitoring queue health while processing emails.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"

	goredis "github.com/ftrinciante/gomail/queue/redis"
	"github.com/redis/go-redis/v9"
)

func main() {
	redisClient := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	defer redisClient.Close()

	queue := goredis.New(redisClient, goredis.Config{
		QueueKey: "emails:outbox",
	})
	defer queue.Close()

	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "password",
		From:     "noreply@example.com",
		UseTLS:   true,
	})

	client := gomail.NewClient(
		provider,
		gomail.WithQueue(queue),
	)
	defer client.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
	defer cancel()

	// Start processor
	go client.ProcessQueue(ctx)

	// Queue some messages
	for i := 0; i < 5; i++ {
		client.SendAsync(ctx, &gomail.Message{
			To:      []string{fmt.Sprintf("user%d@example.com", i)},
			Subject: "Test",
			Body:    "Test body",
		})
	}

	// Monitor queue health
	ticker := time.NewTicker(5 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			health, err := queue.Health(ctx)
			if err != nil {
				log.Printf("Health check failed: %v", err)
				continue
			}

			fmt.Printf("Queue Health - Size: %d, DLQ: %d, Processing: %d\n",
				health.QueueSize, health.DLQSize, health.ProcessingCount)

			// Alert if DLQ has messages
			if health.DLQSize > 0 {
				log.Printf("WARNING: %d messages in dead letter queue", health.DLQSize)
			}
		}
	}
}
Example (MultipleAttachments)

Example_multipleAttachments demonstrates sending an email with multiple attachments. This example requires a valid SMTP server and test files to run.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Create SMTP provider
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "your-email@example.com",
		Password: "your-password",
		From:     "your-email@example.com",
		UseTLS:   true,
	})

	// Create client
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create message with multiple attachments
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Project Documents",
		Body:    "<h1>Project Documents</h1><p>Please find all project documents attached.</p>",
		IsHTML:  true,
		Attachments: []gomail.Attachment{
			{
				Filename: "proposal.pdf",
				Path:     "./testdata/proposal.pdf",
			},
			{
				Filename: "budget.xlsx",
				Path:     "./testdata/budget.xlsx",
			},
			{
				Filename: "timeline.docx",
				Path:     "./testdata/timeline.docx",
			},
		},
	}

	// Send email
	if err := client.Send(context.Background(), msg); err != nil {
		log.Printf("Failed to send email: %v", err)
		return
	}

	fmt.Println("Email sent successfully with multiple attachments")

}
Example (MultipleRecipients)

Example_multipleRecipients demonstrates sending to multiple recipients.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	config := smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "your-password",
		From:     "sender@example.com",
		UseTLS:   true,
	}

	provider := smtp.New(&config)
	client := gomail.NewClient(provider)
	defer client.Close()

	// Create message with multiple recipients
	msg := &gomail.Message{
		To: []string{
			"user1@example.com",
			"user2@example.com",
			"user3@example.com",
		},
		Subject: "Team Announcement",
		Body:    "This message is sent to multiple recipients.",
		IsHTML:  false,
	}

	err := client.Send(context.Background(), msg)
	if err != nil {
		log.Fatalf("Failed to send to multiple recipients: %v", err)
	}

	fmt.Println("Email sent to multiple recipients successfully")
}
Example (QueueWithTemplates)

Example_queueWithTemplates demonstrates combining queue and templates for async templated emails.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"

	goredis "github.com/ftrinciante/gomail/queue/redis"
	"github.com/redis/go-redis/v9"
)

func main() {
	// Create Redis client
	redisClient := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	defer redisClient.Close()

	// Create queue
	queue := goredis.New(redisClient, goredis.Config{
		QueueKey:   "emails:outbox",
		MaxRetries: 3,
		RetryDelay: 5 * time.Minute,
	})
	defer queue.Close()

	// Create SMTP provider
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "password",
		From:     "noreply@example.com",
		UseTLS:   true,
	})

	// Create client with BOTH queue and templates
	client := gomail.NewClient(
		provider,
		gomail.WithQueue(queue),
		gomail.WithTemplates("./templates/*.html"),
	)
	defer client.Close()

	// Start queue processor in background
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	go func() {
		if err := client.ProcessQueue(ctx); err != nil {
			log.Printf("Queue processor: %v", err)
		}
	}()

	// Send templated emails asynchronously
	for i := 1; i <= 10; i++ {
		err := client.SendFromTemplateAsync(
			ctx,
			"welcome.html",
			[]string{fmt.Sprintf("user%d@example.com", i)},
			"Welcome to Our Service!",
			map[string]interface{}{
				"Name":   fmt.Sprintf("User %d", i),
				"Number": i,
			},
		)
		if err != nil {
			log.Printf("Failed to queue email %d: %v", i, err)
		}
	}

	fmt.Println("10 templated emails queued for async sending")
}
Example (Templates)

Example_templates demonstrates sending emails with HTML templates.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Create SMTP provider
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "password",
		From:     "noreply@example.com",
		UseTLS:   true,
	})

	// Create client with templates
	// Templates are loaded from the glob pattern
	client := gomail.NewClient(
		provider,
		gomail.WithTemplates("./templates/*.html"),
	)
	defer client.Close()

	// Template data
	data := map[string]interface{}{
		"Name":     "John Doe",
		"Product":  "Premium Plan",
		"Price":    99,
		"Discount": 10,
	}

	// Send using template
	err := client.SendFromTemplate(
		context.Background(),
		"welcome.html",
		[]string{"john@example.com"},
		"Welcome to Our Service!",
		data,
	)
	if err != nil {
		log.Fatalf("Failed to send templated email: %v", err)
	}

	fmt.Println("Templated email sent")
}
Example (TemplatesAsync)

Example_templatesAsync demonstrates async template email sending.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// This requires queue support
	// See queue/redis examples for full implementation

	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "password",
		From:     "noreply@example.com",
		UseTLS:   true,
	})

	client := gomail.NewClient(
		provider,
		gomail.WithTemplates("./templates/*.html"),
		// gomail.WithQueue(queue), // Add queue for async
	)
	defer client.Close()

	// Send template async (requires queue)
	err := client.SendFromTemplateAsync(
		context.Background(),
		"welcome.html",
		[]string{"user@example.com"},
		"Welcome!",
		map[string]string{"Name": "Charlie"},
	)
	if err != nil {
		log.Printf("Note: Async sending requires queue configuration")
	}

	fmt.Println("Template queued for async sending")
}
Example (TemplatesCustomFunctions)

Example_templatesCustomFunctions demonstrates using custom template functions.

package main

import (
	"fmt"
)

func main() {
	// Create a temporary template file for demonstration
	tmplContent := `
<html>
<body>
	<h1>Invoice for {{.Name}}</h1>
	<p>Product: {{.Product}}</p>
	<p>Price: ${{.Price}}</p>
	<p>Discount: ${{.Discount}}</p>
	<p>Tax (10%): ${{div (mul .Price 10) 100}}</p>
	<p>Total: ${{sub (add .Price (div (mul .Price 10) 100)) .Discount}}</p>
</body>
</html>
`
	// Custom functions available: add, sub, mul, div
	// Example: {{add 5 10}} = 15
	// Example: {{sub 100 20}} = 80
	// Example: {{mul 10 5}} = 50
	// Example: {{div 100 4}} = 25

	_ = tmplContent // Use in actual implementation
	fmt.Println("Template with custom functions")
}
Example (TemplatesFromFiles)

Example_templatesFromFiles demonstrates loading templates from files.

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	// Create temporary template directory
	os.MkdirAll("./templates", 0755)
	defer os.RemoveAll("./templates")

	// Create welcome template
	welcomeHTML := `
<html>
<body>
	<h1>Welcome {{.Name}}!</h1>
	<p>Thank you for joining us.</p>
</body>
</html>
`
	os.WriteFile("./templates/welcome.html", []byte(welcomeHTML), 0644)

	// Create notification template
	notificationHTML := `
<html>
<body>
	<h1>Notification</h1>
	<p>{{.Message}}</p>
</body>
</html>
`
	os.WriteFile("./templates/notification.html", []byte(notificationHTML), 0644)

	// Create client with templates
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "password",
		From:     "noreply@example.com",
		UseTLS:   true,
	})

	client := gomail.NewClient(
		provider,
		gomail.WithTemplates("./templates/*.html"),
	)
	defer client.Close()

	// Send welcome email
	client.SendFromTemplate(
		context.Background(),
		"welcome.html",
		[]string{"newuser@example.com"},
		"Welcome!",
		map[string]string{"Name": "Bob"},
	)

	// Send notification email
	client.SendFromTemplate(
		context.Background(),
		"notification.html",
		[]string{"user@example.com"},
		"New Notification",
		map[string]string{"Message": "You have a new message"},
	)

	fmt.Println("Emails sent from file templates")
}
Example (TemplatesInline)

Example_templatesInline demonstrates inline template definition.

package main

import (
	"context"
	"fmt"
	"html/template"
	"log"

	"github.com/ftrinciante/gomail"
	"github.com/ftrinciante/gomail/provider/smtp"
)

func main() {
	provider := smtp.New(&smtp.Config{
		Host:     "smtp.example.com",
		Port:     587,
		Username: "user@example.com",
		Password: "password",
		From:     "noreply@example.com",
		UseTLS:   true,
	})

	client := gomail.NewClient(provider)
	defer client.Close()

	// Create inline template
	tmpl := template.Must(template.New("welcome").Parse(`
<html>
<body>
	<h1>Hello {{.Name}}!</h1>
	<p>Welcome to our service.</p>
</body>
</html>
`))

	// Set templates directly
	client.SetTemplates(tmpl)

	// Send using template
	err := client.SendFromTemplate(
		context.Background(),
		"welcome",
		[]string{"user@example.com"},
		"Welcome!",
		map[string]string{"Name": "Alice"},
	)
	if err != nil {
		log.Fatalf("Failed to send: %v", err)
	}

	fmt.Println("Inline templated email sent")
}
Example (Validation)

Example_validation demonstrates message validation before sending.

package main

import (
	"fmt"
	"log"

	"github.com/ftrinciante/gomail"
)

func main() {
	// Create a message with validation
	msg := &gomail.Message{
		To:      []string{"recipient@example.com"},
		Subject: "Validation Example",
		Body:    "Testing email validation.",
		IsHTML:  false,
	}

	// Validate the message
	err := msg.Validate()
	if err != nil {
		log.Fatalf("Message validation failed: %v", err)
	}

	fmt.Println("Message validation passed")

	// Example of invalid email
	invalidMsg := &gomail.Message{
		To:      []string{"invalid-email"},
		Subject: "Test",
		Body:    "Body",
	}

	err = invalidMsg.Validate()
	if err != nil {
		fmt.Println("Invalid message detected")
	}
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func SaveEmailAsMarkdown

func SaveEmailAsMarkdown(email *ReceivedEmail, outputDir string) error

SaveEmailAsMarkdown saves a received email as a markdown file with YAML frontmatter. The markdown file includes all email metadata, body content, and attachment information.

func ValidateEmail

func ValidateEmail(email string) error

ValidateEmail validates an email address format using RFC 5322 parsing and additional regex validation.

Types

type Attachment

type Attachment struct {
	Filename    string
	Path        string
	ContentType string
	Content     []byte
}

Attachment represents a file attachment to be sent with an email. Attachments can be loaded from disk (Path) or provided as in-memory bytes (Content). Only one of Path or Content should be set.

func (*Attachment) GetContent

func (a *Attachment) GetContent() ([]byte, error)

GetContent returns the attachment content, loading from Path if necessary.

func (*Attachment) GetContentType

func (a *Attachment) GetContentType() string

GetContentType returns the MIME type of the attachment. If ContentType is set, it returns that value. Otherwise, it attempts to detect the MIME type from the filename or content.

func (*Attachment) Validate

func (a *Attachment) Validate() error

Validate checks if the attachment has valid configuration.

type AttachmentInfo

type AttachmentInfo struct {
	Filename    string
	Path        string
	ContentType string
	ContentID   string
	Data        []byte
	Size        int64
	IsInline    bool
}

AttachmentInfo contains metadata about an email attachment.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the main email client that orchestrates sending emails.

func NewClient

func NewClient(provider EmailSender, opts ...ClientOption) *Client

NewClient creates a new email client with the specified provider and options.

func (*Client) Close

func (c *Client) Close() error

Close closes any resources held by the client (queue connections, etc.).

func (*Client) ProcessQueue

func (c *Client) ProcessQueue(ctx context.Context) error

ProcessQueue starts processing messages from the queue. This should be run in a goroutine. It will block until the context is canceled.

func (*Client) Send

func (c *Client) Send(ctx context.Context, msg *Message) error

Send sends an email message directly using the configured provider.

func (*Client) SendAsync

func (c *Client) SendAsync(ctx context.Context, msg *Message) error

SendAsync queues an email message for asynchronous sending. Requires a queue to be configured via WithQueue option.

func (*Client) SendFromTemplate

func (c *Client) SendFromTemplate(ctx context.Context, templateName string, to []string, subject string, data interface{}) error

SendFromTemplate renders an email template with the provided data and sends it.

func (*Client) SendFromTemplateAsync

func (c *Client) SendFromTemplateAsync(ctx context.Context, templateName string, to []string, subject string, data interface{}) error

SendFromTemplateAsync queues an email rendered from a template.

func (*Client) SetTemplates

func (c *Client) SetTemplates(tmpl *template.Template)

SetTemplates sets the template instance directly (useful for testing).

type ClientOption

type ClientOption func(*Client)

ClientOption is a functional option for configuring the Client.

func WithQueue

func WithQueue(queue Queue) ClientOption

WithQueue configures the client to use a queue for async sending.

func WithTemplates

func WithTemplates(pattern string) ClientOption

WithTemplates loads email templates from the specified glob pattern. Example: "./templates/*.html"

type EmailProcessor

type EmailProcessor func(*ReceivedEmail) error

EmailProcessor is a callback function for processing received emails.

type EmailSender

type EmailSender interface {
	Send(ctx context.Context, msg *Message) error
}

EmailSender is the interface that email providers must implement.

type FetchOptions

type FetchOptions struct {
	// Since only fetches emails received after this date
	Since *time.Time

	// Before only fetches emails received before this date
	Before *time.Time

	// Folder specifies which mailbox/folder to fetch from (default: INBOX)
	Folder string

	// AttachmentDir is where to save attachments
	AttachmentDir string

	// MarkdownDir is where to save markdown files
	MarkdownDir string

	// Limit restricts the number of emails to fetch (0 = no limit)
	Limit int

	// MarkAsRead marks fetched emails as read
	MarkAsRead bool

	// Unseen only fetches unread emails
	Unseen bool

	// SaveAttachments saves attachments to disk
	SaveAttachments bool

	// SaveToMarkdown saves each email as a markdown file
	SaveToMarkdown bool
}

FetchOptions configures how emails are fetched from the mail server.

type InlineImage

type InlineImage struct {
	CID         string
	Path        string
	ContentType string
	Content     []byte
}

InlineImage represents an inline image to be embedded in HTML emails using Content-ID (CID). Inline images are referenced in HTML using cid:<CID>, e.g., <img src="cid:logo">.

func (*InlineImage) GetContent

func (i *InlineImage) GetContent() ([]byte, error)

GetContent returns the inline image content, loading from Path if necessary.

func (*InlineImage) GetContentType

func (i *InlineImage) GetContentType() string

GetContentType returns the MIME type of the inline image. If ContentType is set, it returns that value. Otherwise, it attempts to detect the MIME type from the filename or content.

func (*InlineImage) Validate

func (i *InlineImage) Validate() error

Validate checks if the inline image has valid configuration.

type Logger

type Logger interface {
	// Debug logs a debug-level message with structured fields.
	Debug(ctx context.Context, msg string, fields map[string]interface{})

	// Info logs an info-level message with structured fields.
	Info(ctx context.Context, msg string, fields map[string]interface{})

	// Warn logs a warning-level message with structured fields.
	Warn(ctx context.Context, msg string, fields map[string]interface{})

	// Error logs an error-level message with structured fields.
	Error(ctx context.Context, msg string, fields map[string]interface{})
}

Logger defines an interface for structured logging. Implementations can integrate with zap, logrus, zerolog, or standard log package. This is a v1.0.0 interface - implementations will be provided in future versions.

Example zap implementation:

type ZapLogger struct {
    logger *zap.Logger
}

func (l *ZapLogger) Debug(ctx context.Context, msg string, fields map[string]interface{}) {
    l.logger.Debug(msg, zapFields(fields)...)
}

type Message

type Message struct {
	Headers      map[string]string
	ReplyTo      string
	From         string
	Subject      string
	Body         string
	BCC          []string
	Attachments  []Attachment
	InlineImages []InlineImage
	CC           []string
	To           []string
	RetryCount   int
	MaxRetries   int
	IsHTML       bool
}

Message represents an email message to be sent.

func (*Message) AllRecipients

func (m *Message) AllRecipients() []string

AllRecipients returns all recipient addresses (To, CC, BCC) combined.

func (*Message) Validate

func (m *Message) Validate() error

Validate checks if the message has valid required fields and email addresses.

type MetricsCollector

type MetricsCollector interface {
	// RecordSend records an email send operation.
	// provider: SMTP provider name (smtp, sendgrid, ses, etc.)
	// success: whether the send was successful
	// duration: time taken to send the email
	RecordSend(ctx context.Context, provider string, success bool, duration time.Duration)

	// RecordQueue records queue operation metrics.
	// operation: enqueue, dequeue, retry, dlq
	// queueName: name of the queue
	// success: whether the operation was successful
	RecordQueue(ctx context.Context, operation, queueName string, success bool)

	// SetQueueLength sets the current queue length gauge.
	// queueName: name of the queue
	// length: current number of items in the queue
	SetQueueLength(ctx context.Context, queueName string, length int64)
}

MetricsCollector defines an interface for collecting email metrics. Implementations can integrate with Prometheus, StatsD, or other monitoring systems. This is a v1.0.0 interface - implementations will be provided in future versions.

Example Prometheus implementation:

type PrometheusCollector struct {
    sendTotal      *prometheus.CounterVec
    sendDuration   *prometheus.HistogramVec
    queueLength    *prometheus.GaugeVec
}

func (c *PrometheusCollector) RecordSend(ctx context.Context, provider string, success bool, duration time.Duration) {
    status := "success"
    if !success {
        status = "failure"
    }
    c.sendTotal.WithLabelValues(provider, status).Inc()
    c.sendDuration.WithLabelValues(provider).Observe(duration.Seconds())
}

type NoOpLogger

type NoOpLogger struct{}

NoOpLogger is a no-op implementation of Logger. Used as default when no logger is configured.

func (*NoOpLogger) Debug

func (n *NoOpLogger) Debug(ctx context.Context, msg string, fields map[string]interface{})

Debug is a no-op implementation that does nothing.

func (*NoOpLogger) Error

func (n *NoOpLogger) Error(ctx context.Context, msg string, fields map[string]interface{})

Error is a no-op implementation that does nothing.

func (*NoOpLogger) Info

func (n *NoOpLogger) Info(ctx context.Context, msg string, fields map[string]interface{})

Info is a no-op implementation that does nothing.

func (*NoOpLogger) Warn

func (n *NoOpLogger) Warn(ctx context.Context, msg string, fields map[string]interface{})

Warn is a no-op implementation that does nothing.

type NoOpMetricsCollector

type NoOpMetricsCollector struct{}

NoOpMetricsCollector is a no-op implementation of MetricsCollector. Used as default when no metrics collector is configured.

func (*NoOpMetricsCollector) RecordQueue

func (n *NoOpMetricsCollector) RecordQueue(ctx context.Context, operation, queueName string, success bool)

RecordQueue is a no-op implementation that does nothing.

func (*NoOpMetricsCollector) RecordSend

func (n *NoOpMetricsCollector) RecordSend(ctx context.Context, provider string, success bool, duration time.Duration)

RecordSend is a no-op implementation that does nothing.

func (*NoOpMetricsCollector) SetQueueLength

func (n *NoOpMetricsCollector) SetQueueLength(ctx context.Context, queueName string, length int64)

SetQueueLength is a no-op implementation that does nothing.

type NoOpTracer

type NoOpTracer struct{}

NoOpTracer is a no-op implementation of Tracer. Used as default when no tracer is configured.

func (*NoOpTracer) AddEvent

func (n *NoOpTracer) AddEvent(ctx context.Context, name string, attrs map[string]interface{})

AddEvent is a no-op implementation that does nothing.

func (*NoOpTracer) RecordError

func (n *NoOpTracer) RecordError(ctx context.Context, err error)

RecordError is a no-op implementation that does nothing.

func (*NoOpTracer) StartSpan

func (n *NoOpTracer) StartSpan(ctx context.Context, name string, attrs map[string]interface{}) (newCtx context.Context, endSpan func())

StartSpan is a no-op implementation that returns the context unchanged.

type ObservabilityConfig

type ObservabilityConfig struct {
	// Metrics collector for email operations (optional)
	Metrics MetricsCollector

	// Logger for structured logging (optional)
	Logger Logger

	// Tracer for distributed tracing (optional)
	Tracer Tracer
}

ObservabilityConfig holds observability component implementations. Set these on the Client to enable metrics, logging, and tracing.

Example usage:

client := gomail.NewClient(provider)
client.SetObservability(gomail.ObservabilityConfig{
    Metrics: &myPrometheusCollector,
    Logger:  &myZapLogger,
    Tracer:  &myOTelTracer,
})

type Queue

type Queue interface {
	Enqueue(ctx context.Context, msg *Message) error
	Dequeue(ctx context.Context) (*Message, error)
	Close() error
}

Queue is the interface for email queue implementations.

type QueueError

type QueueError struct {
	Err       error
	Operation string
	Message   string
}

QueueError represents an error that occurred during queue operations.

func (*QueueError) Error

func (e *QueueError) Error() string

func (*QueueError) Unwrap

func (e *QueueError) Unwrap() error

type ReceivedEmail

type ReceivedEmail struct {
	ReceivedAt  time.Time
	Date        time.Time
	Headers     map[string][]string
	InReplyTo   string
	Priority    string
	RawHeaders  string
	FilePath    string
	MessageID   string
	BodyText    string
	Body        string
	ID          string
	From        string
	FromName    string
	ReplyTo     string
	Subject     string
	CC          []string
	BCC         []string
	Flags       []string
	To          []string
	References  []string
	Labels      []string
	Attachments []AttachmentInfo
	UID         uint32
	IsHTML      bool
}

ReceivedEmail represents an email message that has been received and processed. This is separate from Message which is used for sending emails.

func LoadEmailFromMarkdown

func LoadEmailFromMarkdown(filePath string) (*ReceivedEmail, error)

LoadEmailFromMarkdown loads an email from a markdown file. This is useful for re-processing emails that were previously saved.

type Receiver

type Receiver interface {
	// Connect establishes connection to the mail server
	Connect(ctx context.Context) error

	// Disconnect closes the connection to the mail server
	Disconnect() error

	// FetchNew fetches new/unread emails based on options
	FetchNew(ctx context.Context, opts *FetchOptions) ([]*ReceivedEmail, error)

	// FetchAll fetches all emails based on options
	FetchAll(ctx context.Context, opts *FetchOptions) ([]*ReceivedEmail, error)

	// MarkAsRead marks specified emails as read
	MarkAsRead(ctx context.Context, uids []uint32) error

	// Move moves emails to another folder
	Move(ctx context.Context, uids []uint32, destFolder string) error

	// Delete deletes specified emails
	Delete(ctx context.Context, uids []uint32) error

	// Stream monitors for new emails and calls handler for each new email
	Stream(ctx context.Context, handler func(*ReceivedEmail) error) error
}

Receiver is the interface for receiving and fetching emails.

type ReceiverConfig

type ReceiverConfig struct {
	// OutputDir is the base directory for saving emails
	OutputDir string

	// AttachmentDir is the directory for saving attachments
	AttachmentDir string

	// SaveAttachments controls whether to save attachments to disk
	SaveAttachments bool

	// SaveToMarkdown controls whether to save emails as markdown files
	SaveToMarkdown bool
}

ReceiverConfig contains common configuration for email receivers.

type SendError

type SendError struct {
	Err      error
	Provider string
	Message  string
}

SendError represents an error that occurred while sending an email.

func (*SendError) Error

func (e *SendError) Error() string

func (*SendError) Unwrap

func (e *SendError) Unwrap() error

type TemplateError

type TemplateError struct {
	Err      error
	Template string
	Message  string
}

TemplateError represents an error that occurred during template rendering.

func (*TemplateError) Error

func (e *TemplateError) Error() string

func (*TemplateError) Unwrap

func (e *TemplateError) Unwrap() error

type Tracer

type Tracer interface {
	// StartSpan starts a new tracing span.
	// name: operation name (e.g., "smtp.send", "queue.enqueue")
	// attrs: span attributes (provider, recipient count, etc.)
	// Returns a new context and a function to end the span.
	StartSpan(ctx context.Context, name string, attrs map[string]interface{}) (context.Context, func())

	// AddEvent adds an event to the current span.
	// name: event name (e.g., "authentication_success", "tls_handshake")
	// attrs: event attributes
	AddEvent(ctx context.Context, name string, attrs map[string]interface{})

	// RecordError records an error in the current span.
	RecordError(ctx context.Context, err error)
}

Tracer defines an interface for distributed tracing. Implementations can integrate with OpenTelemetry, Jaeger, Zipkin, or AWS X-Ray. This is a v1.0.0 interface - implementations will be provided in future versions.

Example OpenTelemetry implementation:

type OTelTracer struct {
    tracer trace.Tracer
}

func (t *OTelTracer) StartSpan(ctx context.Context, name string, attrs map[string]interface{}) (context.Context, func()) {
    ctx, span := t.tracer.Start(ctx, name)
    for k, v := range attrs {
        span.SetAttributes(attribute.String(k, fmt.Sprint(v)))
    }
    return ctx, func() { span.End() }
}

type ValidationError

type ValidationError struct {
	Field   string
	Message string
}

ValidationError represents an email validation error.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Directories

Path Synopsis
examples
basic command
Package provider defines the interface for email providers.
Package provider defines the interface for email providers.
sendgrid
Package sendgrid provides a SendGrid API-based email provider for the gomail library.
Package sendgrid provides a SendGrid API-based email provider for the gomail library.
ses
Package ses provides an AWS SES (Simple Email Service) provider for the gomail library.
Package ses provides an AWS SES (Simple Email Service) provider for the gomail library.
smtp
Package smtp provides an SMTP email provider implementation for gomail.
Package smtp provides an SMTP email provider implementation for gomail.
queue
redis
Package redis provides a Redis-backed queue implementation for gomail.
Package redis provides a Redis-backed queue implementation for gomail.
receiver
imap
Package imap provides an IMAP-based email receiver implementation for the gomail library.
Package imap provides an IMAP-based email receiver implementation for the gomail library.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL