diff options
| author | San Jacobs | 2026-07-01 16:15:38 +0200 |
|---|---|---|
| committer | San Jacobs | 2026-07-01 16:15:38 +0200 |
| commit | ac28fa0afbc104e6dab14ba25fdcfe961cd706c6 (patch) | |
| tree | b6d4a9c5fd74e6e1ef5a6d19a67a9e2b55405b11 | |
| parent | fa3c3561129ee6e956d31a0cdf8b5b40c1eca43a (diff) | |
| download | statics-master.tar.gz statics-master.tar.bz2 statics-master.zip | |
| -rw-r--r-- | main.odin | 73 | ||||
| -rw-r--r-- | time.odin | 342 |
2 files changed, 94 insertions, 321 deletions
@@ -2,7 +2,6 @@ package main import "core:fmt" import "core:os" -import "core:os/os2" import "core:strings" import "core:strconv" import "core:sys/windows" @@ -49,7 +48,7 @@ main :: proc() { when ODIN_OS == .Windows { windows.SetConsoleOutputCP(windows.CODEPAGE.UTF8) } - exe_dir, _ = os2.get_executable_directory(context.allocator) + exe_dir, _ = os.get_executable_directory(context.allocator) data_dir = fmt.tprint(exe_dir, "statics_data", sep = SEPARATOR) alias_dir = fmt.tprint(data_dir, "aliases", sep = SEPARATOR) ics_cache_dir = fmt.tprint(data_dir, "ics_cache", sep = SEPARATOR) @@ -312,7 +311,11 @@ save_alias :: proc(name, url : string) -> bool { output_file_path := fmt.tprint(alias_dir, name, sep=SEPARATOR) just_fucking_make_the_directory(alias_dir) - os.write_entire_file(output_file_path, data) + err := os.write_entire_file(output_file_path, data) + + if err != .NONE { + fmt.eprintln(err) + } fmt.printfln("Saved alias [%s -> %s] to: %s", name, url, output_file_path) return true @@ -320,18 +323,18 @@ save_alias :: proc(name, url : string) -> bool { // Returns false if no aliases exist load_aliases :: proc() -> bool { - w := os2.walker_create(alias_dir) - defer os2.walker_destroy(&w) + w := os.walker_create(alias_dir) + defer os.walker_destroy(&w) found_alias := false - for info in os2.walker_walk(&w) { - if path, err := os2.walker_error(&w); err != nil { + for info in os.walker_walk(&w) { + if path, err := os.walker_error(&w); err != nil { fmt.eprintfln("ERROR: Failed to walk %s: %s", path, err) continue } - file_data, err := os2.read_entire_file_from_path(info.fullpath, allocator=context.allocator) + file_data, err := os.read_entire_file_from_path(info.fullpath, allocator=context.allocator) if err != nil { fmt.eprintln("ERROR: Failed to load data from %s: %s", info.fullpath, err) } else { @@ -385,51 +388,51 @@ import "core:path/filepath" // RECURSIVELY CREATES ANY DIRECTORY LIKE GOD INTENDED just_fucking_make_the_directory :: proc(path : string) -> (directory_now_exists : bool) { - path_clean := filepath.clean(path) - - if os.is_dir(path_clean) { - return true - } - - // Get parent directory - parent := filepath.dir(path_clean) - - // If parent doesn't exist, create it recursively - if parent != path_clean && !os.is_dir(parent) { - ok := just_fucking_make_the_directory(parent) - if !ok { - return false - } - } - - err := os.make_directory(path_clean, 0o755) - return err == os.ERROR_NONE + path_clean, _ := filepath.clean(path) + + if os.is_dir(path_clean) { + return true + } + + // Get parent directory + parent := filepath.dir(path_clean) + + // If parent doesn't exist, create it recursively + if parent != path_clean && !os.is_dir(parent) { + ok := just_fucking_make_the_directory(parent) + if !ok { + return false + } + } + + err := os.make_directory(path_clean) + return err == os.ERROR_NONE } exec :: proc(command: ^[]string) -> bool { - desc := os2.Process_Desc{ + desc := os.Process_Desc{ command = command^, // Inherit parent's print pipes - stdout = os2.stdout, - stderr = os2.stderr, - stdin = os2.stdin, + stdout = os.stdout, + stderr = os.stderr, + stdin = os.stdin, } - process, err := os2.process_start(desc) + process, err := os.process_start(desc) if err != nil { fmt.printf("Failed to start process: %v\n", err) return false } defer { - if close_err := os2.process_close(process); close_err != nil { + if close_err := os.process_terminate(process); close_err != nil { fmt.printf("Failed to close process: %v\n", close_err) } } - state, wait_err := os2.process_wait(process) + state, wait_err := os.process_wait(process) if wait_err != nil { fmt.printf("Error waiting for process: %v\n", wait_err) return false @@ -440,7 +443,7 @@ exec :: proc(command: ^[]string) -> bool { // TODO: Don't call out to the system's curl, ship my own download_file :: proc(url: string, output_path: string) -> (ok: bool) { - command : []string = {"curl", "-L", "-s", "-o", output_path, url} + command : []string = {"curl", "-L", "-s", "-o", output_path, url} exec(&command) return os.exists(output_path) }
\ No newline at end of file @@ -77,215 +77,6 @@ Workday :: struct { // --- MAJOR PROCEDURES --- // -new_workday :: proc(previous_wrap : Moment, - calltime : Moment, - wraptime : Moment, - planned_wraptime : Moment) -> (workday: Workday) { - - workday.call = calltime - workday.wrap = wraptime - workday.planned_wrap = planned_wraptime - - using workday - - initial_block: Timeblock = {call, - // Paragraph 6.7 says that up to 2 hours of unused warned overtime counts as worktime, - // though so that at least one hour of the unused overtime is not counted. - // (It's unclear if an 8-hour day that ends 3 hours in counts as having 5 hours of unused overtime) - time_max(time_clamp(sub(planned_wrap, {0, 1, 0}), wrap, add(wrap, {0, 2, 0})), - add(call, {0, 4, 0})), 1, "", ""} - // ^ Minimum 4 hour day ^ - - // BUG: I think this is causing a bug where if the day ends at 6 and gets extended, - // it doesn't split the day at 6 and set the work-time before that to be extra expensive - - sp_length :: 11 - splitpoints:= [sp_length]Moment{ // --$-- Points where the price may change --$-- // - - // TODO: Replace this terribleness with a system for parsing simple, user-editable rulseset-files - - add(previous_wrap, {0, 10, 0}), // Sleepbreach, 10 hours after previous wrap, aka. turnover - {0, 5, call.day, call.month, call.year}, // 2 hours before 7, aka 5 - {0, 6, call.day, call.month, call.year}, // 6 in the morning - add(call, {0, 8, 0}), // Normal 8 hours of work - add(call, {0, 9, 0}), // 1st hour of overtime is over - add(call, {0, 11, 0}), // 3rd hour of overtime is over - planned_wraptime, // End of warned overtime - add(call, {0, 14, 0}), // The 14-hour mark - {0, 22, call.day, call.month, call.year}, // 22:00 in the evening - add({0, 23, call.day, call.month, call.year}, {0, 1, 0}), // Midnight - add({0, 23, call.day, call.month, call.year}, {0, 7, 0}), // 06:00 the next morning - } - - // Eliminate planned wrap, if it occurs within normal 8-hour period. - // This is to make sure the first period of time becomes a pure 8 hours, - // which makes detecting the main section of the workday easier. - if sortable(splitpoints[6]) < sortable(splitpoints[3]) { - splitpoints[6] = splitpoints[3]; - } - - splitpoints_sorted: [sp_length]Moment = splitpoints - slice.sort_by(splitpoints_sorted[:], lessMoment) - - for each_point, i in splitpoints_sorted { - fmt.printf("Splitpoint %2i: %s\n", i+1, toString(each_point)) - } - - working_block: Timeblock = initial_block - fmt.println("working_block: ", toString(working_block)) - - j: int = 0 - for each_point in splitpoints_sorted { - // If each splitpoint moment is within the workday, and is not equal to the start of the current block - if sortable(each_point) > sortable(call) && // vvvvvvvvvvvvvvvvvvvvvvvv this complication is here to cover - sortable(each_point) < sortable(time_max(wrap, add(call, {0, 4, 0}))) && // for the 4-hour minimum day. - each_point != working_block.start { - - blocks[j], working_block = timesplit(working_block, each_point) - j += 1 - //fmt.println("Split and wrote:", j) - } - } - blocks[j] = working_block - j += 1 - total_timeblocks = j - - // This line is commented out because it shouldn't be needed. - //slice.sort_by(blocks[:], lessTimeblock) - - - fmt.println(total_timeblocks) - - for &block, i in blocks { - if i >= total_timeblocks do break - - //using Weekday - - if lessEq(block.end, splitpoints[0]) do upvalue(&block, 3, "Sleep-breach") // +200% for sleep-breach - if block.start.hours >= 22 do upvalue(&block, 2, "Night") // Work at night, aka. between 22:00 and 06:00 - if (block.end.hours == 6 && block.end.minutes == 0) || block.end.hours <= 5 do upvalue(&block, 2, "Night") // is +100% - if greatEq(block.start, splitpoints[3]) { - upvalue(&block, 1.5, "Overtime") - if getweekday(block.start) == .Saturday do upvalue(&block, 2, "Saturday Overtime") - } - if greatEq(block.start, splitpoints[5]) do upvalue(&block, 2, "Overtime") // End of 3-hour cheap planned overtime - if greatEq(block.start, planned_wrap) && greatEq(block.start, splitpoints[4]) do upvalue(&block, 2, "Overtime") // Unwarned OT - if greatEq(block.start, splitpoints[7]) do upvalue(&block, 3, "Far overtime") // +200% beyond 14th hours is +100% - if getweekday(block.start) == .Saturday do upvalue(&block, 1.5, "Saturday") // Saturdays are +50% - if getweekday(block.start) == .Sunday do upvalue(&block, 2, "Sunday") // Sundays are +100% - - if !(less(call, Moment{0, 7, call.day, call.month, call.year}) && - less(time_min(add(call, Delta{0,8,0}), wrap), Moment{0, 17, call.day, call.month, call.year} )) { - // This was added for rule 6.11c, but in a world without a defined normal workday, - // that rule is already covered already by 6.11g, so this is empty. - } - - - // Holidays! - if (block.start.day==1) && (block.start.month==1) { upvalue(&block, 2, "New year"); continue} - if (block.start.day==1) && (block.start.month==5) { upvalue(&block, 2, "1st of May"); continue} - if (block.start.day==17) && (block.start.month==5) { upvalue(&block, 2, "17th of May"); continue} - if (block.start.day==25 || block.start.day==26) && block.start.month==12 { upvalue(&block, 2, "Christmas"); continue} - easter: Moment = gaussEaster(block.start.year) - if (block.start.day == sub(easter, {0,0,3}).day) && block.start.month == sub(easter, {0,0,3}).month { upvalue(&block, 2, "Maundy Thursday"); continue} - if (block.start.day == sub(easter, {0,0,2}).day) && block.start.month == sub(easter, {0,0,2}).month { upvalue(&block, 2, "Good Friday"); continue} - if (block.start.day == easter.day) && (block.start.month == easter.month) { upvalue(&block, 2, "Easter"); continue} - if (block.start.day == add(easter, {0,0,1}).day) && (block.start.month == add(easter, {0,0,1}).month) { upvalue(&block, 2, "Easter"); continue} - if (block.start.day == add(easter, {0,0,39}).day) && (block.start.month == add(easter, {0,0,39}).month) { upvalue(&block, 2, "Feast of the Ascension"); continue} - if (block.start.day == add(easter, {0,0,49}).day) && (block.start.month == add(easter, {0,0,49}).month) { upvalue(&block, 2, "Pentecost"); continue} - if (block.start.day == add(easter, {0,0,50}).day) && (block.start.month == add(easter, {0,0,50}).month) { upvalue(&block, 2, "Pentecost Monday"); continue} - } - - for each_block, i in blocks { - fmt.printf("Block %2i: %s $f: %i%% %s\n", i+1, toString(each_block), int((each_block.value-1)*100), each_block.reason) - price += f32(f64(dayrate/7.5) * f64(hourcount(each_block)) * f64(each_block.value)) - } - - return -} - -lunch :: proc(workday: ^Workday, lunch_start: Moment, lunch_end: Moment) { - - // - // This basically cuts out part of the workday - // - // |-------|---|-------|----|---------|--------------| - // |--lunch--| - // |-------|---|----| |-------|--------------| - // This ^ works now! - - if lunch_start == lunch_end do return - assert(less(lunch_start, lunch_end), "ERROR: Bad Lunch! Lunch ends before it starts") - - start_index: int - end_index: int - for block, i in workday.blocks { - if (great(lunch_start, block.start) && less(lunch_start, block.end)) || (block.start == lunch_start) { - start_index = i - } - if (great(lunch_end, block.start) && less(lunch_end, block.end)) || (block.end == lunch_end) { - end_index = i - } - } - - assert(start_index <= end_index, "ERROR: Bad Lunch! start_index greater than end_index") - - span: int = end_index - start_index - - // TODO: This is bad and can definitely be simplified and done in a more principled way - // But right now it works perfectly, and is much better than it used to be in the C++ version - switch span { - case 0: - fmt.println("Start and end are in the same block") - switch { - case (lunch_start == workday.blocks[start_index].start) && (lunch_end == workday.blocks[end_index].end): - popBlock(workday, start_index) - case lunch_start == workday.blocks[start_index].start: - workday.blocks[start_index].start = lunch_end - case lunch_end == workday.blocks[end_index].end: - workday.blocks[end_index].end = lunch_start - case: - growBlocks(workday, start_index) - end_index += 1 - workday.blocks[start_index].end = lunch_start - workday.blocks[end_index].start = lunch_end - } - - case 1: - fmt.println("Start and end span one gap") - switch { - case (lunch_start == workday.blocks[start_index].start) && (lunch_end == workday.blocks[end_index].end): - popBlock(workday, start_index, 2) - case lunch_start == workday.blocks[start_index].start: - workday.blocks[end_index].start = lunch_end - popBlock(workday, start_index) - case lunch_end == workday.blocks[end_index].end: - workday.blocks[start_index].end = lunch_start - popBlock(workday, end_index) - case: - workday.blocks[end_index].start = lunch_end - workday.blocks[start_index].end = lunch_start - } - - case 2..=len(workday.blocks): - fmt.println("Start and end span more than one gap") - switch { - case (lunch_start == workday.blocks[start_index].start) && (lunch_end == workday.blocks[end_index].end): - popBlock(workday, start_index, span+1) - case lunch_start == workday.blocks[start_index].start: - workday.blocks[end_index].start = lunch_end - popBlock(workday, start_index, span) - case lunch_end == workday.blocks[end_index].end: - workday.blocks[start_index].end = lunch_start - popBlock(workday, start_index+1, span) - case: - workday.blocks[end_index].start = lunch_end - workday.blocks[start_index].end = lunch_start - popBlock(workday, start_index+1, span-1) - } - } -} - windIndividual :: proc(input_moment: ^Moment, minutes: int, hours: int, @@ -340,8 +131,10 @@ windIndividual :: proc(input_moment: ^Moment, return } windByDelta :: proc(moment: ^Moment, delta: Delta) { - using delta - wind(moment, minutes, hours, days) + wind(moment, + delta.minutes, + delta.hours, + delta.days) return } wind :: proc{windIndividual, windByDelta} @@ -378,13 +171,13 @@ importICS :: proc(path: string, verbose: bool = false) -> ([dynamic]Timeblock, b c : Timeblock - raw, ok := os.read_entire_file_from_filename(path) + raw, err := os.read_entire_file_from_path(path, allocator=context.temp_allocator) content := string(raw) i := 1 line_nr := 1 - if !ok { + if err != nil { // TODO: Actually check the content to see if it is an ICS file. fmt.eprintf("ERROR: No file found at: \"%v\"", path) return output, false @@ -496,8 +289,10 @@ add :: proc(moment: Moment, delta: Delta) -> (output: Moment) { } sub :: proc(moment: Moment, delta: Delta) -> (output: Moment) { output = moment - using delta - wind(&output, minutes*-1, hours*-1, days*-1) + wind(&output, + delta.minutes*-1, + delta.hours*-1, + delta.days*-1) return } maxMoment :: proc(moment_a: Moment, moment_b: Moment) -> Moment { @@ -626,33 +421,38 @@ diff :: proc(moment_a: Moment, moment_b: Moment) -> (acc: Delta) { } sortableTimeDelta :: proc(delta: Delta) -> (output: u64) { - using delta - output, _ = strconv.parse_u64(fmt.tprintf("1%3i%2i%2i", days, hours, minutes)) + output, _ = strconv.parse_u64(fmt.tprintf("1%3i%2i%2i", + delta.days, + delta.hours, + delta.minutes)) return } sortableTimeMoment :: proc(moment: Moment) -> (output: u64) { - using moment - output, _ = strconv.parse_u64(fmt.tprintf("%4i%2i%2i%2i%2i", year, month, day, hours, minutes)) + output, _ = strconv.parse_u64(fmt.tprintf("%4i%2i%2i%2i%2i", + moment.year, + moment.month, + moment.day, + moment.hours, + moment.minutes)) return } sortable :: proc{sortableTimeMoment, sortableTimeDelta} deltaToString :: proc(delta: Delta) -> (output: string) { - using delta - if hours == 0 && - days == 0 && - minutes == 0 { + if delta.hours == 0 && + delta.days == 0 && + delta.minutes == 0 { return "None" - } + } cat_array : [dynamic]string printed_prev : bool = false - if days>0 { + if delta.days>0 { buf: [5]byte - append(&cat_array, fmt.tprint(days)) - if days < 2 { + append(&cat_array, fmt.tprint(delta.days)) + if delta.days < 2 { append(&cat_array, " day") } else { append(&cat_array, " days") @@ -660,13 +460,13 @@ deltaToString :: proc(delta: Delta) -> (output: string) { printed_prev = true } - if hours>0 { + if delta.hours>0 { if printed_prev do append(&cat_array, ", ") buf: [5]byte - append(&cat_array, fmt.tprint(hours)) - if hours < 2 { + append(&cat_array, fmt.tprint(delta.hours)) + if delta.hours < 2 { append(&cat_array, " hour") } else { append(&cat_array, " hours") @@ -674,13 +474,13 @@ deltaToString :: proc(delta: Delta) -> (output: string) { printed_prev = true } - if minutes>0 { + if delta.minutes>0 { if printed_prev do append(&cat_array, ", ") buf: [5]byte - append(&cat_array, fmt.tprint(minutes)) - if minutes < 2 { + append(&cat_array, fmt.tprint(delta.minutes)) + if delta.minutes < 2 { append(&cat_array, " minute") } else { append(&cat_array, " minutes") @@ -693,18 +493,21 @@ deltaToString :: proc(delta: Delta) -> (output: string) { momentToString :: proc(moment: Moment) -> (output: string) { - using moment - cat_array: [dynamic]string - output = fmt.tprintf("%4i-%2i-%2i %2i:%2i", year, month, day, hours, minutes) + output = fmt.tprintf("%4i-%2i-%2i %2i:%2i", + moment.year, + moment.month, + moment.day, + moment.hours, + moment.minutes) return } timeblockToString :: proc(block: Timeblock) -> (output: string) { - using block s: [5]string = {} - output = fmt.aprintf("%s -> %02d:%02d | %0.3f hrs | %s", toString(start), end.hours, end.minutes, hourcount(block), title) + output = fmt.aprintf("%s -> %02d:%02d | %0.3f hrs | %s", + toString(block.start), block.end.hours, block.end.minutes, hourcount(block), block.title) return } toString :: proc{deltaToString, momentToString, timeblockToString} @@ -712,50 +515,22 @@ toString :: proc{deltaToString, momentToString, timeblockToString} clockprintMoment :: proc(moment: Moment) -> string { - using moment - return fmt.tprintf("%2i:%2i", hours, minutes) + return fmt.tprintf("%2i:%2i", moment.hours, moment.minutes) } clockprintTimeblock :: proc(block: Timeblock) -> string { - using block - return fmt.tprintf("%s -> %s", clockprint(start), clockprint(end)) + return fmt.tprintf("%s -> %s", clockprint(block.start), clockprint(block.end)) } clockprint :: proc{clockprintTimeblock, clockprintMoment} dayprintMoment :: proc(moment: Moment) -> string { - using moment - return fmt.tprintf("%4i-%2i-%2i", year, month, day) + return fmt.tprintf("%4i-%2i-%2i", moment.year, moment.month, moment.day) } dayprintTimeblock :: proc(block: Timeblock) -> string { - using block - return fmt.tprintf("%s -> %s", dayprint(start), dayprint(end)) + return fmt.tprintf("%s -> %s", dayprint(block.start), dayprint(block.end)) } dayprint :: proc{dayprintTimeblock, dayprintMoment} -popBlock :: proc(workday: ^Workday, index: int, count: int = 1) { - using workday - when ODIN_DEBUG do fmt.printf("popBlock() running to remove %i block(s) from index %i\n", count, index) - for i in index..<len(blocks)-count { - when ODIN_DEBUG do fmt.printf("Putting the contents of %i/%i into %i\n", i+count, len(blocks)-1, i) - blocks[i] = blocks[i+count] - } - for i in len(blocks)-count-1..<len(blocks) { - blocks[i] = {{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, 0, "", ""} - } - total_timeblocks -= count -} -growBlocks :: proc(workday: ^Workday, index: int, count: int = 1) { - using workday - fmt.printf("growBlocks() running to make space for %i block(s) at index %i\n", count, index) - for i: int = len(blocks)-1-count; i>=index; i-=1 { - fmt.printf("Putting the contents of %i/%i into %i\n", i+count, len(blocks)-1, i) - blocks[i+count] = blocks[i] - } - //for i in index..<index+count { - // blocks[i] = {{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, 0, ""} - //} - total_timeblocks += count -} getweekday :: proc(moment: Moment) -> Weekday { y: int = moment.year @@ -765,28 +540,23 @@ getweekday :: proc(moment: Moment) -> Weekday { } hourcount :: proc(block: Timeblock) -> f32 { - using block - delta: Delta = diff(end, start) - using delta - return f32(f32(minutes)/60 + - f32(hours) + - f32(days) * 24) + delta: Delta = diff(block.end, block.start) + return f32(f32(delta.minutes)/60 + + f32(delta.hours) + + f32(delta.days) * 24) } minutecount :: proc(block: Timeblock) -> int { - using block - delta: Delta = diff(end, start) - using delta - return (minutes) + - (hours*60) + - (days*24*60) + delta: Delta = diff(block.end, block.start) + return (delta.minutes) + + (delta.hours*60) + + (delta.days*24*60) } daycount :: proc(delta: Delta) -> f32 { - using delta assert(delta != {0,0,0}) - return f32(f32(minutes)/60/24 + - f32(hours)/24 + - f32(days) ) + return f32(f32(delta.minutes)/60/24 + + f32(delta.hours)/24 + + f32(delta.days) ) } days_in :: proc(month: int, year: int) -> int { |