#compdef diskutil
# ------------------------------------------------------------------------------
# Copyright (c) 2025 Github zsh-users - https://github.com/zsh-users
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
# ------------------------------------------------------------------------------
# Description
# -----------
#
#  Completion script for diskutil command macOS 26
#
# ------------------------------------------------------------------------------
# Authors
# -------
#
#  * Shohei Yoshida (https://github.com/syohex)
#
# ------------------------------------------------------------------------------

_diskutil() {
  typeset -A opt_args
  local context state line
  local curcontext="$curcontext"
  local ret=1

  _arguments  \
    '1: :_diskutil_subcommands' \
    '*::arg:->args' \
    && ret=0

  case "$state" in
    (args)
      case $words[1] in
        (list)
          _arguments \
            '-plist[output plist instead of normal user-readable output]' \
            '1:: :(internal external)' \
            '2:: :(physical virtual)' \
            && ret=0
          ;;
        (info)
          _arguments \
            '-plist[output plist instead of normal user-readable output]' \
            '-all[output all disks]' \
            && ret=0
          ;;
        (listFilesystems)
          _arguments \
            '-plist[output plist instead of normal user-readable output]' \
            && ret=0
          ;;
        (unmount|unmountDisk|eject|disableJournal|disableJournal)
          _alternative 'force: :(force)' 'device: :_files' \
            && ret=0
          ;;
        (mount)
          _arguments \
            '-mountOptions[mount options]:option' \
            '-mountPoint[mount point]:path:_files -/' \
            '*:: :_diskutil_mount_args' \
            && ret=0
          ;;
        (moveJournal)
          _alternative 'internal: :(internal)' 'device: :_files' \
            && ret=0
          ;;
        (eraseDisk)
          _arguments \
            '-noEFI[do not create EFI partition]' \
            '1:format:_diskutil_file_systems' \
            '2:name' \
            '*:: :_diskutil_erasedisk_args' \
            && ret=0
          ;;
        (eraseVolume)
          _arguments \
            '1:format:_diskutil_file_systems' \
            '2:name' \
            '*:: :_diskutil_erasedisk_args' \
            && ret=0
          ;;
        (zeroDisk)
          _alternative 'force: :(force)' 'short: :(short)' 'device: :_files' \
            && ret=0
          ;;
        (secureErase)
          _arguments \
            '1:format:_diskutil_secure_erase_args' \
            '*:: :_files' \
            && ret=0
          ;;
        (partitionDisk)
          _arguments \
            '-noEFI[do not create EFI partition on the target disk]' \
            '*:: :_files' \
            && ret=0
          ;;
        (resizeVolume)
          _arguments \
            '-plist[emit a property list instead of user-formatted output]' \
            '*:: :_files' \
            && ret=0
          ;;
        (APFS)
          _diskutil_APFS && ret=0
          ;;
        (appleRAID)
          _diskutil_appleRAID && ret=0
          ;;
        (coreStorage)
          _diskutil_corestorage && ret=0
          ;;
        (image)
          _diskutil_image && ret=0
          ;;
        (*)
          _arguments \
            '*:: :_files' \
            && ret=0
          ;;
      esac
      ;;
  esac

  return ret
}

