#+TITLE:  Personal Password Generator
#+AUTHOR: Howard X. Abrams
#+DATE:   2021-01-11
#+FILETAGS: :emacs:

A literate programming version for Emacs code to generate and store passwords.

#+BEGIN_SRC emacs-lisp :exports none
  ;;; ha-passwords --- Emacs code to generate and store passwords. -*- lexical-binding: t; -*-
  ;;
  ;; © 2021-2022 Howard X. Abrams
  ;;   This work is licensed under a Creative Commons Attribution 4.0 International License.
  ;;   See http://creativecommons.org/licenses/by/4.0/
  ;;
  ;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
  ;; Maintainer: Howard X. Abrams
  ;; Created: January 11, 2021
  ;;
  ;; This file is not part of GNU Emacs.
  ;;
  ;; *NB:* Do not edit this file. Instead, edit the original literate file at:
  ;;            ~/other/hamacs/ha-passwords.org
  ;;       And tangle the file to recreate this one.
  ;;
  ;;; Code:
#+END_SRC
* Introduction
Let's assume that I store a bunch of words in data files:

#+BEGIN_SRC emacs-lisp
(defvar ha-passwords-data-files (list (expand-file-name "adjectives.txt"
                                                        (expand-file-name "data" hamacs-source-dir))
                                      (expand-file-name "colors.txt"
                                                        (expand-file-name "data" hamacs-source-dir))
                                      (expand-file-name "nouns.txt"
                                                        (expand-file-name "data" hamacs-source-dir)))
  "List of file name containing a data lines for our password generator. Order of these files matter.")

(defvar ha-passwords-data nil
  "Contains a list of lists of words that we can choose.")
#+END_SRC

You can see where I'm going with this, can't you? Let's read them into list variables.

#+BEGIN_SRC emacs-lisp
(defun ha-passwords--read-data-file (filename)
  (with-temp-buffer
    (insert-file-contents filename)
    (split-string (buffer-string) "\n" t)))

#+END_SRC

Now we just get three or so words from our list of lists:

#+BEGIN_SRC emacs-lisp
(defun ha-passwords-words ()
  (unless ha-passwords-data
    (setq ha-passwords-data
          (--map (ha-passwords--read-data-file it) ha-passwords-data-files)))

  (--map (nth (random (length it)) it) ha-passwords-data))
#+END_SRC

Let's make a password:

#+BEGIN_SRC emacs-lisp
(defun ha-passwords-generate (&optional separator)
  (unless separator
    (setq separator "-"))

  (let* ((choices '("!" "@" "#" "$" "%" "^" "&" "*"))
         (choice (random (length choices)))
         (number (1+ choice)))
    (->> (ha-passwords-words)
         (s-join separator)
         (s-capitalize)
         (s-append (nth choice choices))
         (s-append (number-to-string number)))))
#+END_SRC

#+BEGIN_SRC emacs-lisp
(defun generate-password (&optional separator)
  (interactive)
  (let ((passphrase (ha-passwords-generate separator)))
    (kill-new passphrase)
    (message "Random password: %s" passphrase)))
#+END_SRC
* Keybindings
Got make it easy to call:
#+BEGIN_SRC emacs-lisp
(ha-leader "a g" '("generate passwd" . generate-password))
#+END_SRC
* Technical Artifacts :noexport:
This will =provide= a code name, so that we can =require= this.

#+BEGIN_SRC emacs-lisp :exports none
(provide 'ha-passwords)
;;; ha-passwords.el ends here
#+END_SRC

#+DESCRIPTION: A literate programming version for Emacs code to generate and store passwords.

#+PROPERTY:    header-args:sh :tangle no
#+PROPERTY:    header-args:emacs-lisp :tangle yes
#+PROPERTY:    header-args    :results none :eval no-export :comments no mkdirp yes

#+OPTIONS:     num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
#+OPTIONS:     skip:nil author:nil email:nil creator:nil timestamp:nil
#+INFOJS_OPT:  view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js