Sourcegen-powered object mappers in C#

In most of sufficiently complicated C# projects you need to map one object to another. This is a very tedious and error-prone task so libraries were created to automate this process, most famous one being Automapper. It comes with its own drawbacks, as it uses reflection which makes it slower than custom mappers and it’s (arguably) more complex to set up. With incremental source generators available from .NET 6, is there a better way?

Read More

Reporting code coverage in Rust projects

If you’re writing tests for your code you probably want to measure your code coverage. Doing this helps you identify untested parts of your codebase and understand how many more tests you need to write. Putting code coverage reporting into your CI pipeline will help maintain quality gate failing the build if coverage falls below the threshold.

In this article when I say “code coverage” I will mean “line coverage” unless specified differently. Let’s take for example a simple web service in Actix - popular Rust web framework. This web service has no external dependencies and will simply generate fake weather readings when you GET the root. Here’s the code:

Read More

Cutting allocations when formatting strings in C#

Imagine having a collection of strings that you want to surround with % (like variables in batch scripts if you had to work with those). The code for that is simple in modern C#:

1
public string FormatStr(string str) => $"%{str}%";

Now let’s say some of those strings are enclosed in square brackets [] and you want to remove these brackets if present. Naive code for that is just as easy:

1
public string ChangeAsStr(string str) => $"%{str.TrimStart('[').TrimEnd(']')}%";

Read More

Speeding up CPU-bound jobs in .NET (C#)

In this article I will discuss optimizing a CPU-bound task in C# by parallelizing it. As a simple but somewhat practical number-crunching task I have chosen is calculating π number with Monte-Carlo integration (I have described the method in my previous article when exploring concurrency in Rust).

For a start let’s look at the naive implementation:

1
2
3
4
5
6
7
8
public double CalculateSequentially(int iters)
{
var pointsInCircle = GetPointsInCircle(iters);

var ratio = (double) pointsInCircle / (double) iters;

return ratio * 4d;
}

Read More

Using Pkl with ASP.NET

Recently Apple has released a Pkl - language to write your configuration as code. It has integrations with several languages like Kotlin and Java, but no .NET (unfortunately). Still it’s easy to start using for basic tasks like generating configuration (appsettings.json) as shown below.

First of all, make sure you have Pkl installed. I’ll be using ASP.NET MVC app in this example freshly created from template. We’re going to generate appsettings.json on build, so feel free to remove the existing file and add it to gitignore. Then let’s come up with a simple use case for Pkl - say you want to run your ASP.NET app on a specific port. There are several ways to achieve that in ASP.NET, but to do it through appsettings.json, you need to add the following line to the root:

1
"Urls": "http://*:5500"

Read More

Tiny script to decode JWT (Json Web Token)

If you’re using something like OpenID Connect for authentication you might need to decode JWT tokens when debugging a problem. Common practice is to use something like jwt.io to do that. However, if posting sensitive data to online decoders also feels wrong to you and you don’t need to verify the signature, here’s a tiny node script that will do the trick:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if (process.argv.length < 3) {
console.error('Missing token argument!');
process.exit(1);
}

const token = process.argv[2];

decodeAndPrint(token);

function convertToPureBase64(base64UrlStr){
const base64String = base64UrlStr.replace(/-/g, '+').replace(/_/g, '/');
return base64String;
}

function decodeAndPrint(token){
const parts = token.split('.');
if(parts.length < 3){
console.error("Invalid token!");
process.exit(1);
}

const header = JSON.parse(atob(convertToPureBase64(parts[0])));
const payload = JSON.parse(atob(convertToPureBase64(parts[1])));

console.log("\n", header, "\n\n");
console.log(payload, "\n");
}

Since JWT uses Base64Url we need to convert it to regular Base64 and then we can use built-in JS atob function. Use the script like node script.js [token].

That’s it! I found it useful when I need do some simple debugging.

Incremental source generators in C# for fun and profit

Code generation was added in .NET 5 in form of source generators - basically a Roslyn analyzer that emits source code. And apparently it’s quite popular with .NET community. However, initial (v1) implementation of source generators has suffered from performance problems described in this great article which made it impractical to use in most real-life applications. So in .NET 6 incremental source generators (v2) were rolled out. Still, on a high level the way how source generators - both v1 and v2 - work looks something like this:

Further below, I will describe an example problem and how it can be solved with an incremental source generator.

Read More

Generate Gitlab downstream pipelines with F#

Your pipelines look too simple? Are your fellow developers/devops laughing at you? Have no fear, you can add an extra layer of complexity with running generated downstream pipelines! We’ll also use F# to generate aforementioned downstream pipeline configuration, because why not.

Our project for which we use the generated pipeline will be PrimeChecker - a simple console app to check if provided number is a prime. Actually it doesn’t even matter what the app does, all that matters is its project file which is shown below:

1
2
3
4
5
6
7
8
9
10
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>

Read More

Reviewing code of Rust pong game by GPT 3.5

It’s been a month since GPT-4 has been released, so time to talk about how well GPT-3.5 is generating code, right? In my defense, GPT-4 still isn’t available for everybody so many people are using 3.5 version.

Task

Task is as follows: write a Pong clone for a terminal in Rust. Pong game is very simple, but it’s not common to play it in your terminal. Rust was chosen because it’s arguably harder than JS/TS. Simplicity of the task is important, since chatGPT output is limited and while you can always ask it to continue the output, there’re various problems e.g. with formatting when doing that.

Read More