gout

A static git page generator
git clone https://git.bracken.jp/gout.git
Log | Files | Refs | README | LICENSE

refs.c (4907B)


      1 #include "writer/gemini/refs.h"
      2 
      3 #include <assert.h>
      4 #include <err.h>
      5 #include <stdbool.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 
      9 #include "format.h"
     10 #include "git/commit.h"
     11 #include "third_party/openbsd/reallocarray.h"
     12 #include "utils.h"
     13 #include "writer/gemini/page.h"
     14 
     15 typedef struct {
     16   char* title;
     17   FILE* out;
     18 } GeminiRefsTable;
     19 
     20 typedef struct {
     21   char* shorthand;
     22   time_t author_time;
     23   char* author_name;
     24 } RefData;
     25 
     26 typedef struct {
     27   RefData* items;
     28   size_t count;
     29   size_t capacity;
     30 } RefList;
     31 
     32 struct GeminiRefs {
     33   const GitRepo* repo;
     34   const FileSystem* fs;
     35   FILE* out;
     36   GeminiPage* page;
     37   RefList branches;
     38   RefList tags;
     39 };
     40 
     41 static GeminiRefsTable* gemini_refstable_create(const char* title, FILE* out);
     42 static void gemini_refstable_free(GeminiRefsTable* table);
     43 static void gemini_refstable_begin(GeminiRefsTable* table);
     44 static void gemini_refstable_add_ref(GeminiRefsTable* table,
     45                                      const RefData* ref);
     46 static void gemini_refstable_end(GeminiRefsTable* table);
     47 
     48 static GeminiRefsTable* gemini_refstable_create(const char* title, FILE* out) {
     49   assert(title != NULL);
     50   assert(out != NULL);
     51   GeminiRefsTable* table = ecalloc(1, sizeof(GeminiRefsTable));
     52   table->title = estrdup(title);
     53   table->out = out;
     54   return table;
     55 }
     56 
     57 static void gemini_refstable_free(GeminiRefsTable* table) {
     58   if (!table) {
     59     return;
     60   }
     61   free(table->title);
     62   free(table);
     63 }
     64 
     65 static void gemini_refstable_begin(GeminiRefsTable* table) {
     66   assert(table != NULL);
     67   fprintf(table->out, "## %s\n\n", table->title);
     68   fprintf(table->out, "```\n");
     69   print_utf8_padded(table->out, "Name", 32, ' ');
     70   fprintf(table->out, "  ");
     71   print_utf8_padded(table->out, "Last commit date", 16, ' ');
     72   fprintf(table->out, "  Author\n");
     73 }
     74 
     75 static void gemini_refstable_add_ref(GeminiRefsTable* table,
     76                                      const RefData* ref) {
     77   assert(table != NULL);
     78   assert(ref != NULL);
     79   FILE* out = table->out;
     80 
     81   print_utf8_padded(out, ref->shorthand, 32, ' ');
     82   fprintf(out, "  ");
     83   print_time_short(out, ref->author_time);
     84   fprintf(out, "  %s\n", ref->author_name);
     85 }
     86 
     87 static void gemini_refstable_end(GeminiRefsTable* table) {
     88   assert(table != NULL);
     89   fprintf(table->out, "```\n\n");
     90 }
     91 
     92 static void reflist_add(RefList* list, const GitReference* ref) {
     93   if (list->count >= list->capacity) {
     94     list->capacity = list->capacity == 0 ? 16 : list->capacity * 2;
     95     list->items = reallocarray(list->items, list->capacity, sizeof(RefData));
     96     if (!list->items) {
     97       err(1, "reallocarray");
     98     }
     99   }
    100   RefData* item = &list->items[list->count++];
    101   item->shorthand = estrdup(ref->shorthand);
    102   if (ref->commit) {
    103     item->author_time = ref->commit->author_time;
    104     item->author_name = estrdup(ref->commit->author_name);
    105   } else {
    106     item->author_time = 0;
    107     item->author_name = estrdup("");
    108   }
    109 }
    110 
    111 static void reflist_clear(RefList* list) {
    112   for (size_t i = 0; i < list->count; i++) {
    113     free(list->items[i].shorthand);
    114     free(list->items[i].author_name);
    115   }
    116   free(list->items);
    117   list->items = NULL;
    118   list->count = 0;
    119   list->capacity = 0;
    120 }
    121 
    122 GeminiRefs* gemini_refs_create(const GitRepo* repo, const FileSystem* fs) {
    123   assert(repo != NULL);
    124   assert(fs != NULL);
    125   GeminiRefs* refs = ecalloc(1, sizeof(GeminiRefs));
    126   refs->repo = repo;
    127   refs->fs = fs;
    128   refs->out = fs->fopen("refs.gmi", "w");
    129   if (!refs->out) {
    130     err(1, "fopen: refs.gmi");
    131   }
    132   refs->page = gemini_page_create(refs->out, repo, fs, "Refs", "");
    133   return refs;
    134 }
    135 
    136 void gemini_refs_free(GeminiRefs* refs) {
    137   if (!refs) {
    138     return;
    139   }
    140   refs->fs->fclose(refs->out);
    141   gemini_page_free(refs->page);
    142   reflist_clear(&refs->branches);
    143   reflist_clear(&refs->tags);
    144   free(refs);
    145 }
    146 
    147 void gemini_refs_begin(GeminiRefs* refs) {
    148   assert(refs != NULL);
    149   gemini_page_begin(refs->page);
    150 }
    151 
    152 void gemini_refs_add_ref(GeminiRefs* refs, const GitReference* ref) {
    153   assert(refs != NULL);
    154   assert(ref != NULL);
    155   switch (ref->type) {
    156     case kReftypeBranch:
    157       reflist_add(&refs->branches, ref);
    158       break;
    159     case kReftypeTag:
    160       reflist_add(&refs->tags, ref);
    161       break;
    162   }
    163 }
    164 
    165 void gemini_refs_end(GeminiRefs* refs) {
    166   assert(refs != NULL);
    167   if (refs->branches.count > 0) {
    168     GeminiRefsTable* table = gemini_refstable_create("Branches", refs->out);
    169     gemini_refstable_begin(table);
    170     for (size_t i = 0; i < refs->branches.count; i++) {
    171       gemini_refstable_add_ref(table, &refs->branches.items[i]);
    172     }
    173     gemini_refstable_end(table);
    174     gemini_refstable_free(table);
    175   }
    176   if (refs->tags.count > 0) {
    177     GeminiRefsTable* table = gemini_refstable_create("Tags", refs->out);
    178     gemini_refstable_begin(table);
    179     for (size_t i = 0; i < refs->tags.count; i++) {
    180       gemini_refstable_add_ref(table, &refs->tags.items[i]);
    181     }
    182     gemini_refstable_end(table);
    183     gemini_refstable_free(table);
    184   }
    185   gemini_page_end(refs->page);
    186 }