lib/mix/tasks/hf.repos.ex

defmodule Mix.Tasks.Hf.Repos do
  @shortdoc "List your HuggingFace repositories"
  @moduledoc """
  Lists repositories for the authenticated user or a specific author.

      $ mix hf.repos
      $ mix hf.repos --author my-org
      $ mix hf.repos --type dataset
      $ mix hf.repos --author huggingface --limit 20

  ## Options

    * `--author` / `-a` — list repos for this user/org
    * `--type` / `-t` — `model`, `dataset`, `space` (default: all)
    * `--limit` / `-n` — max results (default: 20)
    * `--sort` / `-s` — sort by: `downloads`, `likes`, `lastModified`
    * `--token` — HF API token
  """

  use Mix.Task

  @impl Mix.Task
  def run(args) do
    {opts, _, _} =
      OptionParser.parse(args,
        aliases: [a: :author, t: :type, n: :limit, s: :sort],
        strict: [author: :string, type: :string, limit: :integer, sort: :string, token: :string]
      )

    token = opts[:token] || HuggingfaceClient.Config.token()
    limit = opts[:limit] || 20
    author = opts[:author]
    type = opts[:type]

    search_opts = [
      access_token: token,
      sort: opts[:sort] || "lastModified",
      direction: -1,
      limit: limit
    ]

    search_opts = if author, do: Keyword.put(search_opts, :author, author), else: search_opts

    repos =
      case type do
        "dataset" -> HuggingfaceClient.Hub.Search.datasets(search_opts) |> Enum.take(limit)
        "space" -> HuggingfaceClient.Hub.Search.spaces(search_opts) |> Enum.take(limit)
        _ -> HuggingfaceClient.Hub.Search.models(search_opts) |> Enum.take(limit)
      end

    if repos == [] do
      Mix.shell().info("No repositories found.")
    else
      Mix.shell().info(String.pad_trailing("ID", 50) <> "  " <> "Downloads" <> "  " <> "Likes")
      Mix.shell().info(String.duplicate("─", 70))

      Enum.each(repos, fn r ->
        id = r["id"] || r["modelId"] || ""
        downloads = r["downloads"] || r["downloadsAllTime"] || "-"
        likes = r["likes"] || "-"

        line =
          String.pad_trailing(id, 50) <>
            "  " <>
            String.pad_leading("#{downloads}", 9) <>
            "  " <>
            String.pad_leading("#{likes}", 5)

        Mix.shell().info(line)
      end)

      Mix.shell().info("\n#{length(repos)} repository/ies shown")
    end
  end

  defp print_repo_row(r) do
    id = r["id"] || r["modelId"] || ""
    downloads = r["downloads"] || r["downloadsAllTime"] || "-"
    likes = r["likes"] || "-"

    row =
      String.pad_trailing(id, 50) <>
        "  " <>
        String.pad_leading("#{downloads}", 9) <>
        "  " <>
        String.pad_leading("#{likes}", 5)

    Mix.shell().info(row)
  end
end