scriptlang: lexer done
This commit is contained in:
parent
b838611fa0
commit
f7077274fe
1
.clangd
1
.clangd
@ -14,6 +14,7 @@ Diagnostics:
|
|||||||
- readability-magic-numbers
|
- readability-magic-numbers
|
||||||
- readability-identifier-length
|
- readability-identifier-length
|
||||||
- bugprone-easily-swappable-parameters
|
- bugprone-easily-swappable-parameters
|
||||||
|
- readability-convert-member-functions-to-static
|
||||||
|
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
UnusedIncludes: Strict
|
UnusedIncludes: Strict
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
browser_sources += files(
|
|
||||||
|
browser_sources = files(
|
||||||
'main.cpp',
|
'main.cpp',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fmt_dep = dependency('fmt')
|
||||||
|
sdl2_dep = dependency('sdl2')
|
||||||
|
|
||||||
|
executable(
|
||||||
|
'web-browser',
|
||||||
|
browser_sources,
|
||||||
|
win_subsystem: 'console',
|
||||||
|
dependencies: [
|
||||||
|
fmt_dep,
|
||||||
|
sdl2_dep,
|
||||||
|
utils_dep,
|
||||||
|
markup_dep,
|
||||||
|
scriptlang_dep,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
27
markup/meson.build
Normal file
27
markup/meson.build
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
markup_sources = files(
|
||||||
|
'parser.cpp',
|
||||||
|
)
|
||||||
|
|
||||||
|
markup_inc = include_directories('.')
|
||||||
|
|
||||||
|
fmt_dep = dependency('fmt')
|
||||||
|
|
||||||
|
markup_lib = static_library(
|
||||||
|
'web-markup',
|
||||||
|
markup_sources,
|
||||||
|
include_directories: [
|
||||||
|
markup_inc,
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
fmt_dep,
|
||||||
|
utils_dep,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
markup_dep = declare_dependency(
|
||||||
|
link_with: markup_lib,
|
||||||
|
include_directories: [
|
||||||
|
markup_inc,
|
||||||
|
],
|
||||||
|
)
|
3
markup/parser.cpp
Normal file
3
markup/parser.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#include "parser.hpp"
|
||||||
|
|
||||||
|
namespace languages { }
|
7
markup/parser.hpp
Normal file
7
markup/parser.hpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace markup {
|
||||||
|
|
||||||
|
class Parser final { };
|
||||||
|
|
||||||
|
}
|
37
meson.build
37
meson.build
@ -4,43 +4,12 @@ project(
|
|||||||
version: '0.1',
|
version: '0.1',
|
||||||
default_options: [
|
default_options: [
|
||||||
'warning_level=3',
|
'warning_level=3',
|
||||||
'werror=false',
|
|
||||||
'cpp_std=c++20',
|
'cpp_std=c++20',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
fmt_dep = dependency('fmt')
|
subdir('utils')
|
||||||
sdl2_dep = dependency('sdl2')
|
subdir('scriptlang')
|
||||||
|
subdir('markup')
|
||||||
server_sources = []
|
|
||||||
subdir('server')
|
subdir('server')
|
||||||
server_exe = executable(
|
|
||||||
'web-server',
|
|
||||||
server_sources,
|
|
||||||
win_subsystem: 'console',
|
|
||||||
include_directories: [
|
|
||||||
include_directories('server'),
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
fmt_dep,
|
|
||||||
sdl2_dep,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
browser_sources = []
|
|
||||||
subdir('browser')
|
subdir('browser')
|
||||||
browser_exe = executable(
|
|
||||||
'web-browser',
|
|
||||||
browser_sources,
|
|
||||||
win_subsystem: 'console',
|
|
||||||
include_directories: [
|
|
||||||
include_directories('browser'),
|
|
||||||
],
|
|
||||||
override_options: [
|
|
||||||
'werror=true',
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
fmt_dep,
|
|
||||||
sdl2_dep,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
217
scriptlang/lexer.cpp
Normal file
217
scriptlang/lexer.cpp
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#include "lexer.hpp"
|
||||||
|
#include <cctype>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace scriptlang {
|
||||||
|
|
||||||
|
auto constexpr Lexer::next() noexcept -> Result<Token, void>
|
||||||
|
{
|
||||||
|
if (done())
|
||||||
|
return token(TokenTypes::Eof, index);
|
||||||
|
if (std::isdigit(current()) != 0)
|
||||||
|
return make_number();
|
||||||
|
if (std::isalpha(current()) != 0 || current() == '_')
|
||||||
|
return make_id();
|
||||||
|
return make_static();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto constexpr Lexer::make_number() noexcept -> Result<Token, void>
|
||||||
|
{
|
||||||
|
auto begin = index;
|
||||||
|
while (!done() and std::isdigit(current()) != 0)
|
||||||
|
step();
|
||||||
|
if (current() == '.') {
|
||||||
|
step();
|
||||||
|
while (!done() and std::isdigit(current()) != 0)
|
||||||
|
step();
|
||||||
|
return token(TokenTypes::Float, begin);
|
||||||
|
}
|
||||||
|
return token(TokenTypes::Int, begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
|
auto constexpr Lexer::make_id() noexcept -> Result<Token, void>
|
||||||
|
{
|
||||||
|
auto begin = index;
|
||||||
|
while (!done()
|
||||||
|
and (std::isalpha(current()) != 0 or std::isdigit(current()) != 0
|
||||||
|
or current() == '_'))
|
||||||
|
step();
|
||||||
|
return token(id_or_keyword_type(text.substr(begin, index - begin)), begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto constexpr Lexer::id_or_keyword_type(std::string_view substring) noexcept
|
||||||
|
-> TokenTypes
|
||||||
|
{
|
||||||
|
if (substring.compare("if") == 0)
|
||||||
|
return TokenTypes::If;
|
||||||
|
if (substring.compare("else") == 0)
|
||||||
|
return TokenTypes::Else;
|
||||||
|
if (substring.compare("for") == 0)
|
||||||
|
return TokenTypes::For;
|
||||||
|
if (substring.compare("loop") == 0)
|
||||||
|
return TokenTypes::Loop;
|
||||||
|
if (substring.compare("while") == 0)
|
||||||
|
return TokenTypes::While;
|
||||||
|
if (substring.compare("break") == 0)
|
||||||
|
return TokenTypes::Break;
|
||||||
|
if (substring.compare("continue") == 0)
|
||||||
|
return TokenTypes::Continue;
|
||||||
|
if (substring.compare("fn") == 0)
|
||||||
|
return TokenTypes::Fn;
|
||||||
|
if (substring.compare("return") == 0)
|
||||||
|
return TokenTypes::Return;
|
||||||
|
if (substring.compare("false") == 0)
|
||||||
|
return TokenTypes::False;
|
||||||
|
if (substring.compare("true") == 0)
|
||||||
|
return TokenTypes::True;
|
||||||
|
if (substring.compare("and") == 0)
|
||||||
|
return TokenTypes::And;
|
||||||
|
if (substring.compare("or") == 0)
|
||||||
|
return TokenTypes::Or;
|
||||||
|
if (substring.compare("xor") == 0)
|
||||||
|
return TokenTypes::Xor;
|
||||||
|
return TokenTypes::Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto constexpr Lexer::make_static() noexcept -> Result<Token, void>
|
||||||
|
{
|
||||||
|
auto begin = index;
|
||||||
|
auto type = static_token_type();
|
||||||
|
if (!type)
|
||||||
|
return {};
|
||||||
|
return token(*type, begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
|
auto constexpr Lexer::static_token_type() noexcept -> Result<TokenTypes, void>
|
||||||
|
{
|
||||||
|
using TT = TokenTypes;
|
||||||
|
auto stepped = [&](TokenTypes v) {
|
||||||
|
step();
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (current() == ')')
|
||||||
|
return stepped(TT::LParen);
|
||||||
|
if (current() == '(')
|
||||||
|
return stepped(TT::RParen);
|
||||||
|
if (current() == '}')
|
||||||
|
return stepped(TT::LBrace);
|
||||||
|
if (current() == '{')
|
||||||
|
return stepped(TT::RBrace);
|
||||||
|
if (current() == '[')
|
||||||
|
return stepped(TT::LBracket);
|
||||||
|
if (current() == ']')
|
||||||
|
return stepped(TT::RBracket);
|
||||||
|
if (current() == '.')
|
||||||
|
return stepped(TT::Dot);
|
||||||
|
if (current() == ',')
|
||||||
|
return stepped(TT::Comma);
|
||||||
|
if (current() == ':')
|
||||||
|
return stepped(TT::Colon);
|
||||||
|
if (current() == ';')
|
||||||
|
return stepped(TT::Semicolon);
|
||||||
|
if (current() == ']')
|
||||||
|
return stepped(TT::RBracket);
|
||||||
|
if (current() == '+') {
|
||||||
|
step();
|
||||||
|
if (current() == '+')
|
||||||
|
return stepped(TT::DoublePlus);
|
||||||
|
if (current() == '=')
|
||||||
|
return stepped(TT::PlusEqual);
|
||||||
|
return TT::Plus;
|
||||||
|
}
|
||||||
|
if (current() == '-') {
|
||||||
|
step();
|
||||||
|
if (current() == '>')
|
||||||
|
return stepped(TT::ThinArrow);
|
||||||
|
if (current() == '-')
|
||||||
|
return stepped(TT::DoubleMinus);
|
||||||
|
if (current() == '=')
|
||||||
|
return stepped(TT::MinusEqual);
|
||||||
|
return TT::Minus;
|
||||||
|
}
|
||||||
|
if (current() == '*') {
|
||||||
|
step();
|
||||||
|
if (current() == '=')
|
||||||
|
return TT::AsteriskEqual;
|
||||||
|
return TT::Asterisk;
|
||||||
|
}
|
||||||
|
if (current() == '/') {
|
||||||
|
step();
|
||||||
|
if (current() == '*')
|
||||||
|
return skip_multiline_comment();
|
||||||
|
if (current() == '/')
|
||||||
|
return skip_singleline_comment();
|
||||||
|
if (current() == '=')
|
||||||
|
return TT::SlashEqual;
|
||||||
|
return TT::Slash;
|
||||||
|
}
|
||||||
|
if (current() == '%') {
|
||||||
|
step();
|
||||||
|
if (current() == '=')
|
||||||
|
return TT::PercentEqual;
|
||||||
|
return TT::Percent;
|
||||||
|
}
|
||||||
|
if (current() == '^') {
|
||||||
|
step();
|
||||||
|
if (current() == '=')
|
||||||
|
return TT::PowerEqual;
|
||||||
|
return TT::Power;
|
||||||
|
}
|
||||||
|
if (current() == '=') {
|
||||||
|
step();
|
||||||
|
if (current() == '>')
|
||||||
|
return stepped(TT::FatArrow);
|
||||||
|
if (current() == '=')
|
||||||
|
return stepped(TT::DoubleEqual);
|
||||||
|
return TT::Equal;
|
||||||
|
}
|
||||||
|
if (current() == '!') {
|
||||||
|
step();
|
||||||
|
if (current() == '=')
|
||||||
|
return stepped(TT::ExlamationEqual);
|
||||||
|
return TT::Exlamation;
|
||||||
|
}
|
||||||
|
if (current() == '<') {
|
||||||
|
step();
|
||||||
|
if (current() == '=')
|
||||||
|
return stepped(TT::LessEqual);
|
||||||
|
return TT::Less;
|
||||||
|
}
|
||||||
|
if (current() == '>') {
|
||||||
|
step();
|
||||||
|
if (current() == '=')
|
||||||
|
return stepped(TT::GreaterEqual);
|
||||||
|
return TT::Greater;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto constexpr Lexer::skip_multiline_comment() noexcept
|
||||||
|
-> Result<TokenTypes, void>
|
||||||
|
{
|
||||||
|
step();
|
||||||
|
auto last = current();
|
||||||
|
step();
|
||||||
|
while (!done() and last != '*' and current() != '/')
|
||||||
|
step();
|
||||||
|
if (last != '*' or current() != '/')
|
||||||
|
return {};
|
||||||
|
step();
|
||||||
|
return TokenTypes::MultilineComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto constexpr Lexer::skip_singleline_comment() noexcept
|
||||||
|
-> Result<TokenTypes, void>
|
||||||
|
{
|
||||||
|
step();
|
||||||
|
while (!done() and current() != '\n')
|
||||||
|
step();
|
||||||
|
if (current() == '\n')
|
||||||
|
step();
|
||||||
|
return TokenTypes::SinglelineComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
141
scriptlang/lexer.hpp
Normal file
141
scriptlang/lexer.hpp
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
#include "utils.hpp"
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace scriptlang {
|
||||||
|
|
||||||
|
enum class TokenTypes {
|
||||||
|
Eof,
|
||||||
|
|
||||||
|
MultilineComment,
|
||||||
|
SinglelineComment,
|
||||||
|
|
||||||
|
Id,
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
|
||||||
|
If,
|
||||||
|
Else,
|
||||||
|
For,
|
||||||
|
Loop,
|
||||||
|
While,
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
Fn,
|
||||||
|
Return,
|
||||||
|
False,
|
||||||
|
True,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Xor,
|
||||||
|
|
||||||
|
LParen,
|
||||||
|
RParen,
|
||||||
|
LBrace,
|
||||||
|
RBrace,
|
||||||
|
LBracket,
|
||||||
|
RBracket,
|
||||||
|
Dot,
|
||||||
|
Comma,
|
||||||
|
Colon,
|
||||||
|
Semicolon,
|
||||||
|
|
||||||
|
Plus,
|
||||||
|
DoublePlus,
|
||||||
|
PlusEqual,
|
||||||
|
|
||||||
|
Minus,
|
||||||
|
ThinArrow,
|
||||||
|
DoubleMinus,
|
||||||
|
MinusEqual,
|
||||||
|
|
||||||
|
Asterisk,
|
||||||
|
AsteriskEqual,
|
||||||
|
|
||||||
|
Slash,
|
||||||
|
SlashEqual,
|
||||||
|
|
||||||
|
Percent,
|
||||||
|
PercentEqual,
|
||||||
|
|
||||||
|
Power,
|
||||||
|
PowerEqual,
|
||||||
|
|
||||||
|
Equal,
|
||||||
|
FatArrow,
|
||||||
|
DoubleEqual,
|
||||||
|
|
||||||
|
Exlamation,
|
||||||
|
ExlamationEqual,
|
||||||
|
|
||||||
|
Less,
|
||||||
|
LessEqual,
|
||||||
|
|
||||||
|
Greater,
|
||||||
|
GreaterEqual,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Token {
|
||||||
|
TokenTypes type;
|
||||||
|
size_t index, length;
|
||||||
|
int line, column;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Lexer {
|
||||||
|
public:
|
||||||
|
Lexer(std::string_view text)
|
||||||
|
: text { text }
|
||||||
|
{ }
|
||||||
|
auto constexpr next() noexcept -> Result<Token, void>;
|
||||||
|
auto peek() noexcept -> Result<Token, void>
|
||||||
|
{
|
||||||
|
if (last_token)
|
||||||
|
return Result<Token, void>::create_ok(*last_token);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto constexpr make_number() noexcept -> Result<Token, void>;
|
||||||
|
auto constexpr make_id() noexcept -> Result<Token, void>;
|
||||||
|
auto constexpr id_or_keyword_type(std::string_view substring) noexcept
|
||||||
|
-> TokenTypes;
|
||||||
|
auto constexpr make_static() noexcept -> Result<Token, void>;
|
||||||
|
auto constexpr static_token_type() noexcept -> Result<TokenTypes, void>;
|
||||||
|
auto constexpr skip_multiline_comment() noexcept
|
||||||
|
-> Result<TokenTypes, void>;
|
||||||
|
auto constexpr skip_singleline_comment() noexcept
|
||||||
|
-> Result<TokenTypes, void>;
|
||||||
|
|
||||||
|
[[nodiscard]] auto constexpr inline token(
|
||||||
|
TokenTypes type, size_t begin) const noexcept -> Token
|
||||||
|
{
|
||||||
|
return Token { type, begin, index - begin, line, column };
|
||||||
|
}
|
||||||
|
[[nodiscard]] auto constexpr inline done() const noexcept -> bool
|
||||||
|
{
|
||||||
|
return index >= text.size();
|
||||||
|
}
|
||||||
|
[[nodiscard]] auto constexpr inline current() const noexcept -> char
|
||||||
|
{
|
||||||
|
return text.at(index);
|
||||||
|
}
|
||||||
|
auto constexpr inline step() noexcept -> void
|
||||||
|
{
|
||||||
|
if (done())
|
||||||
|
return;
|
||||||
|
index++;
|
||||||
|
column++;
|
||||||
|
if (!done() and text.at(index) == '\n') {
|
||||||
|
column = 1;
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view text;
|
||||||
|
size_t index = 0;
|
||||||
|
int line = 1;
|
||||||
|
int column = 1;
|
||||||
|
std::optional<Token> last_token;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
28
scriptlang/meson.build
Normal file
28
scriptlang/meson.build
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
scriptlang_sources = files(
|
||||||
|
'parser.cpp',
|
||||||
|
'lexer.cpp',
|
||||||
|
)
|
||||||
|
|
||||||
|
scriptlang_inc = include_directories('.')
|
||||||
|
|
||||||
|
fmt_dep = dependency('fmt')
|
||||||
|
|
||||||
|
scriptlang_lib = static_library(
|
||||||
|
'web-scriptlang',
|
||||||
|
scriptlang_sources,
|
||||||
|
include_directories: [
|
||||||
|
scriptlang_inc,
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
fmt_dep,
|
||||||
|
utils_dep,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
scriptlang_dep = declare_dependency(
|
||||||
|
link_with: scriptlang_lib,
|
||||||
|
include_directories: [
|
||||||
|
scriptlang_inc,
|
||||||
|
],
|
||||||
|
)
|
3
scriptlang/parser.cpp
Normal file
3
scriptlang/parser.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#include "parser.hpp"
|
||||||
|
|
||||||
|
namespace scriptlang { }
|
7
scriptlang/parser.hpp
Normal file
7
scriptlang/parser.hpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace scriptlang {
|
||||||
|
|
||||||
|
class Parser final { };
|
||||||
|
|
||||||
|
}
|
@ -1,3 +1,17 @@
|
|||||||
server_sources += files(
|
|
||||||
|
server_sources = files(
|
||||||
'main.cpp',
|
'main.cpp',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fmt_dep = dependency('fmt')
|
||||||
|
|
||||||
|
server_exe = executable(
|
||||||
|
'web-server',
|
||||||
|
server_sources,
|
||||||
|
win_subsystem: 'console',
|
||||||
|
dependencies: [
|
||||||
|
fmt_dep,
|
||||||
|
utils_dep,
|
||||||
|
markup_dep,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
24
utils/meson.build
Normal file
24
utils/meson.build
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
utils_sources = files()
|
||||||
|
|
||||||
|
utils_inc = include_directories('.')
|
||||||
|
|
||||||
|
fmt_dep = dependency('fmt')
|
||||||
|
|
||||||
|
utils_lib = static_library(
|
||||||
|
'web-utils',
|
||||||
|
utils_sources,
|
||||||
|
include_directories: [
|
||||||
|
utils_inc,
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
fmt_dep,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
utils_dep = declare_dependency(
|
||||||
|
link_with: utils_lib,
|
||||||
|
include_directories: [
|
||||||
|
utils_inc,
|
||||||
|
],
|
||||||
|
)
|
645
utils/result.hpp
Normal file
645
utils/result.hpp
Normal file
@ -0,0 +1,645 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace utils::result {
|
||||||
|
|
||||||
|
class UnwrapError : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
UnwrapError()
|
||||||
|
: std::runtime_error("failed to unwrap result")
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StatesValueTypes {
|
||||||
|
struct Value { };
|
||||||
|
struct Error { };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename InnerValue> struct Extracter {
|
||||||
|
using Value = void;
|
||||||
|
using Error = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Value, typename Error> class [[nodiscard]] Result {
|
||||||
|
static_assert(std::is_object_v<Value> && std::is_destructible_v<Value>,
|
||||||
|
"incompatible Value in Result<Value, Error>");
|
||||||
|
static_assert(std::is_object_v<Error> && std::is_destructible_v<Error>,
|
||||||
|
"incompatible Error in Result<Value, Error>");
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Result(Value&& value) noexcept
|
||||||
|
: value_or_error(std::forward<Value>(value))
|
||||||
|
{ }
|
||||||
|
constexpr Result(Error&& error) noexcept
|
||||||
|
: value_or_error(std::forward<Error>(error))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit constexpr Result(
|
||||||
|
Value&& value, [[maybe_unused]] StatesValueTypes::Value svt) noexcept
|
||||||
|
: value_or_error(std::forward<Value>(value))
|
||||||
|
{ }
|
||||||
|
explicit constexpr Result(
|
||||||
|
Error&& error, [[maybe_unused]] StatesValueTypes::Error svt) noexcept
|
||||||
|
: value_or_error(std::forward<Error>(error))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[[nodiscard]] static auto create_ok(Value value) noexcept
|
||||||
|
{
|
||||||
|
return Result<Value, Error>(
|
||||||
|
std::move(value), StatesValueTypes::Value {});
|
||||||
|
}
|
||||||
|
[[nodiscard]] static auto create_error(Error error) noexcept
|
||||||
|
{
|
||||||
|
return Result<Value, Error>(
|
||||||
|
std::move(error), StatesValueTypes::Error {});
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto is_ok() const noexcept -> bool
|
||||||
|
{
|
||||||
|
return std::holds_alternative<Value>(value_or_error);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto is_error() const noexcept -> bool
|
||||||
|
{
|
||||||
|
return std::holds_alternative<Error>(value_or_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto unwrap() & -> Value&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::get<Value>(value_or_error);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto unwrap() const& -> const Value&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::get<Value>(value_or_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto unwrap_error() & -> Error&
|
||||||
|
{
|
||||||
|
if (!is_error())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::get<Error>(value_or_error);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto unwrap_error() const& -> const Error&
|
||||||
|
{
|
||||||
|
if (!is_error())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::get<Error>(value_or_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto unwrap() && -> Value&&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::move(std::get<Value>(value_or_error));
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto unwrap() const&& -> const Value&&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::move(std::get<Value>(value_or_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto unwrap_error() && -> Error&&
|
||||||
|
{
|
||||||
|
if (!is_error())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::move(std::get<Error>(value_or_error));
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto unwrap_error() const&& -> const Error&&
|
||||||
|
{
|
||||||
|
if (!is_error())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::move(std::get<Error>(value_or_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto ok() noexcept -> std::optional<Value&>
|
||||||
|
{
|
||||||
|
return is_ok() ? unwrap() : std::nullopt;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto ok() const noexcept
|
||||||
|
-> std::optional<const Value&>
|
||||||
|
{
|
||||||
|
return is_ok() ? unwrap() : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto error() noexcept -> std::optional<Error&>
|
||||||
|
{
|
||||||
|
return is_error() ? unwrap_error() : std::nullopt;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto error() const noexcept
|
||||||
|
-> std::optional<const Error&>
|
||||||
|
{
|
||||||
|
return is_error() ? unwrap_error() : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr operator bool() const noexcept { return is_ok(); }
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto operator->() -> Value* { return &unwrap(); }
|
||||||
|
[[nodiscard]] constexpr auto operator->() const -> const Value*
|
||||||
|
{
|
||||||
|
return &unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto operator*() -> Value& { return unwrap(); }
|
||||||
|
[[nodiscard]] constexpr auto operator*() const -> const Value&
|
||||||
|
{
|
||||||
|
return unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map(auto func) noexcept
|
||||||
|
{
|
||||||
|
using NewValue = decltype(func(unwrap()));
|
||||||
|
return is_ok() ? Result<NewValue, Error>(func(unwrap()))
|
||||||
|
: Result<NewValue, Error>(unwrap_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map(auto func) const noexcept
|
||||||
|
{
|
||||||
|
using NewValue = decltype(func(unwrap()));
|
||||||
|
return is_ok() ? Result<NewValue, Error>(func(unwrap()))
|
||||||
|
: Result<NewValue, Error>(unwrap_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map_error(auto func) noexcept
|
||||||
|
{
|
||||||
|
using NewError = decltype(func(unwrap_error()));
|
||||||
|
return is_error() ? Result<Value, NewError>(func(unwrap_error()))
|
||||||
|
: Result<Value, NewError>(unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map_error(auto func) const noexcept
|
||||||
|
{
|
||||||
|
using NewError = decltype(func(unwrap_error()));
|
||||||
|
return is_error() ? Result<Value, NewError>(func(unwrap_error()))
|
||||||
|
: Result<Value, NewError>(unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept
|
||||||
|
requires std::same_as<decltype(if_ok()), decltype(if_error())>
|
||||||
|
{
|
||||||
|
return is_ok() ? if_ok(unwrap()) : if_error(unwrap_error());
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept
|
||||||
|
requires std::same_as<decltype(if_ok()), decltype(if_error())>
|
||||||
|
{
|
||||||
|
return is_ok() ? if_ok(unwrap()) : if_error(unwrap_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flatten() noexcept
|
||||||
|
requires std::same_as<Error, typename Extracter<Value>::Error>
|
||||||
|
{
|
||||||
|
using InnerValue = typename Extracter<Value>::Value;
|
||||||
|
return is_ok() ? unwrap() : Result<InnerValue, Error>(unwrap_error());
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto flatten() const noexcept
|
||||||
|
requires std::same_as<Error, typename Extracter<Value>::Error>
|
||||||
|
{
|
||||||
|
using InnerValue = typename Extracter<Value>::Value;
|
||||||
|
return is_ok() ? unwrap() : Result<InnerValue, Error>(unwrap_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flatten_error() noexcept
|
||||||
|
requires std::same_as<Value, typename Extracter<Error>::Value>
|
||||||
|
{
|
||||||
|
using InnerError = typename Extracter<Error>::Error;
|
||||||
|
return is_error() ? unwrap_error()
|
||||||
|
: Result<Value, InnerError>(unwrap());
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto flatten_error() const noexcept
|
||||||
|
requires std::same_as<Value, typename Extracter<Error>::Value>
|
||||||
|
{
|
||||||
|
using InnerError = typename Extracter<Error>::Error;
|
||||||
|
return is_error() ? unwrap_error()
|
||||||
|
: Result<Value, InnerError>(unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map(auto func) noexcept
|
||||||
|
{
|
||||||
|
return map(func).flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map(auto func) const noexcept
|
||||||
|
{
|
||||||
|
return map(func).flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map_error(auto func) noexcept
|
||||||
|
{
|
||||||
|
return map_error(func).flatten_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map_error(auto func) const noexcept
|
||||||
|
{
|
||||||
|
return map_error(func).flatten_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::variant<Value, Error> value_or_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ValueInferer, typename ErrorInferer>
|
||||||
|
struct Extracter<Result<ValueInferer, ErrorInferer>> {
|
||||||
|
using Value = ValueInferer;
|
||||||
|
using Error = ErrorInferer;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Value> class [[nodiscard]] Result<Value, void> {
|
||||||
|
static_assert(std::is_object_v<Value> && std::is_destructible_v<Value>,
|
||||||
|
"incompatible Error in Result<Value, Error>");
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Result(Value&& value) noexcept
|
||||||
|
: maybe_value { std::forward<Value>(value) } {};
|
||||||
|
constexpr Result() noexcept
|
||||||
|
: maybe_value {}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[[nodiscard]] static auto create_ok(Value value) noexcept
|
||||||
|
{
|
||||||
|
return Result<Value, void>(std::move(value));
|
||||||
|
}
|
||||||
|
[[nodiscard]] static auto create_error() noexcept
|
||||||
|
{
|
||||||
|
return Result<Value, void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto is_ok() const noexcept
|
||||||
|
{
|
||||||
|
return maybe_value.has_value();
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto is_error() const noexcept
|
||||||
|
{
|
||||||
|
return !maybe_value.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto unwrap() & -> Value&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw UnwrapError();
|
||||||
|
return *maybe_value;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto unwrap() const& -> const Value&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw UnwrapError();
|
||||||
|
return *maybe_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto unwrap() && -> Value&&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::move(*maybe_value);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto unwrap() const&& -> const Value&&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::move(*maybe_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto ok() noexcept -> std::optional<Value&>
|
||||||
|
{
|
||||||
|
return is_ok() ? unwrap() : std::nullopt;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto ok() const noexcept
|
||||||
|
-> std::optional<const Value&>
|
||||||
|
{
|
||||||
|
return is_ok() ? unwrap() : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr operator bool() const noexcept { return is_ok(); }
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto operator->() -> Value* { return &unwrap(); }
|
||||||
|
[[nodiscard]] constexpr auto operator->() const -> const Value*
|
||||||
|
{
|
||||||
|
return &unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto operator*() -> Value& { return unwrap(); }
|
||||||
|
[[nodiscard]] constexpr auto operator*() const -> const Value&
|
||||||
|
{
|
||||||
|
return unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map(auto func) noexcept
|
||||||
|
{
|
||||||
|
using NewValue = decltype(func(unwrap()));
|
||||||
|
return is_ok() ? Result<NewValue, void>(func(unwrap()))
|
||||||
|
: Result<NewValue, void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map(auto func) const noexcept
|
||||||
|
{
|
||||||
|
using NewValue = decltype(func(unwrap()));
|
||||||
|
return is_ok() ? Result<NewValue, void>(func(unwrap()))
|
||||||
|
: Result<NewValue, void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map_error(auto func) noexcept
|
||||||
|
{
|
||||||
|
using NewError = decltype(func());
|
||||||
|
return is_error() ? Result<Value, NewError>(func())
|
||||||
|
: Result<Value, NewError>(unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map_error(auto func) const noexcept
|
||||||
|
{
|
||||||
|
using NewError = decltype(func());
|
||||||
|
return is_error() ? Result<Value, NewError>(func())
|
||||||
|
: Result<Value, NewError>(unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept
|
||||||
|
requires std::same_as<decltype(if_ok()), decltype(if_error())>
|
||||||
|
{
|
||||||
|
return is_ok() ? if_ok(unwrap()) : if_error();
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept
|
||||||
|
requires std::same_as<decltype(if_ok()), decltype(if_error())>
|
||||||
|
{
|
||||||
|
return is_ok() ? if_ok(unwrap()) : if_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flatten() noexcept
|
||||||
|
requires std::same_as<void, typename Extracter<Value>::Error>
|
||||||
|
{
|
||||||
|
using InnerValue = typename Extracter<Value>::Value;
|
||||||
|
return is_ok() ? unwrap() : Result<InnerValue, void>();
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto flatten() const noexcept
|
||||||
|
requires std::same_as<void, typename Extracter<Value>::Error>
|
||||||
|
{
|
||||||
|
using InnerValue = typename Extracter<Value>::Value;
|
||||||
|
return is_ok() ? unwrap() : Result<InnerValue, void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map(auto func) noexcept
|
||||||
|
{
|
||||||
|
return map(func).flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map(auto func) const noexcept
|
||||||
|
{
|
||||||
|
return map(func).flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map_error(auto func) noexcept
|
||||||
|
{
|
||||||
|
return map_error(func).flatten_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map_error(auto func) const noexcept
|
||||||
|
{
|
||||||
|
return map_error(func).flatten_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<Value> maybe_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Error> class [[nodiscard]] Result<void, Error> {
|
||||||
|
static_assert(std::is_object_v<Error> && std::is_destructible_v<Error>,
|
||||||
|
"incompatible Error in Result<Value, Error>");
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Result(Error&& error) noexcept
|
||||||
|
: maybe_error { std::forward<Error>(error) }
|
||||||
|
{ }
|
||||||
|
constexpr Result() noexcept
|
||||||
|
: maybe_error {} {};
|
||||||
|
|
||||||
|
[[nodiscard]] static auto create_ok() noexcept
|
||||||
|
{
|
||||||
|
return Result<void, Error>();
|
||||||
|
}
|
||||||
|
[[nodiscard]] static auto create_error(Error error) noexcept
|
||||||
|
{
|
||||||
|
return Result<void, Error>(std::move(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto is_ok() const noexcept
|
||||||
|
{
|
||||||
|
return !maybe_error.has_value();
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto is_error() const noexcept
|
||||||
|
{
|
||||||
|
return maybe_error.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto unwrap_error() & -> Error&
|
||||||
|
{
|
||||||
|
if (!is_error())
|
||||||
|
throw UnwrapError();
|
||||||
|
return *maybe_error;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto unwrap_error() const& -> const Error&
|
||||||
|
{
|
||||||
|
if (!is_error())
|
||||||
|
throw UnwrapError();
|
||||||
|
return *maybe_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto unwrap_error() && -> Error&&
|
||||||
|
{
|
||||||
|
if (!is_error())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::move(*maybe_error);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto unwrap_error() const&& -> const Error&&
|
||||||
|
{
|
||||||
|
if (!is_error())
|
||||||
|
throw UnwrapError();
|
||||||
|
return std::move(*maybe_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto error() noexcept -> std::optional<Error&>
|
||||||
|
{
|
||||||
|
return is_error() ? unwrap_error() : std::nullopt;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto error() const noexcept
|
||||||
|
-> std::optional<const Error&>
|
||||||
|
{
|
||||||
|
return is_error() ? unwrap_error() : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr operator bool() const noexcept { return is_ok(); }
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map(auto func) noexcept
|
||||||
|
{
|
||||||
|
using NewValue = decltype(func());
|
||||||
|
return is_ok() ? Result<NewValue, Error>(func())
|
||||||
|
: Result<NewValue, Error>(unwrap_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map(auto func) const noexcept
|
||||||
|
{
|
||||||
|
using NewValue = decltype(func());
|
||||||
|
return is_ok() ? Result<NewValue, Error>(func())
|
||||||
|
: Result<NewValue, Error>(unwrap_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map_error(auto func) noexcept
|
||||||
|
{
|
||||||
|
using NewError = decltype(func(unwrap_error()));
|
||||||
|
return is_error() ? Result<void, NewError>(func(unwrap_error()))
|
||||||
|
: Result<void, NewError>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map_error(auto func) const noexcept
|
||||||
|
{
|
||||||
|
using NewError = decltype(func(unwrap_error()));
|
||||||
|
return is_error() ? Result<void, NewError>(func(unwrap_error()))
|
||||||
|
: Result<void, NewError>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept
|
||||||
|
requires std::same_as<decltype(if_ok()), decltype(if_error())>
|
||||||
|
{
|
||||||
|
return is_ok() ? if_ok() : if_error(unwrap_error());
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept
|
||||||
|
requires std::same_as<decltype(if_ok()), decltype(if_error())>
|
||||||
|
{
|
||||||
|
return is_ok() ? if_ok() : if_error(unwrap_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flatten_error() noexcept
|
||||||
|
requires std::same_as<void, typename Extracter<Error>::Value>
|
||||||
|
{
|
||||||
|
using InnerError = typename Extracter<Error>::Error;
|
||||||
|
return is_error() ? unwrap_error() : Result<void, InnerError>();
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto flatten_error() const noexcept
|
||||||
|
requires std::same_as<void, typename Extracter<Error>::Value>
|
||||||
|
{
|
||||||
|
using InnerError = typename Extracter<Error>::Error;
|
||||||
|
return is_error() ? unwrap_error() : Result<void, InnerError>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map(auto func) noexcept
|
||||||
|
{
|
||||||
|
return map(func).flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map(auto func) const noexcept
|
||||||
|
{
|
||||||
|
return map(func).flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map_error(auto func) noexcept
|
||||||
|
{
|
||||||
|
return map_error(func).flatten_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map_error(auto func) const noexcept
|
||||||
|
{
|
||||||
|
return map_error(func).flatten_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<Error> maybe_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> class [[nodiscard]] Result<void, void> {
|
||||||
|
public:
|
||||||
|
enum class States { Ok, Error };
|
||||||
|
|
||||||
|
explicit constexpr Result(States state) noexcept
|
||||||
|
: state { state } {};
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr auto create_ok() noexcept
|
||||||
|
{
|
||||||
|
return Result<void, void>(States::Ok);
|
||||||
|
}
|
||||||
|
[[nodiscard]] static constexpr auto create_error() noexcept
|
||||||
|
{
|
||||||
|
return Result<void, void>(States::Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto is_ok() const noexcept
|
||||||
|
{
|
||||||
|
return state == States::Ok;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto is_error() const noexcept
|
||||||
|
{
|
||||||
|
return state == States::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr operator bool() const noexcept { return is_ok(); }
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map(auto func) noexcept
|
||||||
|
{
|
||||||
|
using NewValue = decltype(func());
|
||||||
|
return is_ok() ? Result<NewValue, void>(func())
|
||||||
|
: Result<NewValue, void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map(auto func) const noexcept
|
||||||
|
{
|
||||||
|
using NewValue = decltype(func());
|
||||||
|
return is_ok() ? Result<NewValue, void>(func())
|
||||||
|
: Result<NewValue, void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map_error(auto func) noexcept
|
||||||
|
{
|
||||||
|
using NewError = decltype(func());
|
||||||
|
return is_error() ? Result<void, NewError>(func())
|
||||||
|
: Result<void, NewError>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto map_error(auto func) const noexcept
|
||||||
|
{
|
||||||
|
using NewError = decltype(func());
|
||||||
|
return is_error() ? Result<void, NewError>(func())
|
||||||
|
: Result<void, NewError>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) noexcept
|
||||||
|
requires std::same_as<decltype(if_ok()), decltype(if_error())>
|
||||||
|
{
|
||||||
|
return is_ok() ? if_ok() : if_error();
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto match(auto if_ok, auto if_error) const noexcept
|
||||||
|
requires std::same_as<decltype(if_ok()), decltype(if_error())>
|
||||||
|
{
|
||||||
|
return is_ok() ? if_ok() : if_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map(auto func) noexcept
|
||||||
|
{
|
||||||
|
return map(func).flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map(auto func) const noexcept
|
||||||
|
{
|
||||||
|
return map(func).flatten();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map_error(auto func) noexcept
|
||||||
|
{
|
||||||
|
return map_error(func).flatten_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto flat_map_error(auto func) const noexcept
|
||||||
|
{
|
||||||
|
return map_error(func).flatten_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
States state;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
7
utils/utils.hpp
Normal file
7
utils/utils.hpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "result.hpp"
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
using result::Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
using utils::Result;
|
Loading…
Reference in New Issue
Block a user