Zach Goethel

Source Generation

Source Generation

Repo

Generated code allows developers to skip over tedium and keep focused on important design details.

My source generation platform aims to establish a definitive .NET project architecture of declarative descriptor languages. Several backend and frontend tasks follow patterns which can be boilerplated from a source descriptor document.

Account.model
schema {

int Id,

string Email = {""},

string FirstName = {""},

string LastName = {""}

}

partial WithPassword {

string PasswordScheme = {""},

string PasswordHash = {""},

string PasswordSalt = {""},

DateTime? PasswordSet

}

repo {

dbo.Account_GetById(int id)

=> Account,

dbo.Account_GetWithPassword(string email)

=> Account.WithPassword,

...

}

service {

AttemptLogin(string email, string password)

=> Account.WithSession,

LogOut(),

ResetPassword(string resetToken, string password),

...

}

DTO classes are generated for the table schema and derived partials.

var dto = new Account();
dto = new Account.WithPassword();

A service interface is generated which must be implemented fully in the application backend.

public class AccountService : Account.IBackendService
{ ... }

Also accompanying functions to call SQL stored procedures and API endpoints.

Account.Repository repo;
repo.Account_GetWithPassword(...);
Account.IService service;
service.AttemptLogin(...);

The developer can build large portions of the application codebase from succinct lines leveraging structured generation patterns.

Language Server with Visual Studio integration
Language server and Visual Studio 2022
(above: source generation language server running next to Visual Studio 2022)

Syntax highlighting and validation
Custom syntax highlighting
(above: custom syntax highlighting extension with semantic colors)
Rudimentary syntax error messages
(above: simple validation messages for syntax correctness)

This Site

My portfolio site is written in my own reactive declarative markup language, serving as a testbed for new source generation features.

Some demos:

Count: 0
state {
	int count
}

interface {
	Increment(),
	Decrement()
}

div(| class = {"d-flex flex-row"} |
	div(| class = {"my-auto"} |
		{"Count: " + count}
	)
	button(|
		class = {"btn btn-danger ms-2"},
		onclick = {"dispatch(this, 'Decrement');"}
		|
		<">-</">
	)
	button(|
		class = {"btn btn-success ms-2"},
		onclick = {"dispatch(this, 'Increment');"}
		|
		<">+</">
	)
)
namespace TestApp.Views;

using Generated;

public class UpDownDemo : UpDownDemoBase
{
    public UpDownDemo(IServiceProvider sp) : base(sp)
    {
    }

    public override void Decrement()
    {
        count--;
    }

    public override void Increment()
    {
        count++;
    }
}

Serverside code is generated to render HTML for the component. Controller endpoints are also generated corresponding to any actions within the component interface.

Some minimal JS swaps out DOM elements to reactively update the page to reflect its latest state.

ValueActions
Row 1
Row 2
state {
	List<string> rows = {new() { "Row 1", "Row 2" }}
}

interface {
	Add(string row),
	Remove(int index),
	MoveUp(int index),
	MoveDown(int index)
}

table(| class = {"table table-striped table-bordered"} |
	colgroup(
		col(| style = {"width: 70%;"} |)
		col(| style = {"width: 30%;"} |)
	)
	thead(
		tr(
			th(<">Value</">)
			th(<">Actions</">)
		)
	)
	tbody(
		tr(
			td(
				input(|
					type = {"text"},
					id = {"table-add"},
					class = {"form-control"},
					value = {rows.Count >= 5 ? "Max 5 rows" : ""},
					disabled = {rows.Count >= 5}
					|)
			)
			td(
				button(|
					type = {"button"},
					class = {"btn btn-outline-primary"},
					onclick = {"dispatch(this, 'Add', { row: $('#table-add').val() });"},
					disabled = {rows.Count >= 5}
					|
					<">Add</">
				)
			)
		)
		foreach({var (row, i) in rows.Select((it, i) => (it, i))}

			tr(
				td({string.IsNullOrEmpty(row) ? "Blank!" : row})
				td(
					i(|
						class = {"fa fa-arrow-up me-2"},
						style = {"cursor: pointer;"},
						onclick = {"dispatch(this, 'MoveUp', { index: " + i + " });"}
						|)
					i(|
						class = {"fa fa-arrow-down me-2"},
						style = {"cursor: pointer;"},
						onclick = {"dispatch(this, 'MoveDown', { index: " + i + " });"}
						|)
					i(|
						class = {"fa fa-trash me-2"},
						style = {"cursor: pointer;"},
						onclick = {"dispatch(this, 'Remove', { index: " + i + " });"}
						|)
				)
			)
		)
	)
)
namespace TestApp.Views;

using Generated;

public class TableRowDemo : TableRowDemoBase
{
    public TableRowDemo(IServiceProvider sp) : base(sp)
    {
    }

    public override void Add(string row)
    {
        if (rows.Count < 5)
        {
            rows.Add(row);
        }
    }

    public override void MoveDown(int index)
    {
        if (index >= rows.Count - 1)
        {
            return;
        }
        var temp = rows[index];
        rows[index] = rows[index + 1];
        rows[index + 1] = temp;
    }

    public override void MoveUp(int index)
    {
        if (index <= 0)
        {
            return;
        }
        var temp = rows[index];
        rows[index] = rows[index - 1];
        rows[index - 1] = temp;
    }

    public override void Remove(int index)
    {
        rows.RemoveAt(index);
    }
}
Created by Zach Goethel. Have a wonderful day!