;;; rutils-renv.el --- R renv with transient           -*- lexical-binding: t; -*-

;; Copyright (C) 2021-2024  Shuguang Sun

;; Author: Shuguang Sun <shuguang79@qq.com>
;; Created: 2021/06/25
;; URL: https://github.com/ShuguangSun/rutils.el
;; Package-Requires: ((emacs "27.1") (ess "19") (transient "0.4"))
;; Keywords: convenience

;; 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:

;; Transient for renv for R.  Every command is generated by
;; `rutils-define-rcommand' from an explicit spec passed to it.

;;; Code:

(eval-when-compile
  (require 'cl-lib)
  (require 'subr-x))
(require 'rutils-lib)

(defgroup rutils-renv nil
  "R renv utilities with transient menu."
  :group 'rutils
  :prefix "rutils-renv")


(defcustom rutils-renv-repos nil
  "Default repository to pass to renv commands as `repos'.
When non-nil it is used as the default value of `repos' arguments."
  :group 'rutils-renv
  :type '(choice (const :tag "Use R default" nil)
                 (string :tag "Repository URL")))


;;; * Generated commands

;;;###autoload (autoload 'rutils-renv-init "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-init
  :r-call "renv::init"
  :group "Renv::"
  :key "i"
  :doc "R renv::init."
  :args
  ((project :key "p" :type directory :arg "--project="
            :desc "The project directory. Default NULL, current getwd().")
   (profile :key "P" :type string :arg "--profile="
            :desc "The profile to be activated. Default NULL.")
   (repos   :key "r" :type string :arg "--repos="
            :desc "The R package repository. Default getOption(\"repos\").")
   (bare    :key "B" :type boolean :arg "--bare"
            :desc "Bare? Default FALSE")
   (force   :key "F" :type boolean :arg "--force"
            :desc "Force? Default FALSE")
   (restart :key "R" :type boolean :arg "--restart"
            :desc "Restart? Default interactive()")))

;;;###autoload (autoload 'rutils-renv-snapshot "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-snapshot
  :r-call "renv::snapshot"
  :group "Renv::"
  :key "s"
  :doc "R renv::snapshot."
  :args
  ((project  :key "p" :type directory :arg "--project="
             :desc "The project directory. Default current getwd().")
   (lockfile :key "l" :type file :arg "--lockfile="
             :desc "The path to the lockfile. Default `renv.lock'.")
   (packages :key "P" :type package :arg "--packages="
             :desc "The packages to snapshot. Default all used packages.")
   (exclude  :key "x" :type string :arg "--exclude="
             :desc "Packages to be excluded." :multi-value t)
   (type     :key "t" :type choice :arg "--type="
             :desc "The type of snapshot. Default `implicit'."
             :choices ("implicit" "all" "explicit" "custom"))
   (repos    :key "r" :type string :arg "--repos="
             :desc "The R package repository. Default getOption(\"repos\").")
   (update   :key "u" :type boolean :arg "--update"
             :desc "Update the lockfile? Default FALSE")
   (force    :key "F" :type boolean :arg "--force"
             :desc "Force? Default FALSE")
   (reprex   :key "R" :type boolean :arg "--reprex"
             :desc "Reprex? Default FALSE")))

;;;###autoload (autoload 'rutils-renv-status "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-status
  :r-call "renv::status"
  :group "Renv::"
  :key "S"
  :doc "R renv::status."
  :args
  ((project  :key "p" :type directory :arg "--project="
             :desc "The project directory. Default current getwd().")
   (lockfile :key "l" :type file :arg "--lockfile="
             :desc "The path to the lockfile. Default `renv.lock'.")
   (sources  :key "s" :type boolean :arg "--sources"
             :desc "Check the sources of the packages? Default TRUE")
   (cache    :key "c" :type boolean :arg "--cache"
             :desc "Cache? Default FALSE")))

;;;###autoload (autoload 'rutils-renv-restore "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-restore
  :r-call "renv::restore"
  :group "Renv::"
  :key "r"
  :doc "R renv::restore."
  :args
  ((project  :key "p" :type directory :arg "--project="
             :desc "The project directory. Default current getwd().")
   (lockfile :key "l" :type file :arg "--lockfile="
             :desc "The path to the lockfile. Default `renv.lock'.")
   (packages :key "P" :type package :arg "--packages="
             :desc "The packages to restore.")
   (exclude  :key "x" :type string :arg "--exclude="
             :desc "Packages to be excluded." :multi-value t)
      (rebuild  :key "b" :type boolean :arg "--rebuild"
                :desc "Force packages to be rebuilt? Default FALSE")
      (repos    :key "R" :type string :arg "--repos="
                :desc "The R package repository. Default getOption(\"repos\").")
      (clean    :key "C" :type boolean :arg "--clean"
                :desc "Clean? Default FALSE")
      (strict   :key "s" :type boolean :arg "--strict"
                :desc "Strict? Default FALSE")))

