git.sophuwu.com > mailboxxer   
              226
            
             package db

import (
	"database/sql"
	"fmt"
	_ "github.com/glebarez/go-sqlite"
	"os"
	"path/filepath"
)

var DBPATH, INBOX, SAVEPATH string

func getHomeBox() string {
	home, err := os.UserHomeDir()
	if err != nil || home == "" {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	return filepath.Join(home, ".mailbox")
}

func getConf() {
	var mailbox string
	if len(os.Args) > 2 && os.Args[1] == "-m" {
		mailbox = os.Args[2]
	} else {
		mailbox = getHomeBox()
	}
	var err error
	if _, err = os.Stat(mailbox); os.IsNotExist(err) {
		os.MkdirAll(mailbox, 0700)
	}
	DBPATH = filepath.Join(mailbox, "mailbox.sqlite")
	INBOX = filepath.Join(mailbox, "inbox", "new")
	if _, err = os.Stat(INBOX); os.IsNotExist(err) {
		os.MkdirAll(INBOX, 0700)
	}
	SAVEPATH = filepath.Join(mailbox, "saved")
	if _, err = os.Stat(SAVEPATH); os.IsNotExist(err) {
		os.MkdirAll(SAVEPATH, 0700)
	}
}

func readRows(rows *sql.Rows) ([]EmailMeta, error) {
	var metas []EmailMeta
	var meta EmailMeta
	var err error
	for rows.Next() {
		err = rows.Scan(&meta.Id, &meta.Subject, &meta.To, &meta.From, &meta.Date)
		if err != nil {
			return metas, err
		}
		metas = append(metas, meta)
	}
	return metas, nil
}

var db *sql.DB

func openDB() error {
	var err error
	db, err = sql.Open("sqlite", DBPATH)
	if err != nil {
		return err
	}
	if db == nil {
		return fmt.Errorf("unknown reason")
	}
	_, err = db.Exec("CREATE TABLE IF NOT EXISTS emails (id TEXT PRIMARY KEY, subject TEXT, toaddr TEXT, fromaddr TEXT, date TEXT)")
	if err != nil {
		return err
	}
	return nil
}

func Open() {
	getConf()
	err := openDB()
	if err != nil {
		fmt.Fprintln(os.Stderr, "error opening database: ", err)
		os.Exit(1)
	}
	err = parseNewMail()
	if err != nil {
		fmt.Fprintln(os.Stderr, "error parsing new mail: ", err)
		os.Exit(1)
		return
	}
}

func Close() {
	if db != nil {
		if err := db.Close(); err != nil {
			fmt.Fprintln(os.Stderr, "error closing database: ", err)
		}
		db = nil
	}
}

type Query struct {
	rows       []EmailMeta
	page       int
	pageSize   int
	totalRows  int
	totalPages int
	where      string
}

func (r *Query) Page() int {
	return r.page + 1
}
func (r *Query) PageSize() int {
	return r.pageSize
}
func (r *Query) TotalRows() int {
	return r.totalRows
}
func (r *Query) TotalPages() int {
	return r.totalPages + 1
}
func (r *Query) Rows() []EmailMeta {
	return r.rows
}
func (r *Query) Row(i int) (EmailMeta, error) {
	if i < 0 || i >= len(r.rows) {
		return EmailMeta{}, fmt.Errorf("index out of range: %d", i)
	}
	return r.rows[i], nil
}
func (r *Query) whereClause(q *string) {
	if r.where != "" {
		*q += " WHERE " + r.where
	}
}
func (r *Query) executeQuery() error {
	if db == nil {
		return fmt.Errorf("database not opened")
	}
	q := `select count(id) from emails`
	r.whereClause(&q)
	row := db.QueryRow(q)
	if row == nil {
		return fmt.Errorf("query error: no rows returned")
	}
	if err := row.Scan(&r.totalRows); err != nil {
		return fmt.Errorf("query error: %w", err)
	}
	if r.pageSize <= 0 {
		r.pageSize = 5
	} else if r.pageSize > 100 {
		r.pageSize = 100
	}
	if r.totalRows == 0 {
		r.rows = []EmailMeta{}
		r.page = 0
		r.totalPages = 0
		return nil
	}
	r.totalPages = (r.totalRows - 1) / r.pageSize
	if r.page < 0 {
		r.page = 0
	} else if r.page > r.totalPages {
		r.page = r.totalPages
	}
	q = `SELECT * FROM emails`
	r.whereClause(&q)
	q += fmt.Sprintf(" ORDER BY date DESC LIMIT %d OFFSET %d", r.pageSize, r.page*r.pageSize)
	rows, err := db.Query(q)
	if err != nil {
		return fmt.Errorf("query error: %w", err)
	}
	r.rows, err = readRows(rows)
	if err != nil {
		return fmt.Errorf("error reading rows: %w", err)
	}
	return nil
}

func (r *Query) Next() error {
	if r.page >= r.totalPages {
		return nil
	}
	r.page++
	return r.executeQuery()
}
func (r *Query) Prev() error {
	if r.page <= 0 {
		return nil
	}
	r.page--
	return r.executeQuery()
}
func (r *Query) SetWhere(where string) error {
	r.where = where
	r.page = 0
	return r.executeQuery()
}
func (r *Query) GetWhere() string {
	return r.where
}
func (r *Query) SetPage(page int) error {
	r.page = page
	return r.executeQuery()
}
func (r *Query) SetPageSize(size int) error {
	if size <= 0 {
		return fmt.Errorf("page size must be greater than zero")
	}
	if size > 100 {
		size = 100
	}
	r.pageSize = size
	return r.executeQuery()
}

func NewQuery(pageSize int) (*Query, error) {
	r := &Query{
		page:     0,
		pageSize: pageSize,
		where:    "",
	}
	if err := r.executeQuery(); err != nil {
		return nil, fmt.Errorf("error executing query: %w", err)
	}
	return r, nil
}