feat: add more API methods to Result and Option

This commit is contained in:
DL
2024-02-29 18:03:55 +01:00
parent b8ac4c450c
commit e48a0430b6
2 changed files with 431 additions and 142 deletions

View File

@ -1,38 +1,42 @@
#include <string>
#include "../src/ro.h"
#include <functional>
#include <functional>
#include <iostream>
#include <cstring>
using namespace std;
using namespace ro;
struct Stat{
struct Stat
{
int passed;
int total;
};
static Stat g_stat = {0,0};
static Stat g_stat = {0, 0};
#define assert(v,fmt,...) if(!(v)) { \
throw Error(ro::sfmt("ASSERT ERROR %s:%d: " fmt, __FILE__, __LINE__, ##__VA_ARGS__)); \
}
#define assert(v, fmt, ...) \
if (!(v)) \
{ \
throw Error(ro::sfmt("ASSERT ERROR %s:%d: " fmt, __FILE__, __LINE__, ##__VA_ARGS__)); \
}
class TestError: public Error {
class TestError : public Error
{
public:
TestError(const char *s) : Error(s){};
};
void test(const char* desc, const std::function<void()>& lambda)
void test(const char *desc, const std::function<void()> &lambda)
{
std::cout << "Run test: " << desc << "..........";
try {
try
{
lambda();
std::cout << "OK" << std::endl;
g_stat.passed++;
}
catch(ro::Error e)
catch (ro::Error e)
{
std::cout << "FAILED" << std::endl;
std::cout << e.what() << std::endl;
@ -40,47 +44,63 @@ void test(const char* desc, const std::function<void()>& lambda)
g_stat.total++;
}
Result<int, Error> test_macro(const Result<int, Error> & target)
Result<int, Error> test_macro(const Result<int, Error> &target)
{
int a;
try_unwrap(a,target);
try_unwrap(a, target);
return a;
}
class Point
{
private:
int x;
int y;
public:
Point(int a, int b) : x(a), y(b){};
Point(){};
inline friend bool operator==(const Point &l, const Point &r)
{
return l.x == r.x && l.y == r.y;
}
};
int main(int argc, char const *argv[])
{
test("Test Option", [](){
test("Test Option", []()
{
Option<string> opt;
assert(opt.is_none(), "Option is not empty");
assert(! opt.is_some(), "Option has some value");
string text = "This is some data";
opt = text;
assert(opt.is_some(), "Option shall has some value");
assert(opt.unwrap() == text,"Value mistmatch");
});
assert(opt.unwrap() == text,"Value mismatch"); });
test("Option -> Result convert", [](){
test("Option -> Result convert", []()
{
Option<string> opt;
Result<string, Error> ret = opt.ok_or(ERR("Option is null"));
assert(ret.is_err(), "Object should containe error object");
string err = ret.err().unwrap().what();
assert( err.find("Option is null") != string::npos, "Error message mistmatch");
assert( err.find("Option is null") != string::npos, "Error message mismatch");
opt = "abc";
opt = string("abc");
ret = opt.ok_or(ERR("Option is null"));
assert(ret.is_ok(), "Object should contain data");
assert(ret.unwrap() == "abc", "Data mistmatch");
});
assert(ret.unwrap() == "abc", "Data mismatch"); });
test("OPtion get_or_insert", [](){
test("OPtion get_or_insert", []()
{
auto x = None(int);
int y = x.get_or_insert(5);
int & y = x.get_or_insert(5);
assert(y == 5, "Unexpected value");
assert(x == 5, "Unexpected value");
});
assert(x == 5, "Unexpected value"); });
test("Option map", [](){
test("Option map", []()
{
auto fn = [](int x){
return "Hello";
};
@ -91,11 +111,10 @@ int main(int argc, char const *argv[])
opt = 2;
opt1 = opt.map<string>(fn);
assert(opt1.unwrap() == "Hello", "Value mismatch");
});
assert(opt1.unwrap() == "Hello", "Value mismatch"); });
test("Option map_or", [](){
test("Option map_or", []()
{
auto fn = [](int x){
return ++x;
};
@ -106,20 +125,20 @@ int main(int argc, char const *argv[])
opt = 2;
v = opt.map_or<int>(0, fn);
assert(v == 3, "Value mismatch %d",v);
});
assert(v == 3, "Value mismatch %d",v); });
test("Option filter", [](){
test("Option filter", []()
{
auto fn = [](int n) {
return n % 2 == 0;
};
//Option<int> opt = None(int).filter(fn);
assert(None(int).filter(fn).is_none(), "Filter value shall be None");
assert(Some(3).filter(fn).is_none(), "Filter value shall be None");
assert(Some(4).filter(fn).is_some(), "Filter value shall not be None");
});
assert(Some(4).filter(fn).is_some(), "Filter value shall not be None"); });
test("Option OR", [](){
test("Option OR", []()
{
auto x = Some(2);
auto y = None(int);
assert((x | y) == Some(2), "Unexpected value");
@ -134,10 +153,10 @@ int main(int argc, char const *argv[])
x = None(int);
y = None(int);
assert((x | y) == None(int), "Unexpected value");
});
assert((x | y) == None(int), "Unexpected value"); });
test("Option AND", [](){
test("Option AND", []()
{
auto x = Some(2);
auto y = None(int);
assert((x & y) == None(int), "Unexpected value");
@ -152,10 +171,10 @@ int main(int argc, char const *argv[])
x = None(int);
y = None(int);
assert((x & y) == None(int), "Unexpected value");
});
assert((x & y) == None(int), "Unexpected value"); });
test("Option XOR", [](){
test("Option XOR", []()
{
auto x = Some(2);
auto y = None(int);
assert((x ^ y) == Some(2), "Unexpected value");
@ -170,27 +189,27 @@ int main(int argc, char const *argv[])
x = None(int);
y = None(int);
assert((x ^ y) == None(int), "Unexpected value");
});
assert((x ^ y) == None(int), "Unexpected value"); });
test("Option or_else", [](){
test("Option or_else", []()
{
auto nobody = [](){return None(int);};
auto vikings = [](){return Some(100);};
assert(None(int).or_else(vikings) == Some(100), "Unexpected value");
assert(None(int).or_else(nobody) == None(int), "Unexpected value");
assert(Some(2).or_else(vikings) == Some(2), "Unexpected value");
});
assert(Some(2).or_else(vikings) == Some(2), "Unexpected value"); });
test("Option and_then", [](){
test("Option and_then", []()
{
auto fn = [](int x){return string("Hello");};
assert(Some(1).and_then<string>(fn) == Some(string("Hello")), "Unexpected value");
assert(None(int).and_then<string>(fn) == None(string), "Unexpected value");
});
assert(None(int).and_then<string>(fn) == None(string), "Unexpected value"); });
test("Option take", [](){
test("Option take", []()
{
auto x = Some(2);
auto y = x.take();
@ -200,10 +219,10 @@ int main(int argc, char const *argv[])
auto z = None(int);
y = z.take();
assert(x == None(int), "Unexpected value");
assert(y == None(int), "Unexpected value");
});
assert(y == None(int), "Unexpected value"); });
test("Option take_if", [](){
test("Option take_if", []()
{
auto fn = [](int n) {
return n % 2 == 0;
};
@ -216,34 +235,61 @@ int main(int argc, char const *argv[])
auto z = Some(3);
y = z.take_if(fn);
assert(z == Some(3), "Unexpected value");
assert(y == None(int), "Unexpected value");
});
assert(y == None(int), "Unexpected value"); });
test("Option unwrap_or", [](){
assert(Some(2).unwrap_or(4) == 2, "Unexpected value");
assert(None(int).unwrap_or(4) == 4, "Unexpected value");
test("Option unwrap_or", []()
{
assert(Some(2).unwrap_or(4) == 2, "Unexpected value");
assert(None(int).unwrap_or(4) == 4, "Unexpected value");
assert(None(Error).unwrap_or(Error("Hello")) == Error("Hello"), "Unexpected value");
assert(None(Error).unwrap_or(Error("Hello")) == Error("Hello"), "Unexpected value");
});
test("Option unwrap_or_else", []()
{
assert(Some(2).unwrap_or_else([]()
{ return 4; }) == 2,
"Unexpected value");
assert(None(int).unwrap_or_else([]()
{ return 4; }) == 4,
"Unexpected value");
});
test("Option zip", []()
{
auto x = Some(2);
auto y = Some(string("Hello"));
auto z = None(int);
assert(x.zip(y).unwrap() == std::tuple(2, string("Hello")), "Unexpected value");
assert(x.zip(z).is_none(), "Unexpected value");
});
test("Option unzip", []()
{
auto x = Some(std::tuple(2, string("Hello")));
auto y = x.unzip<int,string>();
assert(std::get<0>(y) == Some(2) , "Unexpected value");
assert(std::get<1>(y) == Some(string("Hello")), "Unexpected value");
x = None(std::tuple<int,string>);
y = x.unzip<int,string>();
assert(std::get<0>(y) == None(int) , "Unexpected value");
assert(std::get<1>(y) == None(string), "Unexpected value"); });
test("Option zip with", []()
{
});
test("Option unwrap_or_else", [](){
assert(Some(2).unwrap_or_else([](){ return 4; }) == 2, "Unexpected value");
assert(None(int).unwrap_or_else([](){ return 4; }) == 4, "Unexpected value");
});
test("Option zip", [](){
auto fn = [](const int& a, const int & b) {return Point(a,b);};
auto x = Some(2);
auto y = Some(string("Hello"));
auto z = None(int);
auto y = Some(8);
auto z = x.zip_with<int,Point>(y, fn);
assert(z == Some(Point(2,8)), "Unexpected value");
z = x.zip_with<int,Point>(None(int), fn);
assert(z.is_none(), "Unexpected value"); });
assert(x.zip(y).unwrap() == std::tuple(2, string("Hello")), "Unexpected value");
assert(x.zip(z).is_none(), "Unexpected value");
});
test("Option Into", [](){
test("Option Into", []()
{
Option<int> x = 2;
int y = 0;
bool v = x.into(y);
@ -254,18 +300,67 @@ int main(int argc, char const *argv[])
v = x.into(y);
y = 0;
assert(v == false, "Return value shall not be true");
assert(y == 0, "Unexpected value");
});
assert(y == 0, "Unexpected value"); });
test("Test Result", []{
test("Test Option match", []
{
Option<int> x = 10;
int y = x.match<int>(
[](int & x) { x += 10; return x;},
[]() {return 0; }
);
assert(x == y, "Data mismatch");
x = None(int);
y = x.match<int>(
[](int & x) { x += 10; return x;},
[]() {return 0;}
);
assert(x == None(int), "Data mismatch");
assert(y == 0, "Data mismatch"); });
test("Option flatten", []
{
Option<int> z = Some(1);
Option<Option<int>> x = Some(Some(6));
assert(x.flatten() == Some(6), "Unexpected value");
Option< Option< Option<int> > > y = Option(Option<Option<int>>(Option(10)));
assert(y.flatten() == Some(Some(10)), "Unexpected value");
assert(y.flatten().flatten() == Some(10), "Unexpected value"); });
test("Option transpose", []
{
Option<Result<int>> x = Some(Result(10));
Result<Option<int>> y = Some(10);
assert(x.transpose() == y, "Unexpected value");
x = Some(Result<int>(Error("error")));
assert(x.transpose() == Error("error"), "Unexpected value"); });
test("Replace", []
{
Option<int> x = 20;
auto y = x.replace(10);
assert(x == 10, "Unexpected value");
assert(y == 20, "Unexpected value");
x = None(int);
y = x.replace(10);
assert(x == 10, "Unexpected value");
assert(y == None(int), "Unexpected value"); });
test("Test Result", []
{
Result<string, Error> ret = ERR("Error");
assert(ret.is_err(), "Object should containe error object");
ret = string("Hello");
assert(ret.is_ok(), "Object should contain data");
assert(ret.unwrap() == "Hello", "Data mistmatch");
});
assert(ret.unwrap() == "Hello", "Data mismatch"); });
test("Result->option convert",[](){
test("Result->option convert", []()
{
Result<string, Error> ret = ERR("Error");
Option<string> opt_v = ret.ok();
assert(opt_v.is_none(), "Value shall not be present");
@ -276,10 +371,10 @@ int main(int argc, char const *argv[])
opt_v = ret.ok();
assert(opt_v.is_some(), "Value shall be present");
opt_err = ret.err();
assert(opt_err.is_none(), "Erreur shall be none");
});
assert(opt_err.is_none(), "Erreur shall be none"); });
test("Result expect", [](){
test("Result expect", []()
{
try{
Result<string, Error> ret = ERR("Error");
auto s = ret.expect(ERR("Expect message"));
@ -289,10 +384,10 @@ int main(int argc, char const *argv[])
{
assert(true, "%s", e.what());
cout << e.what() << endl;
}
});
} });
test("Result map", [](){
test("Result map", []()
{
auto err = ERR("Error");
auto fn = [](string data){
return 1;
@ -300,29 +395,28 @@ int main(int argc, char const *argv[])
Result<string, Error> ret = err;
Result<int, Error> ret1 = ret.map<int>(fn);
assert(ret1.is_err(), "mapped result shall be an error");
assert(ret1.err() == ret.err(), "Error object mistmatch");
assert(ret1.err() == ret.err(), "Error object mismatch");
ret = string("Hello");
ret1 = ret.map<int>(fn);
assert(ret1.is_ok(), "mapped result shall not be an error");
assert(ret1.unwrap() == 1, "Value mistmatch");
});
assert(ret1.unwrap() == 1, "Value mismatch"); });
test("Result map_err", [](){
test("Result map_err", []()
{
auto fn = [](int x){
return "Hello";
};
Result<bool, int> ret = true;
Result<bool, string> ret1 = ret.map_err<string>(fn);
assert(ret1.unwrap() == true, "Value mistmatch");
assert(ret1.unwrap() == true, "Value mismatch");
ret = 10;
ret1 = ret.map_err<string>(fn);
assert(ret1.err().unwrap() == "Hello", "Value mistmatch");
});
assert(ret1.err().unwrap() == "Hello", "Value mismatch"); });
test("Result map_or", [](){
test("Result map_or", []()
{
auto fn = [](int x){
return ++x;
};
@ -333,10 +427,10 @@ int main(int argc, char const *argv[])
ret = 2;
v = ret.map_or<int>(0, fn);
assert(v == 3, "Value mismatch %d",v);
});
assert(v == 3, "Value mismatch %d",v); });
test("Result AND", [](){
test("Result AND", []()
{
Result<int, Error> x = 2;
Result<int, Error> y = Error("Hello");
assert((x&y) == Error("Hello"), "Value mismatch");
@ -351,10 +445,10 @@ int main(int argc, char const *argv[])
x = 1;
y = 10;
assert((x&y) == 10, "Value mismatch");
});
assert((x&y) == 10, "Value mismatch"); });
test("Result OR", [](){
test("Result OR", []()
{
Result<int, Error> x = 2;
Result<int, Error> y = Error("Hello");
assert((x|y) == 2, "Value mismatch");
@ -369,20 +463,20 @@ int main(int argc, char const *argv[])
x = 1;
y = 10;
assert((x|y) == 1, "Value mismatch");
});
assert((x|y) == 1, "Value mismatch"); });
test("Result and_then", [](){
test("Result and_then", []()
{
auto fn = [](int x){return string("Hello");};
Result<int, Error> x = 2;
auto test = Result<string,Error>(string("Hello"));
assert(x.and_then<string>(fn) == string("Hello"), "Unexpected value");
x = Error("Error");
assert(x.and_then<string>(fn) == Error("Error"), "Unexpected value");
});
assert(x.and_then<string>(fn) == Error("Error"), "Unexpected value"); });
test("Result or_else", [](){
test("Result or_else", []()
{
auto sq = [](int x){return x*x;};
auto err = [](int x){return x;};
Result<double, int> x(2.0);
@ -396,33 +490,32 @@ int main(int argc, char const *argv[])
assert(ret == 9, "Unexpected value");
ret = x.or_else<int>(err).or_else<int>(err);
assert(ret == 3, "Unexpected value");
});
assert(ret == 3, "Unexpected value"); });
test("Result unwrap_err", [](){
test("Result unwrap_err", []()
{
Result<int, Error> x = Error("Hello");
assert(x.unwrap_err() == Error("Hello"), "Unexpected value");
});
assert(x.unwrap_err() == Error("Hello"), "Unexpected value"); });
test("Result unwrap_or", [](){
test("Result unwrap_or", []()
{
Result<int, Error> x = 2;
assert(x.unwrap_or(10) == 2, "Unexpected value");
x = ERR("Error");
assert(x.unwrap_or(10) == 10, "Unexpected value");
});
assert(x.unwrap_or(10) == 10, "Unexpected value"); });
test("Result unwrap_or_else", [](){
test("Result unwrap_or_else", []()
{
auto fn = [](const Error& x){return strlen(x.what());};
Result<int, Error> x = 2;
assert(x.unwrap_or_else(fn) == 2, "Unexpected value");
x = Error("HELLO");
assert(x.unwrap_or_else(fn) == 5, "Unexpected value");
});
assert(x.unwrap_or_else(fn) == 5, "Unexpected value"); });
test("Result Into", [](){
test("Result Into", []()
{
Result<int, Error> x = 2;
int y = 0;
bool v = x.into(y);
@ -433,16 +526,59 @@ int main(int argc, char const *argv[])
v = x.into(y);
y = 0;
assert(v == false, "Return value shall not be true");
assert(y == 0, "Unexpected value");
});
assert(y == 0, "Unexpected value"); });
test("try_unwrap", [](){
test("try_unwrap", []()
{
Result<int, Error> x = test_macro(Result<int, Error>(10));
assert(x == 10, "Unexpected value");
x = test_macro(Result<int, Error>(Error("Hello")));
assert(x.is_err(), "Unexpected value");
});
assert(x.is_err(), "Unexpected value"); });
test("Test Result match", []
{
Result<int> x = 10;
int y = x.match<int>(
[](int & x) { x += 10; return x;},
[](Error & e) {return 0; }
);
assert(x == y, "Data mismatch");
x = Error("error");
y = x.match<int>(
[](int & x) { x += 10; return x;},
[](Error & e) {return 0;}
);
assert(x == Error("error"), "Data mismatch");
assert(y == 0, "Data mismatch"); });
test("Result Test uwrap mut", []
{
Result<int> x = 10;
int& y = x.unwrap();
y = 50;
assert(x == 50, "Value mismatch"); });
test("Result flatten", []()
{
auto x = Result<Result<int>>(Result(20));
assert(x.flatten() == Result(20),"Value mismatch");
auto z = Result<int>(Error("error"));
x = Result<Result<int>>(Result(z));
assert(x.flatten() == Error("error"),"Value mismatch"); });
test("Result transpose", []()
{
Result<Option<int>>x = Some(5);
Option<Result<int>>y = Some(Result(5));
assert(x.transpose() == y, "Value mismatch");
x = None(int);
assert(x.transpose().is_none(), "Transposed value shall be none");
x = Error("Error");
y = Some(Result<int>(Error("Error")));
assert(x.transpose() == y, "Value mismatch"); });
cout << "REPORT: " << g_stat.passed << " tests passed / " << g_stat.total << " tests." << endl;
}