(( $+functions[_diskutil_subcommands] )) ||
_diskutil_subcommands() {
  local -a commands=(
    "list:List disks"
    "info:Get detailed information about a specific whole disk or partition"
    "activity:Continuously display system-wide disk manipulation activity"
    "listFilesystems:Show the file system personalities available for formatting"
    "unmount:Unmount a single volume"
    "unmountDisk:Given a disk containing a partition map, unmount all of its volumes"
    "eject:Eject a disk"
    "mount:Mount a single volume"
    "mountDisk:Mount all mountable and UI-browsable volumes on the give partition map"
    "rename:Rename a volume"
    "enableJournal:Enable journaling on an HFS+ volume"
    "disableJournal:Disable journaling on an HFS+ volume"
    "moveJournal:Create a 512MB Apple_Journal partiton"
    "enableOwnership:Enable ownership a volume"
    "disableOwnership:Disable ownership of a volume"
    "verifyVolume:Verify the file system data structures of a volume"
    "repairVolume:Repair the file system data structures of a volume"
    "verifyDisk:Verify the partition map layout of a whole disk intended for booting or data use"
    "repairDisk:Repair the partition map layout of a whole disk intended for booting or data use"
    "eraseDisk:Erase an existing disk"
    "eraseVolume:Write out a new empty file system volume"
    "reformat:Erase an existing volume by writing out a new empty file system"
    "eraseOptical:Erase optical media"
    "zeroDisk:Erase a device, writing zeros to the media"
    "randomDisk:Erase a whole disk, writing random data to the media"
    "secureErase:Erase, using a secure method"
    "partitionDisk:Partition a disk, removing all volumes"
    "resizeVolume:Non-destructively resize a volume"
    "splitPartition:Destructively split a volume into multiple partitions"
    "mergePartitions:Merge two or more partitions on a disk"
    "addPartition:Create a new partition following an existing partition"
    "APFS:Apple APFS is a system of virtual volumes"
    "appleRAID:create, manipulate, destroy AppleRAID volumes"
    "coreStorage:gather information/remove CoreStorage volumes"
    "image:manipulate DiskImages framework with StorageKit framework"
  )
  _describe -t commands 'command' commands "$@"
}

(( $+functions[_diskutil_mount_args] )) ||
_diskutil_mount_args() {
  local ret=1

  local -a attributes=(
    'readonly[the file system is mounted read only]'
    'nobrowse[the file system is mounted with a recommendation to prevent display]'
  )

  _alternative \
    'attributes: :_values -w attributes $attributes' \
    'device: :_files' && ret=0

  return ret
}

(( $+functions[_diskutil_erasedisk_args] )) ||
_diskutil_erasedisk_args() {
  local ret=1

  _alternative \
    'type: :(APM MBR GPT)' \
    'device: :_files' && ret=0

  return ret
}

(( $+functions[_diskutil_secure_erase_args] )) ||
_diskutil_secure_erase_args() {
  local ret=1

  local -a levels=(
    '0[Single-pass zero fill erase]'
    '1[Single-pass random fill erase]'
    '2[Seven-pass erase]'
    '3[Gutmann algorithm 35-pass erase]'
    '4[Three-pass erase, consisting of two random fills plus a final zero fill]'
  )

  _alternative \
    'freespace: :(freespace)' \
    'level: :_values level $levels' && ret=0

  return ret
}

(( $+functions[_diskutil_file_systems] )) ||
_diskutil_file_systems() {
  local file_systems=(
    'APFSX:Case-sensitive APFS'
    'APFS:APFS'
    'ExFAT:ExFat'
    'FREE:Free Space'
    'MS-DOS:FAT'
    'FAT32:FAT32'
    'HFS+:MacOS Extended HFS+'
    'HFSX:Case-sensitive Mac OS Extended HFS+'
    'JHFSX:Case-sensitive and journaled Mac OS Extended HFS+'
    'JHFS+:Journaled Mac OS Extended HFS+'
  )

  _describe -t file_systems 'file_system' file_systems "$@"
}

