#!/usr/bin/ruby

require 'colorize'

class String
  def split_path(sep=':')
    split(sep).reject(&:empty?)
  end
end

class ResticOps
  def backup()
    # Required. Raise KeyError if not defined.
    paths = ENV.fetch('BACKUP_PATHS').split_path

    # Optional. The default value will be split to an empty list and the option
    # will be left out.
    exclude_files = ENV.fetch('BACKUP_EXCLUDE_FILES', '').split_path
    exclude_if_present = ENV.fetch('BACKUP_EXCLUDE_IF_PRESENT', '').split_path

    # Optional
    verbosity = ENV.fetch('BACKUP_VERBOSE', 0)

    restic 'backup',
      '--one-file-system',
      '--tag=systemd.timer',
      "--verbose=#{verbosity}",
      *exclude_files.map { |file| "--exclude-file=#{file}" },
      *exclude_if_present.map { |file| "--exclude-if-present=#{file}" },
      *paths
  end

  # Forget backups that have expired. This won't delete anything yet. That is
  # handled by #prune.
  def forget()
    retentions = {
      # Optonal. When we compact, nil entries will be removed.
      daily: ENV['FORGET_KEEP_DAILY'],
      weekly: ENV['FORGET_KEEP_WEEKLY'],
      monthly: ENV['FORGET_KEEP_MONTHLY'],
      yearly: ENV['FORGET_KEEP_YEARLY'],
    }

    retentions.compact!

    # Optional
    verbosity = ENV.fetch('FORGET_VERBOSE', 0)

    restic 'forget',
      '--group-by=paths,tags',
      '--tag=systemd.timer',
      "--verbose=#{verbosity}",
      *retentions.map { |period, value| "--keep-#{period}=#{value}" }
  end

  def mount()
    # Required. Raise KeyError if not defined.
    path = ENV.fetch('MOUNT_PATH')

    # Optional
    verbosity = ENV.fetch('MOUNT_VERBOSE', 0)

    restic 'mount', "--verbose=#{verbosity}", path
  end

  # Delete data that is no longer necessary (e.g., because the backup has been
  # forgotten).
  def prune()
    # Optional
    verbosity = ENV.fetch('PRUNE_VERBOSE', 0)

    restic 'prune', "--verbose=#{verbosity}"
  end

  # Remove any stale locks. Note, this doesn't clear any active locks held by
  # live processes, so we won't interrupt an already-running backup. If this
  # fails for some reason, we should still try to do the backup.
  def unlock()
    # Optional
    verbosity = ENV.fetch('UNLOCK_VERBOSE', 0)

    restic 'unlock', "--verbose=#{verbosity}"
  end

  private

  def restic(op, *args)
    # Log the command for reference
    puts  "#{'>>>'.green} Running restic #{op}..."
    puts

    system 'restic', op, *args
    status = $?

    puts

    if status.success? then
      puts "#{'>>>'.green} Success"
    else
      puts "#{'>>>'.red} Error: Exit status #{status.exitstatus}"
    end

    return status.exitstatus
  end
end


action = ARGV[0]
ops = ResticOps.new
exit_status = ops.send action

exit exit_status

