const std = @import("std"); const mem = std.mem; /// Extract ID from path between prefix and suffix /// e.g., extractId("/flow_runs/abc/set_state", "/flow_runs/", "/set_state") -> "abc" pub fn extractId(target: []const u8, prefix: []const u8, suffix: []const u8) ?[]const u8 { if (!mem.startsWith(u8, target, prefix)) return null; if (!mem.endsWith(u8, target, suffix)) return null; const start = prefix.len; const end = target.len - suffix.len; if (start >= end) return null; return target[start..end]; } /// Extract ID from path after prefix (handles both 32 and 36 char UUIDs) /// e.g., extractIdAfter("/flow_runs/abc-def", "/flow_runs/") -> "abc-def" pub fn extractIdAfter(target: []const u8, prefix: []const u8) ?[]const u8 { if (!mem.startsWith(u8, target, prefix)) return null; const rest = target[prefix.len..]; const id_end = mem.indexOf(u8, rest, "?") orelse rest.len; if (id_end >= 36) { return rest[0..36]; } else if (id_end >= 32) { return rest[0..32]; } return null; } /// Generate a random run name (e.g., "happy-panda") pub fn generateRunName(alloc: std.mem.Allocator) []const u8 { const adjectives = [_][]const u8{ "happy", "quick", "brave", "calm", "eager" }; const nouns = [_][]const u8{ "panda", "tiger", "eagle", "dolphin", "falcon" }; var rng_buf: [2]u8 = undefined; std.crypto.random.bytes(&rng_buf); const adj = adjectives[rng_buf[0] % adjectives.len]; const noun = nouns[rng_buf[1] % nouns.len]; return std.fmt.allocPrint(alloc, "{s}-{s}", .{ adj, noun }) catch "unnamed-run"; }