/*
 * Copyright © 2025 Red Hat, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#pragma once

#include "config.h"

#include <stdbool.h>

#include "util-macros.h"

/**
 * This is a C version of the Rust newtypes in the
 * form struct Foo(u32);
 *
 * Use: DECLARE_NEWTYPE(foo, int)
 *
 * Defines a single-value struct called foo_t
 * with the following helper functions:
 *
 * - int foo_as_int(foo_t f);
 * - int foo(foo f);
 * - foo_t foo_from_int(int);
 * - foo_t foo_copy(foo_t f);
 * - foo_t foo_min(foo_t a, foo_t b);
 * - foo_t foo_max(foo_t a, foo_t b);
 * - int foo_cmp(foo_t a, foo_t b);
 * - bool foo_eq(foo_t a, int b);
 * - bool foo_ne(foo_t a, int b);
 * - bool foo_le(foo_t a, int b);
 * - bool foo_lt(foo_t a, int b);
 * - bool foo_ge(foo_t a, int b);
 * - bool foo_gt(foo_t a, int b);
 *
 * Since all the structs are single-value the provided functions don't
 * use pointers but pass the struct around as value. The compiler
 * hopefully optimizes things so this is the same as passing ints around
 * but having some better type-safety.
 *
 * For example, this logic error is no longer possible using newtypes:
 * ```
 * double cm_to_inches(int cm) { return cm / 2.54; }
 *
 * struct person {
 *      int age;
 *      int height;
 * };
 * struct person p = { .age = 20, .height = 180 };
 * double inches = cm_to_inches(p.age);
 * ```
 * With newtypes this would be:
 * ```
 * DECLARE_NEWTYPE(year, int);
 * DECLARE_NEWTYPE(cm, int);
 * DECLARE_NEWTYPE(in, double);
 *
 * in_t cm_to_inches(cm_t cm) { return in_from_double(cm_as_int(cm) / 2.54); }
 *
 * struct person {
 *      year_t age;
 *      cm_t height;
 * };
 * struct person p = { .age = year_from_int(20), .height = cm_from_int(180) };
 * in_t inches = cm_to_inches(p.age); // Compiler error!
 * in_t inches = cm_to_inches(p.height); // Yay
 * ```
 * And a side-effect is that the units are documented as part of the type.
 */
#define DECLARE_NEWTYPE(name_, type_) \
	typedef struct { \
		type_ v; \
	} name_##_t; \
	\
	static inline name_##_t name_##_from_##type_(type_ v) { return (name_##_t) { .v = v }; }; \
	static inline type_ name_##_as_##type_(name_##_t name_) { return name_.v; }; \
	static inline type_ name_(name_##_t name_) { return name_##_as_##type_(name_); } \
	static inline name_##_t name_##_copy(name_##_t name_) { return name_##_from_##type_(name_.v); }; \
	static inline name_##_t name_##_min(name_##_t a, name_##_t b) { \
		return name_##_from_##type_(min(a.v, b.v)); \
	}; \
	static inline name_##_t name_##_max(name_##_t a, name_##_t b) { \
		return name_##_from_##type_(max(a.v, b.v)); \
	}; \
	static inline int name_##_cmp(name_##_t a, name_##_t b) { \
		return a.v < b.v ? -1 : (a.v > b.v ? 1 : 0); \
	}; \
	static inline bool name_##_eq(name_##_t a, type_ b) { return a.v == b; }\
	static inline bool name_##_ne(name_##_t a, type_ b) { return a.v != b; }\
	static inline bool name_##_le(name_##_t a, type_ b) { return a.v <= b; }\
	static inline bool name_##_lt(name_##_t a, type_ b) { return a.v < b; }\
	static inline bool name_##_ge(name_##_t a, type_ b) { return a.v >= b; }\
	static inline bool name_##_gt(name_##_t a, type_ b) { return a.v > b; }\
	struct __useless_struct_to_allow_trailing_semicolon__
