| @@ -143,19 +143,19 @@ int gymTrackerStatus(Arena *arena, list<string> args) { | |||
| GymLogDbParsed *db = parseDb(arena, os_readEntireFile(arena, DB_FILE_LOCATION)); | |||
| Timestamp startTs = {0}; | |||
| int numDays = 1; | |||
| ParsePositiveIntResult numDays = {1, false}; | |||
| bool showAll = args.length == 1 && strEql(args.data[0], "--all"_s); | |||
| if (!showAll) { | |||
| if (args.length == 2 && (strEql(args.data[0], "--days"_s) || strEql(args.data[0], "-d"_s))) { | |||
| size_t l; | |||
| numDays = parsePositiveInt(args.data[1], &l); | |||
| } | |||
| if (numDays == -1) { | |||
| if (!numDays.valid) { | |||
| puts("Bad argument for --days (-d) parameter."); | |||
| statusCode = 1; | |||
| } else { | |||
| uint64 todayUnix = getSystemUnixTime(); | |||
| UnixTimestamp startUnix = todayUnix - numDays * 24 * 60 * 60; | |||
| UnixTimestamp startUnix = todayUnix - numDays.result * 24 * 60 * 60; | |||
| startTs = timestampFromUnixTime(&startUnix); | |||
| } | |||
| } | |||
| @@ -267,14 +267,14 @@ int gymTrackerDeleteEntries(Arena *arena, list<string> args) { | |||
| statusCode = 1; | |||
| } else { | |||
| size_t position = 0; | |||
| int numToDelete = parsePositiveInt(args.data[0], &position); | |||
| if (numToDelete != -1) { | |||
| ParsePositiveIntResult numToDeleteParsed = parsePositiveInt(args.data[0], &position); | |||
| if (numToDeleteParsed.valid) { | |||
| list<GymLogEntry> logEntries = loadEntryLog(arena, LOG_FILE_LOCATION); | |||
| if (numToDelete > logEntries.length) { | |||
| log("%i is more than the current number of log entries (%i). Aborting.", numToDelete, logEntries.length); | |||
| if (numToDeleteParsed.result > logEntries.length) { | |||
| log("%i is more than the current number of log entries (%i). Aborting.", numToDeleteParsed, logEntries.length); | |||
| statusCode = 1; | |||
| } else { | |||
| os_writeEntireFile(arena, LOG_FILE_LOCATION, (byte *)logEntries.data, (logEntries.length - numToDelete) * sizeof(GymLogEntry)); | |||
| os_writeEntireFile(arena, LOG_FILE_LOCATION, (byte *)logEntries.data, (logEntries.length - numToDeleteParsed.result) * sizeof(GymLogEntry)); | |||
| } | |||
| } else { | |||
| log("Invalid number to delete.\n"); | |||
| @@ -321,17 +321,18 @@ int gymTrackerDo(Arena *arena, list<string> args) { | |||
| if (statusCode == 0) { | |||
| exercise.id = existingEntry->id; | |||
| size_t parsedCount = 0; | |||
| real32 kg = parsePositiveReal32(args.data[1], &parsedCount); | |||
| uint8 reps = parsePositiveInt(args.data[2], &parsedCount); | |||
| if (parsedCount == 0 || kg == NAN || reps == 0 || kg == 0) { | |||
| ParsePositiveReal32Result kg = parsePositiveReal32(args.data[1], &parsedCount); | |||
| ParsePositiveIntResult reps = parsePositiveInt(args.data[2], &parsedCount); | |||
| if (!kg.valid || !reps.valid) { | |||
| log("%zu, %f, %\n", parsedCount, kg, reps); | |||
| log("Invalid reps or weight input.\n"); | |||
| statusCode = 1; | |||
| } else { | |||
| GymLogEntry entry = { | |||
| getSystemUnixTime(), | |||
| exercise.id, | |||
| reps, | |||
| kg, | |||
| reps.result, | |||
| kg.result, | |||
| }; | |||
| os_fileAppend(arena, LOG_FILE_LOCATION, (byte *)&entry, sizeof(entry)); | |||
| @@ -274,7 +274,7 @@ list<string> strSplit(Arena *arena, string splitStr, string inputStr) { | |||
| return result; | |||
| } | |||
| int8 parsePositiveInt(string str, size_t *lengthPointer) { | |||
| ParsePositiveIntResult parsePositiveInt(string str, size_t *lengthPointer) { | |||
| size_t numEnd = 0; | |||
| char currChar = str.str[numEnd]; | |||
| while (numEnd < str.length && isNumeric(currChar)) { | |||
| @@ -288,14 +288,14 @@ int8 parsePositiveInt(string str, size_t *lengthPointer) { | |||
| result *= 10; | |||
| result += str.str[i] - '0'; | |||
| } | |||
| return result; | |||
| return {result, true}; | |||
| } else { | |||
| return -1; | |||
| return {0, false}; | |||
| } | |||
| } | |||
| real32 parsePositiveReal32(string str, size_t *lengthPointer) { | |||
| real32 result = NAN; | |||
| ParsePositiveReal32Result parsePositiveReal32(string str, size_t *lengthPointer) { | |||
| ParsePositiveReal32Result result = {NAN, false}; | |||
| string wholePartStr = string{0}; | |||
| string fractionalPartStr = string{0}; | |||
| @@ -314,16 +314,21 @@ real32 parsePositiveReal32(string str, size_t *lengthPointer) { | |||
| c++; | |||
| } | |||
| if (split) { | |||
| int wholePart = parsePositiveInt(wholePartStr, lengthPointer); | |||
| ParsePositiveIntResult wholePartParsed = parsePositiveInt(wholePartStr, lengthPointer); | |||
| *lengthPointer += 1; | |||
| int fractionalPart = parsePositiveInt(fractionalPartStr, lengthPointer); | |||
| if (wholePart >= 0 && fractionalPart >= 0) { | |||
| ParsePositiveIntResult fractionalPartParsed = parsePositiveInt(fractionalPartStr, lengthPointer); | |||
| if (wholePartParsed.valid && fractionalPartParsed.valid) { | |||
| // TODO(dledda): implement powf with intrinsics? or just custom | |||
| real32 fractionalPartMultiplier = 1.0f / powf(10.0f, (real32)fractionalPartStr.length); | |||
| result = (real32)wholePart + (real32)fractionalPart * (real32)fractionalPartMultiplier; | |||
| result.result = (real32)wholePartParsed.result + (real32)fractionalPartParsed.result * (real32)fractionalPartMultiplier; | |||
| result.valid = true; | |||
| } | |||
| } else if (c > 0) { | |||
| result = (real32)parsePositiveInt(str, lengthPointer); | |||
| ParsePositiveIntResult intPartParsed = parsePositiveInt(str, lengthPointer); | |||
| if (intPartParsed.valid) { | |||
| result.result = (real32)intPartParsed.result; | |||
| result.valid = true; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| @@ -176,8 +176,10 @@ list<string> strSplit(Arena *arena, string splitStr, string inputStr); | |||
| string strPrintfv(Arena *arena, const char *fmt, va_list args); | |||
| string strPrintf(Arena *arena, const char *fmt, ...); | |||
| int8 parsePositiveInt(string str, size_t *lengthPointer); | |||
| real32 parsePositiveReal32(Arena *arena, string str, size_t *lengthPointer); | |||
| struct ParsePositiveIntResult { uint8 result; bool valid; }; | |||
| ParsePositiveIntResult parsePositiveInt(string str, size_t *lengthPointer); | |||
| struct ParsePositiveReal32Result { real32 result; bool valid; }; | |||
| ParsePositiveReal32Result parsePositiveReal32(Arena *arena, string str, size_t *lengthPointer); | |||
| inline function bool isNumeric(char c); | |||