///|
/// An ordered GIR search path. Each requested namespace is resolved
/// independently, so a primary GIR and its dependencies may live in different
/// directories.
priv struct GirSearchPath {
  directories : Array[@path.Path]
}

///|
fn GirSearchPath::GirSearchPath(
  directories : ArrayView[String],
) -> GirSearchPath {
  let resolved : Array[@path.Path] = []
  for directory in directories {
    if directory != "" {
      let path = @path.Path(directory).resolve()
      if !resolved.contains(path) {
        resolved.push(path)
      }
    }
  }
  { directories: resolved }
}

///|
/// Resolve one namespace using PATH-style first-match semantics.
async fn GirSearchPath::resolve_gir(
  self : GirSearchPath,
  gir_name : String,
) -> @path.Path raise SubclassCommandError {
  for directory in self.directories {
    let candidate = directory.join(@path.Path("\{gir_name}.gir"))
    let exists = @fs.exists(candidate.0) catch {
      error =>
        raise SubclassCommandError::Failure(
          "failed to inspect \{candidate.0}: \{error}",
        )
    }
    if exists {
      return candidate
    }
  }
  let searched : Array[String] = []
  for directory in self.directories {
    searched.push(directory.0)
  }
  raise SubclassCommandError::Failure(
    "cannot find \{gir_name}.gir in GIR search path [\{searched.join(", ")}]; repeat --gir-dir or set GI_GIR_PATH",
  )
}

///|
/// Validate every configured namespace independently before generation starts.
async fn GirSearchPath::validate(
  self : GirSearchPath,
  config : @config.GirConfig,
) -> Unit raise SubclassCommandError {
  ignore(self.resolve_gir(config.gir))
  for gir_name, _ in config.deps {
    ignore(self.resolve_gir(gir_name))
  }
}