(( $+functions[_diskutil_APFS] )) ||
_diskutil_APFS() {
  local ret=1

  _arguments -C \
    '1: :_diskutil_APFS_subcommands' \
    '*:: :->arg' \
    && ret=0

  case $state in
    (arg)
      case $words[1] in
        (list|resizeContainer|listCryptoUsers|listSnapshots)
          _arguments \
            '-plist[emit a property list instead of user-formatted output]' \
            '*:: :_files' \
            && ret=0
          ;;
        (convert)
          _arguments \
            '-dryrun[all calculations, checks, some data moving is performed but your disk is left as valid HFS]' \
            '-prebootSource[staging directory of macOS boot items]' \
            '*:: :_files' \
            && ret=0
          ;;
        (deleteContainer)
          _arguments \
            '-force[Activate an alternate last-resort mode]' \
            '*:: :_files' \
            && ret=0
          ;;
        (addVolume)
          _arguments \
            '-passprompt[will be prompted interactively for a passphrase]' \
            '-passphrase[passphrase]:passphrase' \
            '-stdinpassphrase[read passphrase from standard input]' \
            '-passphraseHint[passphrase hint]:hint' \
            '-reserve[guarantee a minimum amount of space for your volume]:reserve' \
            "-quota[limit your volume's file usage to a maximum amount]:quota" \
            '-role[meta-data flags from APFS Volume Roles]:roles' \
            '-groupWith[become a member of the same APFS Volume Group]' \
            '-sibling[sibling group device]:group_device' \
            '-nomount[leave volume unmounted]' \
            '-mountpoint[mountpoint path]:path:_files' \
            '*:: :_files' \
            && ret=0
          ;;
        (eraseVolume)
          _arguments \
            '-passprompt[will be prompted interactively for a passphrase]' \
            '-passphrase[passphrase]:passphrase' \
            '-stdinpassphrase[read passphrase from standard input]' \
            '-passphraseHint[passphrase hint]:hint' \
            '-role[meta-data flags from APFS Volume Roles]:roles' \
            '-groupWith[become a member of the same APFS Volume Group]' \
            '-sibling[sibling group device]:group_device' \
            '*:: :_files' \
            && ret=0
          ;;
        (unlockVolume)
          _arguments \
            '-user[cryptographic user]:user' \
            '-recoverykeychain[key chain file]:path:_files' \
            '-passphrase[passphrase]:passphrase' \
            '-stdinpassphrase[read passphrase from standard input]' \
            '-nomount[leave volume unmounted]' \
            '-mountpoint[mountpoint path]:path:_files' \
            '-systemreadwrite[mount read/write]' \
            '-verify[test passphrase correctness without affecting the locked or unlocked state]' \
            '-plist[emit a property list instead of user-formatted output]' \
            '*:: :_files' \
            && ret=0
          ;;
        (changePassphrase)
          _arguments \
            '-user[cryptographic user]:user' \
            '-oldPassphrase[old passphrase]:old_passphrase' \
            '-oldStdinpassphrase[read old passphrase from standard input]' \
            '-newPassphrase[new passphrase]:new_passphrase' \
            '-newStdinpassphrase[read new passphrase from standard input]' \
            '*:: :_files' \
            && ret=0
          ;;
        (setPassphraseHint)
          _arguments \
            '-user[cryptographic user]:user' \
            '-hint[hint message]:hint' \
            '-clear[clear any existing hint]' \
            '*:: :_files' \
            && ret=0
          ;;
        (encryptVolume)
          _arguments \
            '-user[cryptographic user]:user' \
            '-passphrase[passphrase]:passphrase' \
            '-stdinpassphrase[read passphrase from standard input]' \
            '*:: :_files' \
            && ret=0
          ;;
        (decryptVolume)
          _arguments \
            '-user[cryptographic user]:user' \
            '-recoverykeychain[key chain file]:path:_files' \
            '-passphrase[passphrase]:passphrase' \
            '-stdinpassphrase[read passphrase from standard input]' \
            '*:: :_files' \
            && ret=0
          ;;
        (deleteSnapshot)
          _arguments \
            '-user[cryptographic user]:user' \
            '-xid[transaction ID]:xid' \
            '-name[snapshort name]:name' \
            '-wait[wait for removing snapshot]' \
            '*:: :_files' \
            && ret=0
          ;;
        (updatePreboot)
          _arguments \
            '-od[open directory path]:dir:_files -/' \
            '*:: :_files' \
            && ret=0
          ;;
      esac
    ;;
  esac

  return ret
}

