Migrating Orca to Concord 2.0.0

June 8, 2022 ยท View on GitHub

Symbol replacement

Replace orca references with ccord

Example

orca

...
ORCAcode code = ORCA_OK;
printf("%s\n", orca_strerror(code));
orca_global_init();
orca_global_cleanup();

concord

...
CCORDcode code = CCORD_OK;
printf("%s\n", ccord_strerror(code));
ccord_global_init();
ccord_global_cleanup();

Custom types have been shortened and no longer take _t suffixing

_t suffixing is usually "reserved" for the C standard.

Example

orca

u64_snowflake_t channel_id;
u64_unix_ms_t time_ms;
u64_bitmask_t bitmask;

concord

u64snowflake channel_id;
u64unix_ms time_ms;
u64bitmask bitmask;

Remove _params suffixing

All instances of _params suffixing have been removed from the codebase:

Example

orca

...
struct discord_create_message_params params = { .content = "Hi" };
discord_create_message(client, channel_id, &params, NULL);

concord

...
struct discord_create_message params = { .content = "Hi" };
discord_create_message(client, channel_id, &params, NULL);

Wrap callback parameters in structs

Callback parameters have been wrapped in structs to reduce the user complexity of having to match a specific callback-signature with many parameters, and also allow them to take ownership of the struct so its not automatically cleaned-up.

Example

orca

void on_ready(struct discord *client)
{
  printf("Bot %s is ready!", discord_self(client)->username);
}

void done_create_message(struct discord *client, struct discord_async_ret *ret)
{
  char *nick = ret->data;
  printf("Reaction has been sent by %s\n", nick);
}

void fail_create_message(struct discord *client, struct discord_async_err *err)
{
  printf("%s\n", discord_strerror(err->code, client));
}

void on_reaction_add(struct discord *client,
                    u64snowflake user_id,
                    u64snowflake channel_id,
                    u64snowflake message_id,
                    u64snowflake guild_id,
                    const struct discord_guild_member *member,
                    const struct discord_emoji *emoji)
{
  printf("Received emoji %s", emoji->name);
  
  struct discord_create_message_params params = { .content = "Received a reaction!" };
  discord_async_next(client, &(struct discord_async_attr){
                                .done = &done_create_message,
                                .fail = &fail_create_message,
                                .data = strdup(member->nick),
                                .cleanup = free
                             });
  discord_create_message(client, &params, NULL);
}

int main(void)
{
  struct discord *client = discord_init(BOT_TOKEN);
  discord_set_on_ready(client, &on_ready);
  discord_set_on_message_reaction_add(client, &on_reaction_add);
}

concord

void on_ready(struct discord *client, const struct discord_ready *event)
{
  printf("Bot %s is ready!", event->user->username);
}

void done_create_message(struct discord *client,
                         struct discord_response *resp,
                         const struct discord_message *msg)
{
  const struct discord_message_reaction_add *event = resp->keep;
  printf("Reaction has been sent by %s\n", event->member->nick);
}

void fail_create_message(struct discord *client, struct discord_response *resp)
{
  printf("%s\n", discord_strerror(resp->code, client));
}

void on_reaction_add(struct discord *client, const struct discord_message_reaction_add *event)
{
  printf("Received emoji %s", event->emoji->name);
  
  struct discord_create_message params = { .content = "Received a reaction!" };
  discord_create_message(client, &params, &(struct discord_ret_message){
                                            .done = &done_create_message,
                                            .fail = &fail_create_message,
                                            .keep = event // ensures event is kept and sent to callbacks
                                          });
}

int main(void)
{
  struct discord *client = discord_init(BOT_TOKEN);
  discord_set_on_ready(client, &on_ready);
  discord_set_on_message_reaction_add(client, &on_reaction_add);
}

Feature replacement

Null-terminated-lists replaced with carray.h implementation

Null-terminated lists were neither begginner friendly or intuitive, there were two common scenarios that would lead to a segfault:

...
// assuming list is a NTL, if it is NULL, then 'list[i]' will lead to a segfault
for (int i = 0; list[i]; ++i)
  printf("%d\n", i);

