1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use std::ffi::OsString;
use std::fs::{self, FileType, Metadata};
use std::io::{Error, Result};
use std::path::{Path, PathBuf};
use std::sync::Arc;

use super::ReadDirSpec;

/// Representation of a file or directory.
///
/// This representation does not wrap a `std::fs::DirEntry`. Instead it copies
/// `file_name`, `file_type`, and optionaly `metadata` out of the underlying
/// `std::fs::DirEntry`. This allows it to quickly drop the underlying file
/// descriptor.
#[derive(Debug)]
pub struct DirEntry {
  /// Depth of this entry relative to the root directory where the walk started.
  pub depth: usize,
  /// File name of this entry without leading path component.
  pub file_name: OsString,
  /// File type result for the file/directory that this entry points at.
  pub file_type: Result<FileType>,
  /// Metadata result for the file/directory that this entry points at. Defaults
  /// to `None`. Filled in by the walk process when the
  /// [`preload_metadata`](struct.WalkDir.html#method.preload_metadata) option
  /// is set.
  pub metadata: Option<Result<Metadata>>,
  /// [`ReadDirSpec`](struct.ReadDirSpec.html) used for reading this entry's
  /// content. This is automatically set for directory entries. The
  /// [`process_entries`](struct.WalkDir.html#method.process_entries) callback
  /// may set this field to `None` to skip reading the contents of this
  /// particular directory.
  pub content_spec: Option<Arc<ReadDirSpec>>,
  /// If `fs::read_dir` generates an error when reading this entry's content
  /// then that error is stored here.
  pub content_error: Option<Error>,
  /// [`ReadDirSpec`](struct.ReadDirSpec.html) used by this entry's parent to
  /// read this entry.
  pub parent_spec: Option<Arc<ReadDirSpec>>,
}

impl DirEntry {
  pub(crate) fn new(
    depth: usize,
    file_name: OsString,
    file_type: Result<FileType>,
    metadata: Option<Result<Metadata>>,
    parent_spec: Option<Arc<ReadDirSpec>>,
    content_spec: Option<Arc<ReadDirSpec>>,
  ) -> DirEntry {
    DirEntry {
      depth,
      file_name,
      file_type,
      parent_spec,
      metadata,
      content_spec,
      content_error: None,
    }
  }

  /// Path to the file/directory represented by this entry.
  ///
  /// The path is created by joining `parent_path` with `file_name`.
  pub fn path(&self) -> PathBuf {
    let mut path = match self.parent_spec.as_ref() {
      Some(parent_spec) => parent_spec.path.to_path_buf(),
      None => PathBuf::from(""),
    };
    path.push(&self.file_name);
    path
  }

  /// Reference to the path of the directory containing this entry.
  pub fn parent_path(&self) -> Option<&Path> {
    self
      .parent_spec
      .as_ref()
      .map(|parent_spec| parent_spec.path.as_ref())
  }

  // Should use std::convert::TryFrom when stable
  pub(crate) fn try_from(path: &Path) -> Result<DirEntry> {
    let metadata = fs::metadata(path)?;
    let root_name = OsString::from("/");
    let file_name = path.file_name().unwrap_or(&root_name);
    let parent_spec = path
      .parent()
      .map(|parent| Arc::new(ReadDirSpec::new(parent.to_path_buf(), 0, None)));

    Ok(DirEntry::new(
      0,
      file_name.to_owned(),
      Ok(metadata.file_type()),
      Some(Ok(metadata)),
      parent_spec,
      None,
    ))
  }
}