(( $+functions[_diskutil_APFS_subcommands] )) ||
_diskutil_APFS_subcommands() {
  local -a commands=(
    'list:Display APFS objects as a tree'
    'convert:Convert an HFS volume to an APFS Container with a single APFS Volume'
    'create:Create an empty APFS Container and add one APFS Volume with the given name'
    'createContainer:Create an empty APFS Container'
    'deleteContainer:Destroy an existing APFS Container'
    'resizeContainer:Resize an existing APFS Container'
    'addVolume:Add a new APFS Volume to an existing APFS Container'
    'deleteVolume:Remove the given APFS Volume from its APFS Container'
    'deleteVolumeGroup:Remove all APFS Volumes from the APFS Container'
    'eraseVolume:Erase the contents of an existing APFS Volume'
    'changeVolumeRole:Change the role metadata flags of an existing APFS Volume'
    'unlockVolume:Unlock the mount an encrypted and locked APFS Volume or verify a passphrase'
    'lockVolume:Unmount and lock an encrypted unlocked APFS Volume'
    'listCryptoUsers:Show all cryptographic users and special-purpose users'
    'changePassphrase:Change the passphrase of the user associated with the given APFS Volume'
    'setPassphraseHint:Set an arbitrary hint string to aid recall of a passphrase'
    'encryptVolume:Start encryption of a currently-unencrypted APFS Volume'
    'decryptVolume:Start decryption of a currently-encrypted APFS Volume'
    'listSnapshots:Show all APFS Snapshots associated with the given APFS Volume'
    'deleteSnapshot:Remove the given APFS Snapshot from its APFS Volume'
    'listGroups:Display the relationships among APFS Volumes which are defined by APFS Volume Groups'
    'defragment:Manage automatic background defragmentation at the APFS Container or Volume level'
    "updatePreboot:Update the target volume's associated Preboot Volume"
    'syncPatchUsers:Perform a specific repair of APFS cryptographic user lock records'
  )
  _describe -t commands 'command' commands "$@"
}

(( $+functions[_diskutil_appleRAID] )) ||
_diskutil_appleRAID() {
  local ret=1

  _arguments -C \
    '1: :_diskutil_appleRAID_subcommands' \
    '*:: :->arg' \
    && ret=0

  case $state in
    (arg)
      case $words[1] in
        (list)
          _arguments \
            '-plist[emit a property list instead of user-formatted output]' \
            '*:: :_files' \
            && ret=0
          ;;
        (create)
          _arguments \
            '1:command:(mirror stripe concat)' \
            '*:: :_files' \
            && ret=0
          ;;
        (add)
          _arguments \
            '1:type:(member spare)' \
            '*:: :_files' \
            && ret=0
          ;;
        (enable)
          _arguments \
            '1:command:(mirror concat)' \
            '*:: :_files' \
            && ret=0
          ;;
        (update)
          _arguments \
            '1:key:(AutoRebuild SetTimeout)' \
            '2:value' \
            '*:: :_files' \
            && ret=0
          ;;
      esac
    ;;
  esac

  return ret
}

(( $+functions[_diskutil_appleRAID_subcommands] )) ||
_diskutil_appleRAID_subcommands() {
  local -a commands=(
    'list:Display AppleRAID volumes with current status and associated member disks'
    'create:Create a new RAID set consisting of multiple disks and/or RAID sets'
    'delete:Destroy an existing RAID set'
    'repairMirror:Repair a degraded mirror'
    'add:Add a new member or hot spare to an existing RAID set'
    'remove:Remove a member or spare from an existing RAID set'
    'enable:Convert a non-RAID disk partition into an RAID set'
    'update:Update the key-value parameters of an existing RAID set'
  )
  _describe -t commands 'command' commands "$@"
}

(( $+functions[_diskutil_corestorage] )) ||
_diskutil_corestorage() {
  local ret=1

  _arguments -C \
    '1: :_diskutil_corestorage_subcommands' \
    '*:: :->arg' \
    && ret=0

  case $state in
    (arg)
      case $words[1] in
        (list|info)
          _arguments \
            '-plist[emit property list instead of the formatted tree output]' \
            '*:: :_files' \
            && ret=0
          ;;
        (unlockVolume)
          _arguments \
            '-nomount[not mount automatically]' \
            '-stdinpassphrase[read password from standard input]' \
            '-passphrase[passphrase]:passphrase' \
            '-recoverykeychain[a path to keychain file]:file:_files' \
            '*:: :_files' \
            && ret=0
          ;;
      esac
    ;;
  esac

  return ret
}

