generating a configuration page using go reflection

a little history:

while I was developing vinegar, a small program that came about to replace it's competitor which was slowly dying, me and my co-founder decided to make the application function transparently. what that meant was, there was no configuration required to make it work, and there was no GUI, it was all entirely hidden from the user.

this was a bad idea, and we needed to expose a way to configure vinegar, and have some way to report progress to the end user (note: i would not make this mistake now). there were many GUI toolkits at the time for Go, such as gio, fyne, and gotk4. after some experimentation, we decided to stick with Gio, a immediate ui toolkit, to indicate progress to the user. however, building a configuration page to me, as i was initially learning Go for the project, was difficult, so we went with plain-text configuration, that was shown via a desktop file.

some time passes, and a libadwaita UI mockup for vinegar appears, made by jamie. in it, included a full configurator and loading screen. I took this as inspiration to go and attempt to move vinegar to puregotk. I deliberately chose puregotk, which was a GTK4 library for Go that was relatively new, as it relied on purego and not CGo, unlike gotk4. this alone lowered compile times from 1 hour for gotk4 on a normal machine, to just a few seconds using puregotk. this took two attempts, with the first attempt resulting in me losing my code, with a few months of a gap inbetween. of course, puregotk was a little trickier to use, but thanks to a year of learning Go, i was able to satisfy only half of the mockup. why? configuration was too difficult, especially when the UI was defined by GTK builder, and i had to connect each and every single row to its configuration counterpart, which included normal types as well as maps, so I reluctantly opted for a configuration page that was literally just the configuration file, and worked fine, despite being very unconventional.

fast forward to now, I had became sole maintainer of vinegar. oh right, I didn't even tell you what it does, and I won't, as I'm not very fond of the company it was made by. anyway, I took it upon myself to find another way to add a configuration page, and that was to completely generate the configuration page using Go reflection. since vinegar was still relying on a configuration file, I had to also implement reflection logic to avoid saving default values to the file. how the configuration generation works, is it loops over the configuration struct's reflected values, and uses struct tags to define the description and other types, such as possible values for strings, or a path selector, and the group for this configuration value:

type Studio struct {
    GameMode bool `toml:"gamemode" group:"Behavior" row:"Apply system optimizations. May improve performance."`

    DXVK      DxvkVersion `toml:"dxvk" group:"Rendering" row:"Improve D3D11 compatibility by translating it to Vulkan,entry,DXVK Version,2.7"`
    Renderer  string      `toml:"renderer" group:"Rendering" row:"Studio's Graphics Mode,vals,D3D11,D3D11FL10,Vulkan,OpenGL"` // Enum reflection is impossible

  WebView  string `toml:"webview" group:"Custom Wine" row:"Installs WebView2 for web pages in Studio,entry,WebView2 Runtime Version,109.0.1518.140"`
...

with this, generating a configuration page was very easy, as i simply had to iterate, create groups, and add the values to their groups. the resultant package of this was called adwaux, which you can find in the source tree at internal/adwaux. as for how it looks, well, feel free to visit the project page. and also thanks to jamie, again, for helping me design the new interface, which is more similar to normal GNOME apps than ever before.