summaryrefslogtreecommitdiff
path: root/radapi.c3
diff options
context:
space:
mode:
authorandromeda <andromeda@lenovo>2026-05-10 22:03:14 +0200
committerandromeda <andromeda@lenovo>2026-05-10 22:03:14 +0200
commit2253eb52841f30c51c88c51f65a2ecc77518f14e (patch)
tree8ef0225183c647097d41ffd30bd76ab7bcbc1838 /radapi.c3
parent735f0ff00859d9dda5e93be401c609653e1467a8 (diff)
partial api compatHEADmaster
Diffstat (limited to 'radapi.c3')
-rw-r--r--radapi.c3821
1 files changed, 782 insertions, 39 deletions
diff --git a/radapi.c3 b/radapi.c3
index f8c35eb..ff1c41b 100644
--- a/radapi.c3
+++ b/radapi.c3
@@ -20,36 +20,326 @@
module radapi;
import std::io;
import std::core::string;
+import std::collections::pair;
+import std::collections::object;
+import std::collections::maybe;
+import std::encoding::json;
import curl;
-enum Request : int (String path, int args)
+enum Request : int (String path)
{
- DELEGATES_REPOS { "/delegates/$1/repos", 1 },
- NODE { "/node", 0 },
- NODE_POLICIES_REPOS { "/node/policies/repos", 0 },
- NODE_POLICIES_REPO { "/node/policies/repos/$1", 1 },
- NODES { "/nodes/$1", 1 },
- NODES_INVENTORY { "/nodes/$1/inventory", 1 },
- REPO_ROOT { "/repos", 0 },
- REPO_SEARCH { "/repos/search", 0 },
- REPO { "/repos/$1", 1 },
- HISTORY { "/repos/$1/commits", 1 },
- COMMIT { "/repos/$1/commits/$2", 2 },
- DIFF { "/repos/$1/diff/$2/$3", 3 },
- ACTIVITY { "/repos/$1/activity", 1 },
- TREE_ROOT { "/repos/$1/tree/$2/", 2 },
- TREE { "/repos/$1/tree/$2/$3", 3 },
- STATS_TREE { "/repos/$1/stats/tree/$2", 2 },
- REMOTES { "/repos/$1/remotes", 1 },
- REMOTE { "/repos/$1/remotes/$2", 2 },
- BLOB { "/repos/$1/blob/$2/$3", 3 },
- README { "/repos/$1/readme/$2", 2 },
- JOB { "/repos/$1/jobs/$2", 2 },
- ISSUES { "/repos/$1/issues", 1 },
- ISSUE { "/repos/$1/issues/$2", 2 },
- PATCHES { "/repos/$1/patches", 1 },
- PATCH { "/repos/$1/patches/$2", 2 },
- STATS { "/stats", 0 },
+ DELEGATES_REPOS { "/delegates/{did}/repos" },
+ NODE { "/node" },
+ NODE_POLICIES_REPOS { "/node/policies/repos" },
+ NODE_POLICIES_REPO { "/node/policies/repos/{rid}" },
+ NODES { "/nodes/{nid}" },
+ NODES_INVENTORY { "/nodes/{nid}/inventory" },
+ REPO_ROOT { "/repos" },
+ REPO_SEARCH { "/repos/search" },
+ REPO { "/repos/{rid}" },
+ HISTORY { "/repos/{rid}/commits" },
+ COMMIT { "/repos/{rid}/commits/{sha}" },
+ DIFF { "/repos/{rid}/diff/{base}/{oid}" },
+ ACTIVITY { "/repos/{rid}/activity" },
+ TREE_ROOT { "/repos/{rid}/tree/{sha}/" },
+ TREE { "/repos/{rid}/tree/{sha}/{path}" },
+ STATS_TREE { "/repos/{rid}/stats/tree/{sha}" },
+ REMOTES { "/repos/{rid}/remotes" },
+ REMOTE { "/repos/{rid}/remotes/{peer}" },
+ BLOB { "/repos/{rid}/blob/{sha}/{path}" },
+ README { "/repos/{rid}/readme/{sha}" },
+ JOB { "/repos/{rid}/jobs/{sha}" },
+ ISSUES { "/repos/{rid}/issues" },
+ ISSUE { "/repos/{rid}/issues/{id}" },
+ PATCHES { "/repos/{rid}/patches" },
+ PATCH { "/repos/{rid}/patches/{id}" },
+ STATS { "/stats" },
+}
+
+alias ResponseRaw = String;
+
+struct SearchRepo
+{
+ Rid rid;
+ struct payloads
+ {
+ struct xyz { struct radicle { struct project
+ {
+ String defaultBranch;
+ String description;
+ String name;
+ }}}
+ }
+ Delegate[] delegates;
+ int seeds;
+}
+
+struct Repo
+{
+ struct payloads
+ {
+ struct xyz { struct radicle { struct project
+ {
+ struct data
+ {
+ String defaultBranch;
+ String description;
+ String name;
+ }
+ struct meta
+ {
+ Sha head;
+ struct issues
+ {
+ int open;
+ int closed;
+ }
+ struct patches
+ {
+ int open;
+ int draft;
+ int archived;
+ int merged;
+ }
+ }
+ }}}
+ }
+ Delegate[] delegates;
+ int threshold;
+ struct visibility
+ {
+ String type;
+ }
+ Rid rid;
+ int seeding;
+ struct refs
+ {
+ Pair{String,Tag}[] tags;
+ Pair{String,Sha}[] refs;
+ }
+}
+
+struct Delegate
+{
+ Did id;
+ Maybe{String} delegate_alias;
+}
+
+struct Tag
+{
+ Sha commit;
+ Maybe{Tagger} tagger;
+ Maybe{String} message;
+}
+
+struct Tagger
+{
+ String name;
+ String email;
+ int timestamp;
+}
+
+struct Node
+{
+ Nid id;
+ String agent;
+ struct config
+ {
+ String node_alias;
+ struct peers
+ {
+ String type;
+ }
+ String[] connect;
+ String[] externalAddresses;
+ String network;
+ String log;
+ String relay;
+ struct limits
+ {
+ int routingMaxSize;
+ int routingMaxAge;
+ int gossipMaxAge;
+ int fetchConcurrency;
+ int maxOpenFiles;
+ struct rate
+ {
+ struct inbound
+ {
+ double fillRate;
+ int capacity;
+ }
+ struct outbound
+ {
+ double fillRate;
+ int capacity;
+ }
+ }
+ struct connection
+ {
+ int inbound;
+ int outbound;
+ }
+ String fetchPackReceive;
+ }
+ int workers;
+ struct seedingPolicy
+ {
+ String default_policy;
+ }
+ }
+ String state;
+ Maybe{String} description;
+}
+
+struct RepoPolicy {
+ String policy;
+ String scope;
+}
+
+struct NodeMeta
+{
+ String node_alias;
+ Did did;
+ struct ssh
+ {
+ String full;
+ String hash;
+ }
+}
+
+struct Commit
+{
+ Sha id;
+ struct author
+ {
+ String name;
+ String email;
+ }
+ String summary;
+ String description;
+ Sha[] parents;
+ struct committer
+ {
+ String name;
+ String email;
+ int time;
+ }
+}
+
+struct CommitFull
+{
+ Commit commit;
+ Diff diff;
+ Pair{String,File}[] files;
+ String[] branches;
+}
+
+struct File
+{
+ Oid id;
+ bool binary;
+ String content;
+}
+
+struct Diff
+{
+ FileDiff[] files;
+ struct stats
+ {
+ int filesChanged;
+ int insertions;
+ int deletions;
+ }
+}
+
+struct FileDiff
+{
+ String status;
+ Maybe{Path} path;
+ Maybe{Path} oldPath;
+ Maybe{Path} newPath;
+ struct diff
+ {
+ String type;
+ Hunk[] hunks;
+ struct stats
+ {
+ int additions;
+ int deletions;
+ }
+ String eof;
+ }
+ Maybe{FileDesc} old;
+ Maybe{FileDesc} new;
+}
+
+struct FileDesc
+{
+ Oid oid;
+ String mode;
+}
+
+struct Hunk
+{
+ String header;
+ Line[] lines;
+ struct old
+ {
+ int start;
+ int end;
+ }
+ struct new
+ {
+ int start;
+ int end;
+ }
+}
+
+struct Line
+{
+ String type;
+ String line;
+ Maybe{int} lineNo;
+ Maybe{int} lineNoOld;
+ Maybe{int} lineNoNew;
+}
+
+struct DiffFull
+{
+ Diff diff;
+ Pair{Oid,File}[] files;
+ Commit[] commits;
+}
+
+union Response
+{
+ Repo[] delegates_repos;
+ Node node;
+ Pair{Rid,RepoPolicy}[] node_policies_repos;
+ RepoPolicy node_policies_repo;
+ NodeMeta nodes;
+ Rid[] nodes_inventory;
+ Repo[] repo_root;
+ SearchRepo[] repo_search;
+ Repo repo;
+ Commit[] history;
+ CommitFull commit;
+ DiffFull diff;
+ ResponseRaw activity;
+ ResponseRaw tree_root;
+ ResponseRaw tree;
+ ResponseRaw stats_tree;
+ ResponseRaw remotes;
+ ResponseRaw remote;
+ ResponseRaw blob;
+ ResponseRaw readme;
+ ResponseRaw job;
+ ResponseRaw issues;
+ ResponseRaw issue;
+ ResponseRaw patches;
+ ResponseRaw patch;
+ ResponseRaw stats;
}
struct Client
@@ -60,6 +350,27 @@ struct Client
usz buffer_index;
}
+alias Did = String;
+alias Nid = String;
+alias Rid = String;
+alias Sha = String;
+alias Base = Sha;
+alias Oid = Sha;
+alias Path = String;
+alias Peer = String;
+
+struct Args
+{
+ Did did;
+ Nid nid;
+ Rid rid;
+ Sha sha;
+ Base base;
+ Oid oid;
+ Path path;
+ Peer peer;
+}
+
fn Client* new_client(String endpoint)
{
Client* self = mem::alloc(Client);
@@ -71,30 +382,462 @@ fn Client* new_client(String endpoint)
return self;
}
-fn CurlResult Client.request(Client* self, Request r, String params, String... args)
+fn Response Client.request(Client* self, Request r, String params, Args args)
{
self.buffer_index = 0;
CurlResult cr;
String s;
@pool()
{
- s = self.endpoint.tconcat(r.path);
- int i;
- while (i < r.args)
- {
- s = s.treplace(string::tformat("$%d", i+1), args[i]);
- i++;
- }
- s = s.tconcat(params);
+ s = self.endpoint
+ .tconcat(r.path)
+ .treplace("{did}", args.did)
+ .treplace("{nid}", args.nid)
+ .treplace("{rid}", args.rid)
+ .treplace("{sha}", args.sha)
+ .treplace("{base}", args.base)
+ .treplace("{oid}", args.oid)
+ .treplace("{path}", args.path)
+ .treplace("{peer}", args.peer)
+ .tconcat(params);
curl::easy_setopt(self.c, CurlOption.URL, s);
cr = curl::easy_perform(self.c);
+ };
+ Object* j = json::tparse(self.raw_response())!!;
+ switch (r)
+ {
+ case Request.DELEGATES_REPOS: return (Response){.delegates_repos = parse_repos(j) };
+ case Request.NODE: return (Response){.node = parse_node(j) };
+ case Request.NODE_POLICIES_REPOS: return (Response){.node_policies_repos = parse_repo_policies(j)};
+ case Request.NODE_POLICIES_REPO: return (Response){.node_policies_repo = parse_repo_policy(j) };
+ case Request.NODES: return (Response){.nodes = parse_node_meta(j) };
+ case Request.NODES_INVENTORY: return (Response){.nodes_inventory = parse_rids(j) };
+ case Request.REPO_ROOT: return (Response){.repo_root = parse_repos(j) };
+ case Request.REPO_SEARCH: return (Response){.repo_search = parse_search_repos(j) };
+ case Request.REPO: return (Response){.repo = parse_repo(j) };
+ case Request.HISTORY: return (Response){.history = parse_commits(j) };
+ case Request.COMMIT: return (Response){.commit = parse_commit_full(j) };
+ case Request.DIFF: return (Response){.diff = parse_diff_full(j) };
+ case Request.ACTIVITY: return (Response){.activity = self.raw_response() };
+ case Request.TREE_ROOT: return (Response){.tree_root = self.raw_response() };
+ case Request.TREE: return (Response){.tree = self.raw_response() };
+ case Request.STATS_TREE: return (Response){.stats_tree = self.raw_response() };
+ case Request.REMOTES: return (Response){.remotes = self.raw_response() };
+ case Request.REMOTE: return (Response){.remote = self.raw_response() };
+ case Request.BLOB: return (Response){.blob = self.raw_response() };
+ case Request.README: return (Response){.readme = self.raw_response() };
+ case Request.JOB: return (Response){.job = self.raw_response() };
+ case Request.ISSUES: return (Response){.issues = self.raw_response() };
+ case Request.ISSUE: return (Response){.issue = self.raw_response() };
+ case Request.PATCHES: return (Response){.patches = self.raw_response() };
+ case Request.PATCH: return (Response){.patch = self.raw_response() };
+ case Request.STATS: return (Response){.stats = self.raw_response() };
+ }
+}
+
+fn Line parse_line(Object* j)
+{
+ Line l;
+ l.type = j.get_string("type")!!;
+ l.line = j.get_string("line")!!;
+ if (j.has_key("lineNo"))
+ {
+ l.lineNo.has_value = true;
+ l.lineNo.value = j.get_int("lineNo")!!;
+ } else {
+ l.lineNo.has_value = false;
+ }
+ if (j.has_key("lineNoOld"))
+ {
+ l.lineNoOld.has_value = true;
+ l.lineNoOld.value = j.get_int("lineNoOld")!!;
+ } else {
+ l.lineNoOld.has_value = false;
+ }
+ if (j.has_key("lineNoNew"))
+ {
+ l.lineNoNew.has_value = true;
+ l.lineNoNew.value = j.get_int("lineNoNew")!!;
+ } else {
+ l.lineNoNew.has_value = false;
+ }
+ return l;
+}
+
+fn Hunk parse_hunk(Object* j)
+{
+ Hunk h;
+ int i;
+ h.header = j.get_string("header")!!;
+ i = 0;
+ while (i < j.get("lines").array.len()!!)
+ {
+ h.lines = array::tconcat(h.lines, (Line[1]){parse_line(j.get("lines").get_at(i)!!)});
+ i++;
+ }
+ h.old.start = j.get("old").get_int("start")!!;
+ h.old.end = j.get("old").get_int("end")!!;
+ h.new.start = j.get("new").get_int("start")!!;
+ h.new.end = j.get("new").get_int("end")!!;
+ return h;
+}
+
+fn FileDiff parse_file_diff(Object* j)
+{
+ FileDiff fd;
+ int i;
+ fd.status = j.get_string("status")!!;
+ if (j.has_key("path"))
+ {
+ fd.path.has_value = true;
+ fd.path.value = j.get_string("path")!!;
+ } else {
+ fd.path.has_value = false;
+ }
+ if (j.has_key("oldPath"))
+ {
+ fd.oldPath.has_value = true;
+ fd.oldPath.value = j.get_string("oldPath")!!;
+ } else {
+ fd.oldPath.has_value = false;
+ }
+ if (j.has_key("newPath"))
+ {
+ fd.newPath.has_value = true;
+ fd.newPath.value = j.get_string("newPath")!!;
+ } else {
+ fd.newPath.has_value = false;
+ }
+ if (j.has_key("diff"))
+ {
+ fd.diff.type = j.get("diff").get_string("type")!!;
+ if (fd.diff.type != "binary")
+ {
+ i = 0;
+ while (i < j.get("diff").get("hunks").array.len()!!)
+ {
+ fd.diff.hunks = array::tconcat(fd.diff.hunks, (Hunk[1]){parse_hunk(j.get("diff").get("hunks").get_at(i)!!)});
+ i++;
+ }
+ fd.diff.stats.additions = j.get("diff").get("stats").get_int("additions")!!;
+ fd.diff.stats.deletions = j.get("diff").get("stats").get_int("deletions")!!;
+ fd.diff.eof = j.get("diff").get_string("eof")!!;
+ }
+ }
+ if (j.has_key("old"))
+ {
+ fd.old.has_value = true;
+ fd.old.value.oid = j.get("old").get_string("oid")!!;
+ fd.old.value.mode = j.get("old").get_string("mode")!!;
+ } else {
+ fd.old.has_value = false;
+ }
+ if (j.has_key("new"))
+ {
+ fd.new.has_value = true;
+ fd.new.value.oid = j.get("new").get_string("oid")!!;
+ fd.new.value.mode = j.get("new").get_string("mode")!!;
+ } else {
+ fd.new.has_value = false;
+ }
+ return fd;
+}
+
+fn Diff parse_diff(Object* j)
+{
+ Diff d;
+ int i = 0;
+ while (i < j.get("files").array.len()!!)
+ {
+ d.files = array::tconcat(d.files, (FileDiff[1]){parse_file_diff(j.get("files").get_at(i)!!)});
+ i++;
+ }
+ d.stats.filesChanged = j.get("stats").get_int("filesChanged")!!;
+ d.stats.insertions = j.get("stats").get_int("insertions")!!;
+ d.stats.deletions = j.get("stats").get_int("deletions")!!;
+ return d;
+}
+
+fn File parse_file(Object* j)
+{
+ File f;
+ f.id = j.get_string("id")!!;
+ f.binary = j.get_bool("binary")!!;
+ f.content = j.get_string("content")!!;
+ return f;
+}
+
+fn CommitFull parse_commit_full(Object* j)
+{
+ CommitFull cf;
+ int i;
+ cf.commit = parse_commit(j.get("commit")!!);
+ cf.diff = parse_diff(j.get("diff")!!);
+ Object.@each_entry(j.get("files")!!; String key, Object* value)
+ {
+ cf.files = array::tconcat(cf.files, (Pair{String,File}[1]){{key, parse_file(value)}});
};
- return cr;
+ i = 0;
+ while (i < j.get("branches").array.len()!!)
+ {
+ cf.branches = array::tconcat(cf.branches, (String[1]){j.get("branches").get_string_at(i)!!});
+ i++;
+ }
+ return cf;
+}
+
+fn DiffFull parse_diff_full(Object* j)
+{
+ DiffFull df;
+ int i;
+ df.diff = parse_diff(j.get("diff")!!);
+ Object.@each_entry(j.get("files")!!; String key, Object* value)
+ {
+ df.files = array::tconcat(df.files, (Pair{Oid,File}[1]){{key, parse_file(value)}});
+ };
+ i = 0;
+ while (i < j.get("commits").array.len()!!)
+ {
+ df.commits = array::tconcat(df.commits, (Commit[1]){parse_commit(j.get("commits").get_at(i)!!)});
+ i++;
+ }
+ return df;
+}
+
+fn Commit[] parse_commits(Object* j)
+{
+ Commit[] cs;
+ int i = 0;
+ while (i < j.array.len())
+ {
+ cs = array::tconcat(cs, (Commit[1]){parse_commit(j.get_at(i))});
+ i++;
+ }
+ return cs;
+}
+
+fn Commit parse_commit(Object* j)
+{
+ Commit c;
+ int i;
+ c.id = j.get_string("id")!!;
+ c.author.name = j.get("author").get_string("name")!!;
+ c.author.email = j.get("author").get_string("email")!!;
+ c.summary = j.get_string("summary")!!;
+ c.description = j.get_string("description")!!;
+ i = 0;
+ while (i < j.get("parents").array.len()!!)
+ {
+ c.parents = array::tconcat(c.parents, (Sha[1]){j.get("parents").get_string_at(i)!!});
+ i++;
+ }
+ c.committer.name = j.get("committer").get_string("name")!!;
+ c.committer.email = j.get("committer").get_string("email")!!;
+ c.committer.time = j.get("committer").get_int("time")!!;
+ return c;
+}
+
+fn Rid[] parse_rids(Object* j)
+{
+ Rid[] rs;
+ int i = 0;
+ while (i < j.array.len())
+ {
+ rs = array::tconcat(rs, (Rid[1]){j.get_string_at(i)!!});
+ i++;
+ }
+ return rs;
+}
+
+fn NodeMeta parse_node_meta(Object* j)
+{
+ NodeMeta nm;
+ nm.node_alias = j.get_string("alias")!!;
+ nm.did = j.get_string("did")!!;
+ nm.ssh.full = j.get("ssh").get_string("full")!!;
+ nm.ssh.hash = j.get("ssh").get_string("hash")!!;
+ return nm;
+}
+
+fn Pair{Rid,RepoPolicy}[] parse_repo_policies(Object* j)
+{
+ Pair{Rid,RepoPolicy}[] rps;
+ int i = 0;
+ while (i < j.array.len())
+ {
+ rps = array::tconcat(rps, (Pair{Rid,RepoPolicy}[1]){{j.get_at(i).get_string("rid")!!, parse_repo_policy(j.get_at(i).get("policy")!!)}});
+ i++;
+ }
+ return rps;
+}
+
+fn RepoPolicy parse_repo_policy(Object* j)
+{
+ RepoPolicy rp;
+ rp.policy = j.get_string("policy")!!;
+ rp.scope = j.get_string("scope")!!;
+ return rp;
+}
+
+fn Node parse_node(Object* j)
+{
+ Node n;
+ int i;
+ n.id = j.get_string("id")!!;
+ n.agent = j.get_string("agent")!!;
+ n.config.node_alias = j.get("config").get_string("alias")!!;
+ n.config.peers.type = j.get("config").get("peers").get_string("type")!!;
+ i = 0;
+ while (i < j.get("config").get("connect").array.len()!!)
+ {
+ n.config.connect = array::tconcat(n.config.connect, (String[1]){j.get("config").get("connect").get_string_at(i)!!});
+ i++;
+ }
+ i = 0;
+ while (i < j.get("config").get("externalAddresses").array.len()!!)
+ {
+ n.config.externalAddresses = array::tconcat(n.config.externalAddresses, (String[1]){j.get("config").get("externalAddresses").get_string_at(i)!!});
+ i++;
+ }
+ n.config.network = j.get("config").get_string("network")!!;
+ n.config.log = j.get("config").get_string("log")!!;
+ n.config.relay = j.get("config").get_string("relay")!!;
+ n.config.limits.routingMaxSize = j.get("config").get("limits").get_int("routingMaxSize")!!;
+ n.config.limits.routingMaxAge = j.get("config").get("limits").get_int("routingMaxAge")!!;
+ n.config.limits.gossipMaxAge = j.get("config").get("limits").get_int("gossipMaxAge")!!;
+ n.config.limits.fetchConcurrency = j.get("config").get("limits").get_int("fetchConcurrency")!!;
+ n.config.limits.maxOpenFiles = j.get("config").get("limits").get_int("maxOpenFiles")!!;
+ n.config.limits.rate.inbound.fillRate = j.get("config").get("limits").get("rate").get("inbound").get_float("fillRate")!!;
+ n.config.limits.rate.inbound.capacity = j.get("config").get("limits").get("rate").get("inbound").get_int("capacity")!!;
+ n.config.limits.rate.outbound.fillRate = j.get("config").get("limits").get("rate").get("outbound").get_float("fillRate")!!;
+ n.config.limits.rate.outbound.capacity = j.get("config").get("limits").get("rate").get("outbound").get_int("capacity")!!;
+ n.config.limits.connection.inbound = j.get("config").get("limits").get("connection").get_int("inbound")!!;
+ n.config.limits.connection.outbound = j.get("config").get("limits").get("connection").get_int("outbound")!!;
+ n.config.workers = j.get("config").get_int("workers")!!;
+ n.config.seedingPolicy.default_policy = j.get("config").get("seedingPolicy").get_string("default")!!;
+ n.state = j.get_string("state")!!;
+ if (j.has_key("description"))
+ {
+ n.description = {j.get_string("description")!!, true};
+ } else {
+ n.description.has_value = false;
+ }
+ return n;
+}
+
+fn Repo[] parse_repos(Object* j)
+{
+ Repo[] rs;
+ int i = 0;
+ while (i < j.array.len())
+ {
+ rs = array::tconcat(rs, (Repo[1]){parse_repo(j.get_at(i))});
+ i++;
+ }
+ return rs;
+}
+
+fn Repo parse_repo(Object* j)
+{
+ Repo r;
+ int i;
+ r.payloads.xyz.radicle.project.data.defaultBranch = j.get("payloads").get("xyz.radicle.project").get("data").get_string("defaultBranch")!!;
+ r.payloads.xyz.radicle.project.data.description = j.get("payloads").get("xyz.radicle.project").get("data").get_string("description")!!;
+ r.payloads.xyz.radicle.project.data.name = j.get("payloads").get("xyz.radicle.project").get("data").get_string("name")!!;
+ r.payloads.xyz.radicle.project.meta.head = j.get("payloads").get("xyz.radicle.project").get("meta").get_string("head")!!;
+ r.payloads.xyz.radicle.project.meta.issues.open = j.get("payloads").get("xyz.radicle.project").get("meta").get("issues").get_int("open")!!;
+ r.payloads.xyz.radicle.project.meta.issues.closed = j.get("payloads").get("xyz.radicle.project").get("meta").get("issues").get_int("closed")!!;
+ r.payloads.xyz.radicle.project.meta.patches.open = j.get("payloads").get("xyz.radicle.project").get("meta").get("patches").get_int("open")!!;
+ r.payloads.xyz.radicle.project.meta.patches.draft = j.get("payloads").get("xyz.radicle.project").get("meta").get("patches").get_int("draft")!!;
+ r.payloads.xyz.radicle.project.meta.patches.archived = j.get("payloads").get("xyz.radicle.project").get("meta").get("patches").get_int("archived")!!;
+ r.payloads.xyz.radicle.project.meta.patches.merged = j.get("payloads").get("xyz.radicle.project").get("meta").get("patches").get_int("merged")!!;
+ i = 0;
+ while (i < j.get("delegates").array.len()!!)
+ {
+ r.delegates = array::tconcat(r.delegates, (Delegate[1]){parse_delegate(j.get("delegates").get_at(i)!!)});
+ i++;
+ }
+ r.threshold = j.get_int("threshold")!!;
+ r.visibility.type = j.get("visibility").get_string("type")!!;
+ r.rid = j.get_string("rid")!!;
+ r.seeding = j.get_int("seeding")!!;
+ i = 0;
+ Object.@each_entry(j.get("refs").get("tags")!!; String key, Object* value)
+ {
+ r.refs.tags = array::tconcat(r.refs.tags, (Pair{String,Tag}[1]){{key, parse_tag(value)}});
+ };
+ Object.@each_entry(j.get("refs").get("refs")!!; String key, Object* value)
+ {
+ r.refs.refs = array::tconcat(r.refs.refs, (Pair{String,Sha}[1]){{key, value.s}});
+ };
+ return r;
+}
+
+fn SearchRepo[] parse_search_repos(Object* j)
+{
+ SearchRepo[] srs;
+ int i = 0;
+ while (i < j.array.len())
+ {
+ srs = array::tconcat(srs, (SearchRepo[1]){parse_search_repo(j.get_at(i))});
+ i++;
+ }
+ return srs;
+}
+
+fn SearchRepo parse_search_repo(Object* j)
+{
+ SearchRepo sr;
+ int i;
+ sr.payloads.xyz.radicle.project.defaultBranch = j.get("payloads").get("xyz.radicle.project").get_string("defaultBranch")!!;
+ sr.payloads.xyz.radicle.project.description = j.get("payloads").get("xyz.radicle.project").get_string("description")!!;
+ sr.payloads.xyz.radicle.project.name = j.get("payloads").get("xyz.radicle.project").get_string("name")!!;
+ i = 0;
+ while (i < j.get("delegates").array.len()!!)
+ {
+ sr.delegates = array::tconcat(sr.delegates, (Delegate[1]){parse_delegate(j.get("delegates").get_at(i)!!)});
+ i++;
+ }
+ sr.rid = j.get_string("rid")!!;
+ sr.seeds = j.get_int("seeds")!!;
+ return sr;
+}
+
+fn Delegate parse_delegate(Object* j)
+{
+ Delegate d;
+ d.id = j.get_string("id")!!;
+ if (j.has_key("alias"))
+ {
+ d.delegate_alias.has_value = true;
+ d.delegate_alias.value = j.get_string("alias")!!;
+ } else {
+ d.delegate_alias.has_value = false;
+ }
+ return d;
+}
+
+fn Tag parse_tag(Object* j)
+{
+ io::printn(j);
+ Tag t;
+ t.commit = j.get_string("commit")!!;
+ if (j.has_key("tagger"))
+ {
+ t.tagger.has_value = true;
+ t.tagger.value.name = j.get("tagger").get_string("name") ?? "";
+ t.tagger.value.email = j.get("tagger").get_string("email") ?? "";
+ t.tagger.value.timestamp = j.get("tagger").get_int("timestamp") ?? 0;
+ } else {
+ t.tagger.has_value = false;
+ }
+ t.message = {j.get_string("message"), true} ?? {"", false};
+ return t;
}
-fn String Client.response(Client* self)
+fn ResponseRaw Client.raw_response(Client* self)
{
- return (String)self.buffer.[..self.buffer_index];
+ return (ResponseRaw)self.buffer.[..self.buffer_index];
}
fn usz cb(char* buffer, usz size, usz nitems, Client* client)