🏡 index : quine-global-labs/bazaar.git

/* search-index-write.c
 *
 * Copyright 2026 Alexander Vanhee
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#include "config.h"

#define G_LOG_DOMAIN  "BAZAAR::SEARCH-INDEX-WRITE"
#define BAZAAR_MODULE "search-index-writer"

#include "bz-entry-group.h"
#include "search-index-write.h"

#define INDEX_MAGIC         "BZSI"
#define INDEX_VERSION       1
#define DESCRIPTION_MAX_LEN 200

static gboolean    write_string (GDataOutputStream *out, const char *str, gsize max_len, GError **error);
static gboolean    entry_group_is_eligible (BzEntryGroup *group);
static const char *entry_group_icon_path (BzEntryGroup *group);

gboolean
bz_write_search_index (GListModel *groups,
                       const char *out_path,
                       GError    **error)
{
  g_autofree char *tmp_path          = NULL;
  g_autoptr (GFile) tmp_file         = NULL;
  g_autoptr (GFile) final_file       = NULL;
  g_autoptr (GOutputStream) base_out = NULL;
  g_autoptr (GDataOutputStream) out  = NULL;
  guint    n_groups                  = 0;
  guint32  count                     = 0;
  gboolean result                    = FALSE;

  g_return_val_if_fail (G_IS_LIST_MODEL (groups), FALSE);
  g_return_val_if_fail (out_path != NULL, FALSE);

  tmp_path = g_strdup_printf ("%s.tmp", out_path);
  tmp_file = g_file_new_for_path (tmp_path);

  base_out = G_OUTPUT_STREAM (g_file_replace (
      tmp_file, NULL, FALSE,
      G_FILE_CREATE_REPLACE_DESTINATION, NULL, error));
  if (base_out == NULL)
    return FALSE;

  out = g_data_output_stream_new (base_out);
  g_data_output_stream_set_byte_order (out, G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN);

  if (!g_output_stream_write_all (G_OUTPUT_STREAM (out), INDEX_MAGIC, 4, NULL, NULL, error))
    return FALSE;
  if (!g_data_output_stream_put_uint32 (out, INDEX_VERSION, NULL, error))
    return FALSE;

  n_groups = g_list_model_get_n_items (groups);
  for (guint i = 0; i < n_groups; i++)
    {
      g_autoptr (BzEntryGroup) group = g_list_model_get_item (groups, i);
      if (entry_group_is_eligible (group))
        count++;
    }

  if (!g_data_output_stream_put_uint32 (out, count, NULL, error))
    return FALSE;

  for (guint i = 0; i < n_groups; i++)
    {
      g_autoptr (BzEntryGroup) group = g_list_model_get_item (groups, i);
      const char *icon_path          = NULL;

      if (!entry_group_is_eligible (group))
        continue;

      icon_path = entry_group_icon_path (group);

      if (!write_string (out, bz_entry_group_get_id (group), 0, error) ||
          !write_string (out, bz_entry_group_get_title (group), 0, error) ||
          !write_string (out, bz_entry_group_get_developer (group), 0, error) ||
          !write_string (out, bz_entry_group_get_description (group), DESCRIPTION_MAX_LEN, error) ||
          !write_string (out, bz_entry_group_get_search_tokens (group), 0, error) ||
          !write_string (out, icon_path, 0, error))
        return FALSE;
    }

  if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, error))
    return FALSE;

  final_file = g_file_new_for_path (out_path);
  result     = g_file_move (
      tmp_file, final_file,
      G_FILE_COPY_OVERWRITE,
      NULL, NULL, NULL, error);

  return result;
}

static gboolean
write_string (GDataOutputStream *out,
              const char        *str,
              gsize              max_len,
              GError           **error)
{
  gsize len = 0;

  if (str != NULL)
    {
      len = strlen (str);
      if (max_len > 0 && len > max_len)
        len = max_len;
    }

  if (!g_data_output_stream_put_uint32 (out, (guint32) len, NULL, error))
    return FALSE;

  if (len > 0)
    return g_output_stream_write_all (G_OUTPUT_STREAM (out), str, len, NULL, NULL, error);

  return TRUE;
}

static gboolean
entry_group_is_eligible (BzEntryGroup *group)
{
  return bz_entry_group_is_searchable (group) &&
         !bz_entry_group_is_addon (group) &&
         bz_entry_group_get_removable (group) == 0 &&
         bz_entry_group_get_eol (group) == NULL;
}

static const char *
entry_group_icon_path (BzEntryGroup *group)
{
  GIcon *icon = NULL;

  icon = bz_entry_group_get_mini_icon (group);
  if (icon == NULL || !G_IS_FILE_ICON (icon))
    return NULL;

  return g_file_peek_path (g_file_icon_get_file (G_FILE_ICON (icon)));
}