;;; pass-coffin.el --- Interface to "pass coffin" -*- lexical-binding: t -*-

;; Copyright (C) 2025 Robert Charusta

;; Author: Robert Charusta <rch-public@posteo.net>
;; Maintainer: Robert Charusta <rch-public@posteo.net>
;; URL: https://codeberg.org/rch/emacs-pass-coffin
;; Package-Version: 1.0.0
;; Package-Revision: 202c9e3b67c5
;; Keywords: tools
;; Package-Requires: ((emacs "28.1"))

;; This file is NOT part of GNU Emacs.

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see
;; <https://www.gnu.org/licenses/>.

;;; Commentary:

;; pass-coffin.el provides a small Emacs interface to the external
;; `pass-coffin' ("pass coffin") command-line utility (an extension
;; around `password-store' ("pass").
;;
;; The commands in this package are thin wrappers that call the `pass`
;; executable (e.g., `pass open`, `pass close`, `pass timer`) and show
;; the resulting output in the echo area.  ANSI escape sequences are
;; stripped before display.
;;
;; Provided interactive commands:
;; - `pass-coffin-open'       - Run "pass open"
;;                              (optionally with -t TIME).
;; - `pass-coffin-open-timer' - Prompt for a duration, then run "pass
;;                              open".
;; - `pass-coffin-close'      - Run "pass close".
;; - `pass-coffin-timer'      - Run "pass timer".
;; - `pass-coffin-timer-stop' - Run "pass timer stop".
;;
;; Requirements:
;; - The `pass' program must be installed and available in PATH.
;; - The underlying `pass-coffin' utility is available at:
;;   https://codeberg.org/rch/pass-coffin
;;
;; Note: These commands invoke the external `pass` program.

;;; Code:

(require 'ansi-color)
(require 'subr-x)

(defgroup pass-coffin nil
  "Emacs interface to pass-coffin."
  :group 'tools)

(defcustom pass-coffin-pass-program "pass"
  "Name/path of the `pass` executable."
  :type 'string)

(defun pass-coffin--strip-ansi (string)
  "Remove ANSI color codes and trailing newline from STRING."
  (string-trim (ansi-color-filter-apply string)))

(defun pass-coffin--run-pass (&rest args)
  "Run `pass' with ARGS and return its output as a string.
Signal an error if `pass' exits non-zero."
  (unless (executable-find pass-coffin-pass-program)
    (error "Cannot find executable: %s" pass-coffin-pass-program))
  (with-temp-buffer
    (let* ((exit (apply #'process-file
                        pass-coffin-pass-program
                        nil (list t t) nil args))
           (out (pass-coffin--strip-ansi (buffer-string))))
      (unless (zerop exit)
        (error "Pass failed (exit %s): %s" exit (string-trim out)))
      out)))

;;;###autoload
(defun pass-coffin-open (&optional time)
  "Run `pass open` and display the output.
If TIME is non-nil, run `pass open -t TIME` instead."
  (interactive)
  (let ((output (if time
                    (pass-coffin--run-pass "open" "-t" time)
                  (pass-coffin--run-pass "open"))))
    (message "%s" (pass-coffin--strip-ansi output))))

;;;###autoload
(defun pass-coffin-open-timer (&optional duration)
  "Prompt for a duration and run `pass open -t DURATION`.
If DURATION is empty or nil, run `pass open` without the `-t` option."
  (interactive
   (list
    (read-string "Enter duration (e.g., 6s, 1h; empty = indefinite): ")))
  (let* ((duration (and duration (string-trim duration)))
         (output (if (or (null duration) (string-empty-p duration))
                     (pass-coffin--run-pass "open")
                   (pass-coffin--run-pass "open" "-t" duration))))
    (message "%s" (pass-coffin--strip-ansi output))))

;;;###autoload
(defun pass-coffin-close ()
  "Run the command `pass close` and display the output."
  (interactive)
  (let ((output (pass-coffin--run-pass "close")))
    (message "%s" (pass-coffin--strip-ansi output))))

;;;###autoload
(defun pass-coffin-timer ()
  "Run the command `pass timer` and display the output."
  (interactive)
  (let ((output (pass-coffin--run-pass "timer")))
    (message "%s" (pass-coffin--strip-ansi output))))

;;;###autoload
(defun pass-coffin-timer-stop ()
  "Run the command `pass timer stop` and display the output."
  (interactive)
  (let ((output (pass-coffin--run-pass "timer" "stop")))
    (message "%s" (pass-coffin--strip-ansi output))))

(provide 'pass-coffin)
;;; pass-coffin.el ends here
