// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: LGPL-2.1-or-later

/**
 * @file
 *
 * Chains of named handlers.
 */

#ifndef DRGN_HANDLER_H
#define DRGN_HANDLER_H

#include <stdbool.h>
#include <stdlib.h>

// This should be embedded as the first member in a structure containing the
// handler implementation.
struct drgn_handler {
	const char *name;
	struct drgn_handler *next;
	bool enabled;
	// Whether this structure and name need to be freed.
	bool free;
};

// This is optimized for frequent drgn_handler_list_for_each_enabled()
// operations; everything else is expected to be rare, so we keep this as small
// as possible.
struct drgn_handler_list {
	// All enabled handlers are first, in order, followed by disabled
	// handlers in no particular order.
	struct drgn_handler *head;
};

// handler->name and handler->free must be initialized.
struct drgn_error *drgn_handler_list_register(struct drgn_handler_list *list,
					      struct drgn_handler *handler,
					      size_t enable_index,
					      const char *what);

struct drgn_error *drgn_handler_list_registered(struct drgn_handler_list *list,
						const char ***names_ret,
						size_t *count_ret);

struct drgn_error *drgn_handler_list_set_enabled(struct drgn_handler_list *list,
						 const char * const *names,
						 size_t count,
						 const char *what);

struct drgn_error *drgn_handler_list_enabled(struct drgn_handler_list *list,
					     const char ***names_ret,
					     size_t *count_ret);

bool drgn_handler_list_disable(struct drgn_handler_list *list,
			       const char *name);

static inline bool drgn_handler_is_last_enabled(struct drgn_handler *handler)
{
	return handler->enabled && (!handler->next || !handler->next->enabled);
}

// Helper to simplify the casting and naming in drgn_handler_list_deinit().
static inline struct drgn_handler *
drgn_handler_free_and_next(struct drgn_handler *handler)
{
	struct drgn_handler *next = handler->next;
	if (handler->free) {
		free((char *)handler->name);
		free(handler);
	}
	return next;
}

// Free all registered handlers, optionally executing a statement for each one.
#define drgn_handler_list_deinit(type, handler, list, ...) do {	\
	type *handler = (type *)(list)->head;			\
	while (handler) {					\
		__VA_ARGS__					\
		handler = (type *)drgn_handler_free_and_next((struct drgn_handler *)handler);\
	}							\
} while (0)

#define drgn_handler_list_for_each_enabled(type, handler, list)		\
	for (type *handler = (type *)(list)->head;			\
	     handler && ((struct drgn_handler *)handler)->enabled;	\
	     handler = (type *)((struct drgn_handler *)handler)->next)

#endif /* DRGN_HANDLER_H */
