aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl/integer.ccm
blob: 8734c2d1f245d37050c99fe34ba592ac097098b0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// This source code is released into the public domain.
module;

#include <ucl.h>

export module nihil.ucl:integer;

import nihil.std;
import nihil.core;

import :object;
import :type;

namespace nihil::ucl {

export struct integer final : object
{
	using contained_type = std::int64_t;
	static constexpr object_type ucl_type = object_type::integer;

	// Create an integer holding the value 0.  Throws std::system_error
	// on failure.
	integer()
		: integer(0)
	{
	}

	// Create an integer holding a specific value.  Throws std::system_error
	// on failure.
	explicit integer(contained_type value)
		: integer(noref, [&] {
			auto *uobj = ::ucl_object_fromint(value);
			if (uobj == nullptr)
				throw std::system_error(std::make_error_code(sys_error()));
			return uobj;
		}())
	{
	}

	// Create a new integer from a UCL object.  Throws type_mismatch
	// on failure.
	integer(ref_t, ::ucl_object_t const *uobj)
		: object(nihil::ucl::ref, ensure_ucl_type(uobj, integer::ucl_type))
	{
	}

	integer(noref_t, ::ucl_object_t *uobj)
		: object(nihil::ucl::noref, ensure_ucl_type(uobj, integer::ucl_type))
	{
	}

	// Return the value of this object.
	[[nodiscard]] auto value(this integer const &self) -> contained_type
	{
		auto        v = contained_type{};
		auto const *uobj = self.get_ucl_object();

		if (::ucl_object_toint_safe(uobj, &v))
			return v;

		throw std::runtime_error("ucl_object_toint_safe failed");
	}

private:
	//
	// Comparison operators.
	//

	[[nodiscard]] friend auto operator==(integer const &a, integer const &b) -> bool
	{
		return a.value() == b.value();
	}

	[[nodiscard]] friend auto operator==(integer const &a, integer::contained_type b) -> bool
	{
		return a.value() == b;
	}

	[[nodiscard]] friend auto
	operator<=>(integer const &a, integer const &b) -> std::strong_ordering
	{
		return a.value() <=> b.value();
	}

	[[nodiscard]] friend auto
	operator<=>(integer const &a, integer::contained_type b) -> std::strong_ordering
	{
		return a.value() <=> b;
	}
};

// Integer constructors.  This returns an error instead of throwing.
export [[nodiscard]] auto
make_integer(integer::contained_type value = 0) -> std::expected<integer, error>
{
	auto *uobj = ::ucl_object_fromint(value);
	if (uobj == nullptr)
		return error(errc::failed_to_create_object, error(sys_error()));

	return integer(noref, uobj);
}

// Literal operator for integers.
inline namespace literals {
export constexpr auto operator""_ucl(unsigned long long i) -> integer
{
	if (std::cmp_greater(i, std::numeric_limits<std::int64_t>::max()))
		throw std::out_of_range("literal out of range");

	return integer(static_cast<std::int64_t>(i));
}
} // namespace literals

} // namespace nihil::ucl

namespace nihil {
inline namespace literals {
export using namespace ::nihil::ucl::literals;
} // namespace literals
} // namespace nihil

// std::formatter for an integer.  This provides the same format operations
// as std::formatter<std::int64_t>.
export template <>
struct std::formatter<nihil::ucl::integer, char>
{
	std::formatter<std::int64_t> base_formatter;

	template <class ParseContext>
	constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
	{
		return base_formatter.parse(ctx);
	}

	template <class FmtContext>
	auto format(nihil::ucl::integer const &o, FmtContext &ctx) const -> FmtContext::iterator
	{
		return base_formatter.format(o.value(), ctx);
	}
};