(( $+functions[_diskutil_corestorage_subcommands] )) ||
_diskutil_corestorage_subcommands() {
  local -a commands=(
    'list:Display a tree view of the CoreStorage world'
    'info:Display properties of the CoreStorage object'
    'delete:Delete a CoreStorage logical volume group'
    'unlockVolume:Unlock a logical volume and file system'
  )
  _describe -t commands 'command' commands "$@"
}

(( $+functions[_diskutil_image] )) ||
_diskutil_image() {
  local ret=1

  _arguments -C \
    '--stdinpassphrase[read the passphrase from stdin]' \
    '--verbose[enable verbose output]' \
    '--plist[produce output in a plist format]' \
    '1: :_diskutil_image_subcommands' \
    '*:: :->arg' \
    && ret=0

  case $state in
    (arg)
      case $words[1] in
        (attach)
          _arguments \
            '--readOnly[disk image is attached read-only]' \
            '--nobrowse[hide the mounted volume in the disk image from GUI applications]' \
            '--mountPoint[path to mount the image]:mount_point:_files -/' \
            '--mountOptions[comma separated mount options]:option' \
            '--mountPolicy[mount policy]:policy:(noMount autoMount forceMount)' \
            '--noMount[skip any mount attempts and only attach the disk image]' \
            '*--shadow[shadow file path]:file:_files' \
            '*:: :_files' \
            && ret=0
          ;;
        (info)
          _arguments \
            '--extra[additional information will be retrieved for some image types]' \
            && ret=0
          ;;
        (create)
          _diskutil_image_create && ret=0
          ;;
        (resize)
          _arguments \
            '(-s --size)'{-s,--size}'[new size of the disk image]:size' \
            '--image-only[only resize the disk image and adjust a secondary GPT table to the new size]' \
            && ret=0
          ;;
      esac
    ;;
  esac

  return ret
}

(( $+functions[_diskutil_image_subcommands] )) ||
_diskutil_image_subcommands() {
  local -a commands=(
    'attach:Attach a disk image as a device'
    'info:Print out information includes about a disk image'
    'chpass:Change the passphrase of a given encrypted image'
    'create:Create a disk image'
    'resize:Resizes an existing disk image represented by given URL'
  )
  _describe -t commands 'command' commands "$@"
}

(( $+functions[_diskutil_image_create] )) ||
_diskutil_image_create() {
  local ret=1

  _arguments -C \
    '1: :_diskutil_image_create_subcommands' \
    '*:: :->arg' \
    && ret=0

  case $state in
    (arg)
      case $words[1] in
        (blank)
          _arguments \
            '--format[disk format]:format:(RAW ASIF USDB)' \
            '--size[disk size]:size' \
            '--volumeName[volume name]:name' \
            '-fs[create a file system in the specified format]:format:(APFS ExFAT MS-DOS)' \
            '*:: :_files' \
            && ret=0
          ;;
        (from)
          _arguments \
            '--format[disk format]:format:(RAW UDRO UDZO ULFO ULMO ASIF UDSB)' \
            '--shadow[path to the shadow file]:path:_files' \
            '*:: :_files' \
            && ret=0
          ;;
      esac
    ;;
  esac

  return ret
}

(( $+functions[_diskutil_image_create_subcommands] )) ||
_diskutil_image_create_subcommands() {
  local -a commands=(
    'blank:create a blank disk image'
    'from:create a disk image from source'
  )
  _describe -t commands 'command' commands "$@"
}

_diskutil "$@"

# Local Variables:
# mode: Shell-Script
# sh-indentation: 2
# indent-tabs-mode: nil
# sh-basic-offset: 2
# End:
# vim: ft=zsh sw=2 ts=2 et
