Done with the ascii module.

This commit is contained in:
Kapendev 2024-08-04 19:55:08 +03:00
parent b37b6c3eb0
commit 03b915dca0
6 changed files with 228 additions and 199 deletions

View file

@ -4,7 +4,7 @@
Cleaning things in core.
* [ ] ascii
* [x] ascii
* [x] colors
* [x] containers
* [x] errors
@ -16,9 +16,10 @@ Cleaning things in core.
* [ ] types
* [ ] unions
Was working on ascii.
Was rewriting the tests.
Was going to write the toValue tests and then I am done!
Done with ascii.
Now I am thinking of making the io module better.
For example, readin does not tell you right now if something went wrong.
It just returns an empty list.
## IDEAS

View file

@ -462,208 +462,121 @@ IStr toStr(T)(T value, ToStrOptions options = ToStrOptions()) {
} else static if (__traits(hasMember, T, "toStr")) {
return value.toStr();
} else {
static assert(0, "The `toStr` function does not handle the `" ~ T.stringof ~ "` type. Implement a `toStr` function for that type.");
static assert(0, "Function `toStr` does not handle the `" ~ T.stringof ~ "` type. Implement a `toStr` function for that type.");
}
}
BasicResult!bool toBool(IStr str) {
auto result = BasicResult!bool();
if (str == "true") {
result.value = true;
} else if (str == "false") {
result.value = false;
if (str == "false") {
return BasicResult!bool(false);
} else if (str == "true") {
return BasicResult!bool(true);
} else {
result.error = BasicError.invalid;
}
return result;
}
ulong toBoolWithNone(IStr str) {
auto conv = toBool(str);
if (conv.error) {
return false;
} else {
return conv.value;
return BasicResult!bool(BasicError.invalid);
}
}
BasicResult!ulong toUnsigned(IStr str) {
auto result = BasicResult!ulong();
if (str.length == 0) {
result.error = BasicError.invalid;
} else if (str.length >= 18) {
result.error = BasicError.overflow;
if (str.length == 0 || str.length >= 18) {
return BasicResult!ulong(BasicError.invalid);
} else {
ulong level = 1;
foreach_reverse (i, c; str) {
if (!isDigit(c)) {
result.error = BasicError.invalid;
break;
}
auto digit = c - '0';
result.value += digit * level;
level *= 10;
if (str.length == 1 && str[0] == '+') {
return BasicResult!ulong(BasicError.invalid);
}
ulong value = 0;
ulong level = 1;
foreach_reverse (i, c; str[(str[0] == '+' ? 1 : 0) .. $]) {
if (isDigit(c)) {
value += (c - '0') * level;
level *= 10;
} else {
return BasicResult!ulong(BasicError.invalid);
}
}
return BasicResult!ulong(value);
}
return result;
}
BasicResult!ulong toUnsigned(char c) {
auto result = BasicResult!ulong();
if (isDigit(c)) {
result.value = c - '0';
return BasicResult!ulong(c - '0');
} else {
result.error = BasicError.invalid;
}
return result;
}
ulong toUnsignedWithNone(IStr str) {
auto conv = toUnsigned(str);
if (conv.error) {
return 0;
} else {
return conv.value;
}
}
ulong toUnsignedWithNone(char c) {
auto conv = toUnsigned(c);
if (conv.error) {
return 0;
} else {
return conv.value;
return BasicResult!ulong(BasicError.invalid);
}
}
BasicResult!long toSigned(IStr str) {
auto result = BasicResult!long();
if (str.length == 0) {
result.error = BasicError.invalid;
} else if (str.length >= 18) {
result.error = BasicError.overflow;
if (str.length == 0 || str.length >= 18) {
return BasicResult!long(BasicError.invalid);
} else {
if (str[0] == '-') {
auto conv = toUnsigned(str[1 .. $]);
if (conv.error) {
result.error = conv.error;
} else {
result.value = -conv.value;
}
} else {
auto conv = toUnsigned(str);
if (conv.error) {
result.error = conv.error;
} else {
result.value = conv.value;
}
auto temp = toUnsigned(str[(str[0] == '-' ? 1 : 0) .. $]);
if (temp.isNone) {
return BasicResult!long(temp.error);
}
return BasicResult!long(str[0] == '-' ? -temp.value : temp.value);
}
return result;
}
BasicResult!long toSigned(char c) {
auto result = BasicResult!long();
auto conv = toUnsigned(c);
if (conv.error) {
result.error = conv.error;
if (isDigit(c)) {
return BasicResult!long(c - '0');
} else {
result.value = cast(long) conv.value;
}
return result;
}
long toSignedWithNone(IStr str) {
auto conv = toSigned(str);
if (conv.error) {
return 0;
} else {
return conv.value;
}
}
long toSignedWithNone(char c) {
auto conv = toSigned(c);
if (conv.error) {
return 0;
} else {
return conv.value;
return BasicResult!long(BasicError.invalid);
}
}
BasicResult!double toDouble(IStr str) {
auto result = BasicResult!double();
result.value = 0.0;
auto hasDot = false;
foreach (i, c; str) {
if (c == '.') {
hasDot = true;
auto lhs = toSigned(str[0 .. i]);
if (lhs.error) {
result.error = lhs.error;
} else {
auto rhs = toSigned(str[i + 1 .. $]);
if (rhs.error) {
result.error = rhs.error;
} else {
auto rhsLevel = 10;
foreach (_; 1 .. str[i + 1 .. $].length) {
rhsLevel *= 10;
}
result.value = lhs.value + ((lhs.value < 0 ? -1 : 1) * rhs.value / cast(double) rhsLevel);
}
auto dotIndex = findStart(str, '.');
if (dotIndex == -1) {
auto temp = toSigned(str);
return temp.isNone ? BasicResult!double(temp.error) : BasicResult!double(temp.value);
} else {
auto left = toSigned(str[0 .. dotIndex]);
auto right = toSigned(str[dotIndex + 1 .. $]);
if (left.isNone || right.isNone) {
return BasicResult!double(BasicError.invalid);
} else if (str[dotIndex + 1] == '-' || str[dotIndex + 1] == '+') {
return BasicResult!double(BasicError.invalid);
} else {
auto sign = str[0] == '-' ? -1 : 1;
auto level = 10;
foreach (i; 1 .. str[dotIndex + 1 .. $].length) {
level *= 10;
}
break;
return BasicResult!double(left.value + sign * (right.value / (cast(double) level)));
}
}
if (!hasDot) {
auto conv = toSigned(str);
result.value = conv.value;
result.error = conv.error;
}
return result;
}
double toDoubleWithNone(IStr str) {
auto conv = toDouble(str);
if (conv.error) {
return 0.0;
BasicResult!double toDouble(char c) {
if (isDigit(c)) {
return BasicResult!double(c - '0');
} else {
return conv.value;
return BasicResult!double(BasicError.invalid);
}
}
BasicResult!T toEnum(T)(IStr str) {
auto result = BasicResult!T();
switch (str) {
static foreach (member; __traits(allMembers, T)) {
mixin("case " ~ member.stringof ~ ": result.value = T." ~ member ~ "; goto switchExit;");
mixin("case " ~ member.stringof ~ ": return BasicResult!T(T." ~ member ~ ");");
}
default: result.error = BasicError.invalid;
}
switchExit:
return result;
}
T toEnumWithNone(T)(IStr str) {
auto conv = toEnum!T(str);
if (conv.error) {
return T.init;
} else {
return conv.value;
default: return BasicResult!T(BasicError.invalid);
}
}
@trusted
ICStr toCStr(IStr str) {
BasicResult!ICStr toCStr(IStr str) {
static char[1024] buffer = void;
auto result = buffer[];
foreach (i, c; str) {
result[i] = c;
if (buffer.length < str.length) {
return BasicResult!ICStr(BasicError.invalid);
} else {
auto value = buffer[];
value.copyChars(str);
value[str.length] = '\0';
return BasicResult!ICStr(value.ptr);
}
result[str.length] = '\0';
return result.ptr;
}
// TODO: Check if the args count is the same with the `{}` count.
@ -738,6 +651,14 @@ void formatl(A...)(ref LStr text, IStr formatStr, A args) {
// Function test.
@trusted
unittest {
enum TestEnum {
one,
two,
}
char[128] buffer = void;
Str str;
assert(isDigit("0123456789?") == false);
assert(isDigit("0123456789") == true);
assert(isUpper("hello") == false);
@ -748,9 +669,6 @@ unittest {
assert(isCStr("hello") == false);
assert(isCStr("hello\0") == true);
char[128] buffer = void;
Str str;
str = buffer[];
str.copy("Hello");
assert(str == "Hello");
@ -833,46 +751,95 @@ unittest {
assert(doubleToStr(69.0, 3) == "69.000");
assert(cStrToStr("Hello\0") == "Hello");
enum TestEnum {
one,
two,
}
assert(enumToStr(TestEnum.one) == "one");
assert(enumToStr(TestEnum.two) == "two");
assert(toBool("F").isSome == false);
assert(toBool("F").unwrapOr() == false);
assert(toBool("T").isSome == false);
assert(toBool("T").unwrapOr() == false);
assert(toBool("false").isSome == true);
assert(toBool("false").unwrapOr() == false);
assert(toBool("true").isSome == true);
assert(toBool("true").unwrapOr() == true);
assert(toUnsigned("1_069").isSome == false);
assert(toUnsigned("1_069").unwrapOr() == 0);
assert(toUnsigned("+1069").isSome == true);
assert(toUnsigned("+1069").unwrapOr() == 1069);
assert(toUnsigned("1069").isSome == true);
assert(toUnsigned("1069").unwrapOr() == 1069);
assert(toUnsigned('+').isSome == false);
assert(toUnsigned('+').unwrapOr() == 0);
assert(toUnsigned('0').isSome == true);
assert(toUnsigned('0').unwrapOr() == 0);
assert(toUnsigned('9').isSome == true);
assert(toUnsigned('9').unwrapOr() == 9);
// TODO: ToValue tests write need!!!
auto text1 = "1.0";
auto conv1 = toDouble(text1);
assert(conv1.value == 1.0);
assert(conv1.error == BasicError.none);
assert(toSigned("1_069").isSome == false);
assert(toSigned("1_069").unwrapOr() == 0);
assert(toSigned("-1069").isSome == true);
assert(toSigned("-1069").unwrapOr() == -1069);
assert(toSigned("+1069").isSome == true);
assert(toSigned("+1069").unwrapOr() == 1069);
assert(toSigned("1069").isSome == true);
assert(toSigned("1069").unwrapOr() == 1069);
assert(toSigned('+').isSome == false);
assert(toSigned('+').unwrapOr() == 0);
assert(toSigned('0').isSome == true);
assert(toSigned('0').unwrapOr() == 0);
assert(toSigned('9').isSome == true);
assert(toSigned('9').unwrapOr() == 9);
auto text2 = "1";
auto conv2 = toDouble(text2);
assert(conv2.value == 1.0);
assert(conv2.error == BasicError.none);
assert(toDouble("1_069").isSome == false);
assert(toDouble("1_069").unwrapOr() == 0);
assert(toDouble(".1069").isSome == false);
assert(toDouble(".1069").unwrapOr() == 0);
assert(toDouble("1069.").isSome == false);
assert(toDouble("1069.").unwrapOr() == 0);
assert(toDouble(".").isSome == false);
assert(toDouble(".").unwrapOr() == 0);
assert(toDouble("-1069.-69").isSome == false);
assert(toDouble("-1069.-69").unwrapOr() == 0);
assert(toDouble("-1069.+69").isSome == false);
assert(toDouble("-1069.+69").unwrapOr() == 0);
assert(toDouble("-1069").isSome == true);
assert(toDouble("-1069").unwrapOr() == -1069);
assert(toDouble("+1069").isSome == true);
assert(toDouble("+1069").unwrapOr() == 1069);
assert(toDouble("1069").isSome == true);
assert(toDouble("1069").unwrapOr() == 1069);
assert(toDouble("1069.0").isSome == true);
assert(toDouble("1069.0").unwrapOr() == 1069);
assert(toDouble("-1069.0095").isSome == true);
assert(toDouble("-1069.0095").unwrapOr() == -1069.0095);
assert(toDouble("+1069.0095").isSome == true);
assert(toDouble("+1069.0095").unwrapOr() == 1069.0095);
assert(toDouble("1069.0095").isSome == true);
assert(toDouble("1069.0095").unwrapOr() == 1069.0095);
assert(toDouble("-0.0095").isSome == true);
assert(toDouble("-0.0095").unwrapOr() == -0.0095);
assert(toDouble("+0.0095").isSome == true);
assert(toDouble("+0.0095").unwrapOr() == 0.0095);
assert(toDouble("0.0095").isSome == true);
assert(toDouble("0.0095").unwrapOr() == 0.0095);
assert(toDouble('+').isSome == false);
assert(toDouble('+').unwrapOr() == 0);
assert(toDouble('0').isSome == true);
assert(toDouble('0').unwrapOr() == 0);
assert(toDouble('9').isSome == true);
assert(toDouble('9').unwrapOr() == 9);
assert(toEnum!TestEnum("?").isSome == false);
assert(toEnum!TestEnum("?").unwrapOr() == TestEnum.one);
assert(toEnum!TestEnum("one").isSome == true);
assert(toEnum!TestEnum("one").unwrapOr() == TestEnum.one);
assert(toEnum!TestEnum("two").isSome == true);
assert(toEnum!TestEnum("two").unwrapOr() == TestEnum.two);
auto text3 = "1?";
auto conv3 = toDouble(text3);
assert(conv3.value == 0.0);
assert(conv3.error == BasicError.invalid);
assert(toCStr("Hello").unwrapOr().length == "Hello".length);
assert(toCStr("Hello").unwrapOr().cStrToStr() == "Hello");
assert(format("") == "");
assert(format("{}") == "{}");
assert(format("{}", "1") == "1");
assert(format("{} {}", "1", "2") == "1 2");
assert(format("{} {} {}", "1", "2", "3") == "1 2 3");
assert(format("{} {} {}", 1, -2, 3.69) == "1 -2 3.69");
assert(format("{}", 420, 320, 220, 120, 20) == "420");
assert(format("", 1, -2, 3.69) == "");
assert(format("({})", format("({}, {})", false, true)) == "((false, true))");
// TODO: Uncoment when the N.00 bug is fixed.
// assert(format("{}", 0.00) == "0.00");
// assert(format("{}", 0.50) == "0.50");
// assert(format("{}", 1.00) == "1.00");
// assert(format("{}", 1.50) == "1.50");
// TODO: Write tests for `format` when it is done.
}

View file

@ -5,6 +5,8 @@
module popka.core.errors;
import popka.core.traits;
@safe @nogc nothrow:
enum BasicError : ubyte {
@ -18,6 +20,61 @@ enum BasicError : ubyte {
}
struct BasicResult(T) {
T value;
BasicError error;
static if (isNumberType!T) {
T value = 0;
} else {
T value;
}
BasicError error = BasicError.some;
@safe @nogc nothrow:
this(T value) {
this.value = value;
error = BasicError.none;
}
this(BasicError error) {
this.error = error;
}
T unwrap() {
if (error) {
assert(0, "");
}
return value;
}
T unwrapOr(T dflt) {
if (error) {
return dflt;
} else {
return value;
}
}
T unwrapOr() {
return value;
}
bool isNone() {
return error != 0;
}
bool isSome() {
return error == 0;
}
}
// BasicResult test.
unittest {
assert(BasicResult!int().isNone == true);
assert(BasicResult!int().isSome == false);
assert(BasicResult!int().unwrapOr() == 0);
assert(BasicResult!int(0).isNone == false);
assert(BasicResult!int(0).isSome == true);
assert(BasicResult!int(0).unwrapOr() == 0);
assert(BasicResult!int(69).isNone == false);
assert(BasicResult!int(69).isSome == true);
assert(BasicResult!int(69).unwrapOr() == 69);
}

View file

@ -39,7 +39,7 @@ void println(A...)(A args) {
@trusted
// TODO: Check the error values and let the user know what went wrong.
void readText(IStr path, ref List!char text) {
auto f = .fopen(toCStr(path), "rb");
auto f = .fopen(toCStr(path).unwrapOr(), "rb");
if (f == null) {
text.clear();
return;
@ -76,7 +76,7 @@ List!char readText(IStr path) {
@trusted
// TODO: Check the error values and let the user know what went wrong.
void writeText(IStr path, IStr text) {
auto f = .fopen(toCStr(path), "w");
auto f = .fopen(toCStr(path).unwrapOr(), "w");
if (f == null) {
return;
}

View file

@ -61,6 +61,10 @@ bool isDoubleType(T)() {
is(T == immutable(double));
}
bool isNumberType(T)() {
return isIntegerType!T || isDoubleType!T;
}
bool isCharType(T)() {
return is(T == char) ||
is(T == const(char)) ||

View file

@ -212,7 +212,7 @@ struct Texture {
void load(const(char)[] path) {
free();
if (path.length != 0) {
data = ray.LoadTexture(path.toAssetsPath.toCStr);
data = ray.LoadTexture(path.toAssetsPath.toCStr().unwrapOr());
}
if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
}
@ -294,7 +294,7 @@ struct Font {
void load(const(char)[] path, uint size, const(dchar)[] runes = []) {
free();
if (path.length != 0) {
data = ray.LoadFontEx(path.toAssetsPath.toCStr, size, cast(int*) runes.ptr, cast(int) runes.length);
data = ray.LoadFontEx(path.toAssetsPath.toCStr().unwrapOr(), size, cast(int*) runes.ptr, cast(int) runes.length);
}
if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
}
@ -343,7 +343,7 @@ struct Sound {
void load(const(char)[] path) {
free();
if (path.length != 0) {
data = ray.LoadSound(path.toAssetsPath.toCStr);
data = ray.LoadSound(path.toAssetsPath.toCStr().unwrapOr());
}
if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
}
@ -396,7 +396,7 @@ struct Music {
void load(const(char)[] path) {
free();
if (path.length != 0) {
data = ray.LoadMusicStream(path.toAssetsPath.toCStr);
data = ray.LoadMusicStream(path.toAssetsPath.toCStr().unwrapOr());
}
if (isEmpty) printfln("Error: The file `{}` does not exist.", path);
}
@ -801,7 +801,7 @@ void openWindow(Vec2 size, const(char)[] title = "Popka", Color color = defaultB
}
ray.SetConfigFlags(ray.FLAG_VSYNC_HINT | ray.FLAG_WINDOW_RESIZABLE);
ray.SetTraceLogLevel(ray.LOG_ERROR);
ray.InitWindow(cast(int) size.x, cast(int) size.y, toCStr(title));
ray.InitWindow(cast(int) size.x, cast(int) size.y, title.toCStr().unwrapOr());
ray.InitAudioDevice();
ray.SetWindowMinSize(cast(int) (size.x * 0.25f), cast(int) (size.y * 0.25f));
ray.SetExitKey(ray.KEY_NULL);