code: def parse_image_name(image: str) -> DockerImage:
      """
      :param image: docker image name
      :return: registry host, repo, tag, digest
  
      >>> parse_image_name("ubuntu:22.04")
      DockerImage(registry=None, repo='library/ubuntu', tag='22.04', digest=None)
      >>> parse_image_name("dstackai/miniforge:py3.9-0.2")
      DockerImage(registry=None, repo='dstackai/miniforge', tag='py3.9-0.2', digest=None)
      >>> parse_image_name("ghcr.io/dstackai/miniforge")
      DockerImage(registry='ghcr.io', repo='dstackai/miniforge', tag='latest', digest=None)
      >>> parse_image_name("dstackai/miniforge@sha256:a4ba18a847a172a248d68faf6689e69fae4779b90b250211b79a26d21ddd6a15")
      DockerImage(registry=None, repo='dstackai/miniforge', tag='latest', digest='sha256:a4ba18a847a172a248d68faf6689e69fae4779b90b250211b79a26d21ddd6a15')
      """
  
      digest = None
      if "@" in image.split("/")[-1]:
          image, digest = image.rsplit("@", maxsplit=1)
  
      tag = "latest"
      if ":" in image.split("/")[-1]:  # avoid detecting port as a tag
          image, tag = image.rsplit(":", maxsplit=1)
  
      registry = None
      components = image.split("/")
      if len(components) == 1:  # default registry, official image
          repo = "library/" + components[0]
      elif not is_host(components[0]):  # default registry, custom image
          repo = "/".join(components)
      else:  # custom registry
          registry = components[0]
          repo = "/".join(components[1:])
  
      return DockerImage(registry, repo, tag, digest)
  
file_path: dstack/src/dstack/_internal/server/services/docker.py
imports: from dataclasses import dataclass
  from enum import Enum
  from typing import Optional
  import requests
label_desc: Use image node_id 2 for calling a global function with example usage: parse_image_name(image) and returns: DockerImage