// if NULL is left out, it will lead to a segfault
int **integers = (int *[]){
  &(int){ 0 },
  &(int){ 2 },
  // NULL
};

Example

orca

...
struct discord_embed **embeds = {
  &(struct discord_embed){
    .title = "First embed"
  },
  &(struct discord_embed){
    .title = "Second embed"
  },
  NULL
};

for (int i = 0; embeds[i]; ++i)
  printf("%s\n", embeds[i]->title);

concord

...
struct discord_embeds embeds = {
  .size = 2,
  .array = (struct discord_embed []){
    {
      .title = "First embed"
    },
    {
      .title = "Second embed"
    }
  }
};

for (int i = 0; i < embeds->size; ++i)
  printf("%s\n", embeds->array[i].title);

Async requests are now the default behavior

This removes discord_async_next(), and adds a parameter at the end of each request function where user can assign their callbacks to.

Example 1

A asynchronous request that doesn't care about capturing the request response

orca

...
discord_async_next(client, NULL);
struct discord_create_message_params params = { .content = "Hi" };
discord_create_message(client, channel_id, &params, NULL);

concord

...
struct discord_create_message params = { .content = "Hi" };
discord_create_message(client, channel_id, &params, NULL);

Example 2

A asynchronous request that captures request response

orca

void done_create_message(struct discord *client, struct discord_async_ret *ret)
{
  const struct discord_message *msg = ret->ret;
  printf("Message has been sent (by %s)\n", msg->author->username);
}

void fail_create_message(struct discord *client, struct discord_async_err *err)
{
  printf("%s\n", discord_strerror(err->code, client));
}
...
discord_async_next(client, &(struct discord_async_attr){
                             .done = &done_create_message,
                             .fail = &fail_create_message
                           });
struct discord_create_message_params params = { .content = "Hi" };
discord_create_message(client, channel_id, &params, NULL);

concord

void done_create_message(struct discord *client, struct discord_response *resp, const struct discord_message *msg)
{
  printf("Message has been sent (by %s)\n", msg->author->username);
}

void fail_create_message(struct discord *client, struct discord_response *resp)
{
  printf("%s\n", discord_strerror(resp->code, client));
}
...
struct discord_create_message params = { .content = "Hi" };
discord_create_message(client, channel_id, &params, &(struct discord_ret_message){
                                                      .done = &done_create_message,
                                                      .fail = &fail_create_message
                                                    });

Example 3

A synchronous (blocking) request that doesn't care about capturing the request response

orca

...
struct discord_create_message_params params = { .content = "Hi" };
ORCAcode code = discord_create_message(client, channel_id, &params, NULL);
if (code == ORCA_OK)
  printf("Message has been sent\n");
else
  printf("%s\n", discord_strerror(code, client));

concord

...
struct discord_create_message params = { .content = "Hi" };
CCORDcode code = discord_create_message(client, channel_id, &params, &(struct discord_ret_message){
                                                                       .sync = DISCORD_SYNC_FLAG // enable blocking request
                                                                      });
if (code == CCORD_OK)
  printf("Message has been sent\n");
else
  printf("%s\n", discord_strerror(code, client));

Example 4

A synchronous (blocking) request that captures request response

orca

...
struct discord_message msg = { 0 };

struct discord_create_message_params params = { .content = "Hi" };
ORCAcode code = discord_create_message(client, channel_id, &params, &msg);
if (code == ORCA_OK) {
  printf("Message has been sent (by %s)\n", msg.author->username);
  discord_message_cleanup(&msg);
}
else {
  printf("%s\n", discord_strerror(code, client));
}

concord

...
struct discord_message msg = { 0 };

struct discord_create_message params = { .content = "Hi" };
CCORDcode code = discord_create_message(client, channel_id, &params, &(struct discord_ret_message){
                                                                       .sync = &msg // enable blocking request
                                                                      });
if (code == CCORD_OK) {
  printf("Message has been sent (by %s)\n", msg.author->username);
  discord_message_cleanup(&msg);
}
else {
  printf("%s\n", discord_strerror(code, client));
}