;;;###autoload (autoload 'rutils-renv-update "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-update
  :r-call "renv::update"
  :group "Renv::"
  :key "u"
  :doc "R renv::update."
  :args
  ((project  :key "p" :type directory :arg "--project="
             :desc "The project directory. Default current getwd().")
   (packages :key "P" :type package :arg "--packages="
             :desc "The packages to update. Default all packages.")
   (exclude  :key "x" :type string :arg "--exclude="
             :desc "Packages to be excluded." :multi-value t)
   (type     :key "t" :type choice :arg "--type="
             :desc "The package type to install."
             :choices ("NULL" "source" "binary"))
   (rebuild  :key "b" :type boolean :arg "--rebuild"
             :desc "Force packages to be rebuilt? Default FALSE")
   (all      :key "a" :type boolean :arg "--all"
             :desc "Update all packages, even those not used? Default FALSE")))

;;;###autoload (autoload 'rutils-renv-hydrate "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-hydrate
  :r-call "renv::hydrate"
  :group "Renv::"
  :key "h"
  :doc "R renv::hydrate."
  :args
  ((project  :key "p" :type directory :arg "--project="
             :desc "The project directory. Default current getwd().")
   (packages :key "P" :type package :arg "--packages="
             :desc "The packages to hydrate.")
   (repos    :key "r" :type string :arg "--repos="
             :desc "The R package repository. Default getOption(\"repos\").")
   (update   :key "u" :type boolean :arg "--update"
             :desc "Update? Default FALSE")
   (report   :key "R" :type boolean :arg "--report"
             :desc "Report? Default TRUE")))

;;;###autoload (autoload 'rutils-renv-dependencies "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-dependencies
  :r-call "renv::dependencies"
  :group "Renv::"
  :key "d"
  :doc "R renv::dependencies."
  :args
  ((path     :key "p" :type directory :arg "--path="
             :desc "The path to R files. Default getwd().")
   (root     :key "r" :type directory :arg "--root="
             :desc "The root directory.")
      (errors   :key "e" :type choice :arg "--errors="
                :desc "The errors to report."
                :choices ("reported" "fatal" "ignored") :multi-value t)
      (progress :key "g" :type boolean :arg "--progress"
                :desc "Progress? Default TRUE")
      (dev      :key "D" :type boolean :arg "--dev"
                :desc "Dev? Default FALSE")))

;;;###autoload (autoload 'rutils-renv-diagnostics "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-diagnostics
  :r-call "renv::diagnostics"
  :group "Renv::"
  :key "D"
  :doc "R renv::diagnostics."
  :args
  ((project :key "p" :type directory :arg "--project="
            :desc "The project directory. Default current getwd().")))

;;;###autoload (autoload 'rutils-renv-install "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-install
  :r-call "renv::install"
  :group "Renv::"
  :key "I"
  :doc "R renv::install."
  :args
  ((packages :key "c" :type package :arg "--cran="
             :desc "CRAN packages to install." :multi-value t)
   (packages :key "r" :type string :arg "--remote="
             :desc "Remote package to install (e.g. user/repo).")
   (library  :key "l" :type directory :arg "--library="
             :desc "The library to install into. Default the project library.")
   (type     :key "t" :type choice :arg "--type="
             :desc "The type of package to install."
             :choices ("NULL" "source" "binary"))
   (rebuild  :key "b" :type boolean :arg "--rebuild"
             :desc "Force packages to be rebuilt? Default FALSE")
   (repos    :key "R" :type string :arg "--repos="
             :desc "The R package repository. Default getOption(\"repos\").")))

;;;###autoload (autoload 'rutils-renv-activate "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-activate
  :r-call "renv::activate"
  :group "Renv::"
  :key "a"
  :doc "R renv::activate."
  :args
  ((project :key "p" :type directory :arg "--project="
            :desc "The project directory. Default current getwd().")
   (profile :key "P" :type string :arg "--profile="
            :desc "The profile to activate. Default NULL.")))

