code: def build_data_index(  # noqa: C901, PLR0912
      index: Union["Index", "IndexView"],
      path: str,
      fs: "FileSystem",
      workspace: str = "repo",
      compute_hash: Optional[bool] = False,
      callback: "Callback" = DEFAULT_CALLBACK,
  ) -> "DataIndex":
      from dvc_data.index import DataIndex, DataIndexEntry, Meta
      from dvc_data.index.build import build_entries, build_entry
      from dvc_data.index.save import build_tree
  
      ignore = None
      if workspace == "repo" and isinstance(fs, LocalFileSystem):
          ignore = index.repo.dvcignore
  
      data = DataIndex()
      parents = set()
      for key in index.data_keys.get(workspace, set()):
          out_path = fs.join(path, *key)
  
          for key_len in range(1, len(key)):
              parents.add(key[:key_len])
  
          if not fs.exists(out_path):
              continue
  
          hash_name = _get_entry_hash_name(index, workspace, key)
          try:
              out_entry = build_entry(
                  out_path,
                  fs,
                  compute_hash=compute_hash,
                  state=index.repo.state,
                  hash_name=hash_name,
              )
          except FileNotFoundError:
              out_entry = DataIndexEntry()
  
          out_entry.key = key
          data.add(out_entry)
          callback.relative_update(1)
  
          if not out_entry.meta or not out_entry.meta.isdir:
              continue
  
          for entry in build_entries(
              out_path,
              fs,
              compute_hash=compute_hash,
              state=index.repo.state,
              ignore=ignore,
              hash_name=hash_name,
          ):
              if not entry.key or entry.key == ("",):
                  # NOTE: whether the root will be returned by build_entries
                  # depends on the filesystem (e.g. local doesn't, but s3 does).
                  continue
  
              entry.key = key + entry.key
              data.add(entry)
              callback.relative_update(1)
  
      for key in parents:
          parent_path = fs.join(path, *key)
          if not fs.exists(parent_path):
              continue
          direntry = DataIndexEntry(
              key=key, meta=Meta(isdir=True), loaded=True
          )
          data.add(direntry)
          callback.relative_update(1)
  
      if compute_hash:
          out_keys = index.out_data_keys.get(workspace, set())
          data_keys = index.data_keys.get(workspace, set())
          for key in data_keys.intersection(out_keys):
              hash_name = _get_entry_hash_name(index, workspace, key)
  
              out_entry = data.get(key)
              if not out_entry or not out_entry.isdir:
                  continue
  
              tree_meta, tree = build_tree(data, key, name=hash_name)
              out_entry.meta = tree_meta
              out_entry.hash_info = tree.hash_info
              out_entry.loaded = True
              data.add(out_entry)
              callback.relative_update(1)
  
      return data
  
imports: import logging
  import time
  from collections import defaultdict
  from functools import partial
  from itertools import chain
  from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, NamedTuple, Optional, Set, Tuple, Union
  from funcy.debug import format_time
  from dvc.dependency import ParamsDependency
  from dvc.fs import LocalFileSystem
  from dvc.fs.callbacks import DEFAULT_CALLBACK
  from dvc.log import logger
  from dvc.utils.objects import cached_property
label_desc: Use image node_id 8 for calling a global function with example usage: build_data_index(index, path, fs, workspace, compute_hash, callback) and returns: data