;;;###autoload (autoload 'rutils-renv-deactivate "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-deactivate
  :r-call "renv::deactivate"
  :group "Renv::"
  :key "A"
  :doc "R renv::deactivate."
  :args
  ((project :key "p" :type directory :arg "--project="
            :desc "The project directory. Default current getwd().")
   (clean   :key "c" :type boolean :arg "--clean"
            :desc "Clean up the project? Default FALSE")))

;;;###autoload (autoload 'rutils-renv-purge "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-purge
  :r-call "renv::purge"
  :group "Renv::"
  :key "p"
  :doc "R renv::purge."
  :args
     ((package :key "P" :type string :arg "--package="
               :desc "The package to purge.")
   (version :key "v" :type string :arg "--version="
            :desc "The package version to purge. Default NULL.")
   (hash    :key "H" :type string :arg "--hash="
            :desc "The package hash to purge. Default NULL.")
   (prompt  :key "r" :type boolean :arg "--prompt"
            :desc "Prompt? Default interactive()")))

;;;###autoload (autoload 'rutils-renv-record "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-record
  :r-call "renv::record"
  :group "Renv::"
  :key "R"
  :doc "R renv::record."
  :args
  ((records  :key "r" :type string :arg "--records="
             :desc "The records to write, e.g. dplyr@1.0.0.")
   (lockfile :key "l" :type file :arg "--lockfile="
             :desc "The path to the lockfile. Default `renv.lock'.")
   (project  :key "p" :type directory :arg "--project="
             :desc "The project directory. Default current getwd().")
   (enrich   :key "e" :type boolean :arg "--enrich"
             :desc "Enrich? Default TRUE")))

;;;###autoload (autoload 'rutils-renv-remove "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-remove
  :r-call "renv::remove"
  :group "Renv::"
  :key "m"
  :doc "R renv::remove."
  :args
  ((packages :key "p" :type package :arg "--packages="
             :desc "The packages to remove." :multi-value t)
   (library  :key "l" :type directory :arg "--library="
             :desc "The library to remove from. Default the project library.")
   (project  :key "P" :type directory :arg "--project="
             :desc "The project directory. Default current getwd().")))

;;;###autoload (autoload 'rutils-renv-use-python "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-use-python
  :r-call "renv::use_python"
  :group "Renv::"
  :key "U"
  :label "use_python"
  :doc "R renv::use_python."
  :args
  ((python  :key "p" :type file :arg "--python="
            :desc "The path to the Python interpreter.")
   (type    :key "t" :type choice :arg "--type="
            :desc "The type of Python environment."
            :choices ("auto" "virtualenv" "conda" "system"))
   (name    :key "n" :type string :arg "--name="
            :desc "The name of the environment. Default NULL.")
   (project :key "P" :type directory :arg "--project="
            :desc "The project directory. Default current getwd().")))

;;;###autoload (autoload 'rutils-renv-isolate "rutils-renv" nil t)
(rutils-define-rcommand rutils-renv-isolate
  :r-call "renv::isolate"
  :group "Renv::"
  :key "o"
  :doc "R renv::isolate."
  :args
  ((project :key "p" :type directory :arg "--project="
            :desc "The project directory. Default current getwd().")))


;;; * Menu

;;;###autoload (autoload 'rutils-renv "rutils-renv" nil t)
(transient-define-prefix rutils-renv ()
  "R renv menu."
  [["Project"
    ("i" "init" rutils-renv-init)
    ("a" "activate" rutils-renv-activate)
    ("A" "deactivate" rutils-renv-deactivate)]
   ["Lockfile"
    ("s" "snapshot" rutils-renv-snapshot)
    ("R" "record" rutils-renv-record)
    ("r" "restore" rutils-renv-restore)]
   ["Packages"
    ("I" "install" rutils-renv-install)
    ("m" "remove" rutils-renv-remove)
    ("p" "purge" rutils-renv-purge)]
   ["Maintain"
    ("u" "update" rutils-renv-update)
    ("h" "hydrate" rutils-renv-hydrate)
    ("o" "isolate" rutils-renv-isolate)]
   ["Inspect"
    ("S" "status" rutils-renv-status)
    ("d" "dependencies" rutils-renv-dependencies)
    ("D" "diagnostics" rutils-renv-diagnostics)
    ("U" "use_python" rutils-renv-use-python)]])


(provide 'rutils-renv)
;;; rutils-renv.el ends here
