<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rayaz blog]]></title><description><![CDATA[Rayaz blog]]></description><link>https://rayaz.ventures</link><generator>RSS for Node</generator><lastBuildDate>Tue, 12 May 2026 16:04:50 GMT</lastBuildDate><atom:link href="https://rayaz.ventures/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Passing around database transactions]]></title><description><![CDATA[I'm not a huge fan of reflection-based ORMs, which is one of the reasons that I've been using sqlc and some service types for my business logic.
Sqlc is great. I define my migrations and my queries and with the POWER OF CODE GENERATION™ ... I get a b...]]></description><link>https://rayaz.ventures/passing-around-database-transactions</link><guid isPermaLink="true">https://rayaz.ventures/passing-around-database-transactions</guid><category><![CDATA[Go Language]]></category><category><![CDATA[sqlc]]></category><category><![CDATA[transactions]]></category><dc:creator><![CDATA[Ryan Faerman]]></dc:creator><pubDate>Thu, 15 Feb 2024 22:04:59 GMT</pubDate><content:encoded><![CDATA[<p>I'm not a huge fan of reflection-based ORMs, which is one of the reasons that I've been using sqlc and some service types for my business logic.</p>
<p>Sqlc is great. I define my migrations and my queries and with the <strong>POWER OF CODE GENERATION™</strong> ... I get a bunch of go types and functions that perform my database queries. They don't always map directly to my actual data models though, sometimes they don't have every field or it's in another form that needs some additional massaging -- so I like to have a layer on top of the sqlc-generated stuff that makes interacting with the database easier.</p>
<p>Not only does this make the API to the data more friendly, I find that it can make testing easier too. My models don't really have much database stuff in them (save for an ID) and my raw queries are made safe with sqlc. Hooray for the power of abstractions!</p>
<p>One thing that is slightly challenging, or at least annoying, is when I need to work with transactions <em>and</em> compose my business-logic. Each function of my service layer needs to create a transaction or work within an existing transaction. So, how do I make it work without just copying the logic between the service functions?</p>
<p>Enter, my fancy-pants transaction function that I name (boringly enough) <code>transaction</code>.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> ctxKey <span class="hljs-keyword">int</span>

<span class="hljs-keyword">const</span> (
    ctxKeyTX ctxKey = <span class="hljs-literal">iota</span>
)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">transaction</span><span class="hljs-params">(ctx context.Context, fn <span class="hljs-keyword">func</span>(context.Context, *dao.Queries)</span> <span class="hljs-title">error</span>) <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">var</span> (
        tx  *sql.Tx
        err error
        ok  <span class="hljs-keyword">bool</span>
    )
    tx, ok = ctx.Value(ctxKeyTX).(*sql.Tx)
    <span class="hljs-keyword">if</span> !ok {
        tx, err = global.db.BeginTx(ctx, <span class="hljs-literal">nil</span>)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> err
        }
        ctx = context.WithValue(ctx, ctxKeyTX, tx)
        <span class="hljs-keyword">defer</span> tx.Rollback()
    }

    err = fn(ctx, global.dao.WithTx(tx))
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }
    <span class="hljs-keyword">if</span> ok {
        <span class="hljs-keyword">return</span> tx.Commit()
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>Using it is easy enough, you just need to wrap the database bits of the service in a transaction. I've basically pulled the example below from my application. There are two functions <code>UpdateCallsign</code> and <code>SaveSettings</code>. Each one can be composed elsewhere in the application. By wrapping the actual logic bits -- I can share the actual database transaction if we're in one, otherwise a new one gets created.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s Account)</span> <span class="hljs-title">UpdateCallsign</span><span class="hljs-params">(ctx context.Context, ...)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">return</span> transaction(ctx, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context, qtx *dao.Queries)</span> <span class="hljs-title">error</span></span> {
        <span class="hljs-comment">// ....</span>
        <span class="hljs-keyword">return</span> qtx.UpdateCallsignForAccount(ctx, ...)
    })
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s Account)</span> <span class="hljs-title">SaveSettings</span><span class="hljs-params">(ctx context.Context, ...)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">return</span> transaction(ctx, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx context.Context, qtx *dao.Queries)</span> <span class="hljs-title">error</span></span> {
        <span class="hljs-comment">// ....</span>
        <span class="hljs-keyword">return</span> qtx.UpdateSettingsForAccount(ctx, ...)
    })
}
</code></pre>
<p>Also, since the transaction is tied to a context at the beginning, if the http request (or any request really) is cancelled midway through -- the transactions all get rolled back.</p>
<p>That about does it, relatively simple. Just toss the transaction into the context and pull it out if it's available.</p>
<p>Oh, and the reason I am using <code>s Account</code> for the type and not <code>a Account</code> is because those are functions on a service. I've been using the single letter of the category: m for model, s for service, h for handler, e for event.</p>
]]></content:encoded></item><item><title><![CDATA[Authorization made most simple]]></title><description><![CDATA[One of the things I often struggle with in my go applications is authorization. Lots of the tools out there are complex or made for use-cases much larger than my own. In a recent project I came up with a very simple approach.
TLDR; Checkout a generic...]]></description><link>https://rayaz.ventures/authorization-made-most-simple</link><guid isPermaLink="true">https://rayaz.ventures/authorization-made-most-simple</guid><category><![CDATA[Go Language]]></category><category><![CDATA[authorization]]></category><dc:creator><![CDATA[Ryan Faerman]]></dc:creator><pubDate>Sun, 04 Feb 2024 15:10:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cVMaxt672ss/upload/9cfb303ed9c960c786453b608d404c25.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the things I often struggle with in my go applications is authorization. Lots of the tools out there are complex or made for use-cases much larger than my own. In a recent project I came up with a very simple approach.</p>
<p><strong>TLDR;</strong> Checkout a generic form here: <a target="_blank" href="https://github.com/ryanfaerman/can">https://github.com/ryanfaerman/can</a></p>
<p>I'll show you what I came up with first and then, a slightly more generic version.</p>
<p>Let's define a couple interfaces and a model:</p>
<pre><code class="lang-go">
<span class="hljs-comment">// Canner allows a resource to define if an account can perform </span>
<span class="hljs-comment">// a given action.</span>
<span class="hljs-keyword">type</span> Canner <span class="hljs-keyword">interface</span> {
    Can(account *Account, action <span class="hljs-keyword">string</span>) error
}

<span class="hljs-comment">// Verber is used to allow a resource to define the actions </span>
<span class="hljs-comment">// it supports.</span>
<span class="hljs-keyword">type</span> Verber <span class="hljs-keyword">interface</span> {
    Verbs() []<span class="hljs-keyword">string</span>
}

<span class="hljs-keyword">type</span> Account {
    ID        <span class="hljs-keyword">int</span>
    Name      <span class="hljs-keyword">string</span>
    Anonymous <span class="hljs-keyword">bool</span>
    <span class="hljs-comment">// ... use your actual model</span>
}

<span class="hljs-keyword">var</span> AnonymousAccount = Account{ID: <span class="hljs-number">-1</span>, Name: <span class="hljs-string">"Anon"</span>, Anonymous: <span class="hljs-literal">true</span>}
</code></pre>
<p>And while we're at it, a basic container type:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Policy is a map of actions to functions that</span>
<span class="hljs-comment">// return an error if the action cannot be performed for the </span>
<span class="hljs-comment">// given account.</span>
<span class="hljs-keyword">type</span> Policy <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(*Account)</span> <span class="hljs-title">error</span></span>

<span class="hljs-comment">// Verbs implements the Verber interface.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(p Policy)</span> <span class="hljs-title">Verbs</span><span class="hljs-params">()</span> []<span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">var</span> verbs []<span class="hljs-keyword">string</span>
    <span class="hljs-keyword">for</span> verb := <span class="hljs-keyword">range</span> p {
        verbs = <span class="hljs-built_in">append</span>(verbs, verb)
    }
    <span class="hljs-keyword">return</span> verbs
}

<span class="hljs-comment">// Can implements the Canner interface.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(p Policy)</span> <span class="hljs-title">Can</span><span class="hljs-params">(account *Account, action <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> fn, ok := p[action]; ok {
        <span class="hljs-keyword">return</span> fn(account)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// An example default policy.</span>
<span class="hljs-keyword">var</span> DefaultPolicy = Policy{
    <span class="hljs-comment">// Anonymous users aren't allowed to view anything</span>
    <span class="hljs-string">"view"</span>: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(account *Account)</span> <span class="hljs-title">error</span></span> {
        <span class="hljs-keyword">if</span> account.Anonymous {
            <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"must not be anonymous"</span>)
        }
    },

    <span class="hljs-comment">// Editing is disabled for everyone</span>
    <span class="hljs-string">"edit"</span>: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(account *Account)</span> <span class="hljs-title">error</span></span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"editing is forbidden"</span>)
    },
}
</code></pre>
<p>And, let's also create an example subject that we want to act on:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> BlogPost <span class="hljs-keyword">struct</span> {
    Title <span class="hljs-keyword">string</span>
    Body  <span class="hljs-keyword">string</span>
    Owner *Account
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(b *BlogPost)</span> <span class="hljs-title">Verbs</span><span class="hljs-params">()</span> []<span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> []<span class="hljs-keyword">string</span>{<span class="hljs-string">"view"</span>, <span class="hljs-string">"edit"</span>, <span class="hljs-string">"promote"</span>, <span class="hljs-string">"delete"</span>}
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(b *BlogPost)</span> <span class="hljs-title">Can</span><span class="hljs-params">(account *Account, action <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">switch</span> action {
        <span class="hljs-keyword">case</span> <span class="hljs-string">"view"</span>:
            <span class="hljs-comment">// anyone can view</span>
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        <span class="hljs-keyword">case</span> <span class="hljs-string">"edit"</span>
            <span class="hljs-keyword">if</span> b.Owner.ID != account.ID {
                <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"cannot edit someone elses blogpost"</span>)
            }
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        <span class="hljs-comment">// case ...</span>
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>Now with those few things, we can build out the actual function that makes this idea work:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Can</span><span class="hljs-params">(account *Account, action <span class="hljs-keyword">string</span>, resources ...any)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> account == <span class="hljs-literal">nil</span> {
        account = AnonymousAccount
    }
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(resources) == <span class="hljs-number">0</span> {
        resources = <span class="hljs-built_in">append</span>(resources, DefaultPolicy)
    }

    <span class="hljs-keyword">for</span> _, resource := <span class="hljs-keyword">range</span> resources {
        <span class="hljs-keyword">if</span> r, ok := resource.(Verber); ok {
            <span class="hljs-keyword">if</span> !slices.Contains(r.Verbs(), action) {
                <span class="hljs-keyword">return</span> ErrInvalidAction
            }
        }

        <span class="hljs-keyword">if</span> r, ok := resource.(Canner); ok {
            <span class="hljs-keyword">if</span> err := r.Can(account, action); err != <span class="hljs-literal">nil</span> {
                <span class="hljs-keyword">return</span> errors.Join(ErrNotAuthorized, err)
            }
        }
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// Some sugar!</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Cannot</span><span class="hljs-params">(account *Account, action <span class="hljs-keyword">string</span>, resources ...any)</span> <span class="hljs-title">error</span></span> {
    !Can(account, action, resources...)
}
</code></pre>
<p>Now, scattered in our codebase we can do things like:</p>
<pre><code class="lang-go"><span class="hljs-comment">// ..</span>
b := LoadBlogPost()
<span class="hljs-keyword">if</span> Cannot(someAccount, <span class="hljs-string">"edit"</span>, b) {
    <span class="hljs-comment">//... do something</span>
}
</code></pre>
<p>Granted, this is partially contrived. In my application I'm using an actual model, not just a random struct, but most of it is pretty similar to what I have.</p>
<p>Two more fun things before we wrap this up.</p>
<p>If you're using templ, like I am, I made a couple helpers.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">CurrentAccount</span><span class="hljs-params">(ctx context.Context)</span> *<span class="hljs-title">models</span>.<span class="hljs-title">Account</span></span> {
    <span class="hljs-comment">// Get the current user from the account</span>
    <span class="hljs-comment">// Or return an AnonymousUser</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">UserCan</span><span class="hljs-params">(ctx context.Context, action <span class="hljs-keyword">string</span>, resources ...any)</span> <span class="hljs-title">bool</span></span> {
    <span class="hljs-keyword">if</span> err := services.Authorization.Can(CurrentAccount(ctx), action, resources...); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
}

templ Can(action <span class="hljs-keyword">string</span>, resources ...any) {
    <span class="hljs-keyword">if</span> UserCan(ctx, action, resources...) {
        { children... }
    }
}

templ Cannot(action <span class="hljs-keyword">string</span>, resources ...any) {
    <span class="hljs-keyword">if</span> !UserCan(ctx, action, resources...) {
        { children... }
    }
}

<span class="hljs-comment">// in some templ file:</span>

@Can(<span class="hljs-string">"edit"</span>, theBlogPost) {
    &lt;h1&gt;You can Edit this!&lt;/h1&gt;
}
@Cannot(<span class="hljs-string">"edit"</span>, theBlogPost) {
    &lt;h1&gt;Sorry bud! You cannot do that&lt;/h1&gt;
}
</code></pre>
<p>I've split up the <code>UserCan</code> from <code>Can</code> and <code>Cannot</code> because doing assignments and error checking is a bit of a pain in templ.</p>
<p>If you want to see this in a less of a contrived fashion, you can view it in action in my application: <a target="_blank" href="https://github.com/ryanfaerman/netctl/blob/master/internal/services/authorization.go">https://github.com/ryanfaerman/netctl/blob/master/internal/services/authorization.go</a></p>
<p>I've also made a simple package with a generic form here: <a target="_blank" href="https://github.com/ryanfaerman/can">https://github.com/ryanfaerman/can</a></p>
]]></content:encoded></item><item><title><![CDATA[Handling odd JSON decoding in Go]]></title><description><![CDATA[I was recently writing a client for a JSON-based API that had some unique behavior. The API lets you look up ham radio callsigns and their associated information. If the callsign exists you get back a JSON object with those details.
The JSON of a val...]]></description><link>https://rayaz.ventures/handling-odd-json-decoding-in-go</link><guid isPermaLink="true">https://rayaz.ventures/handling-odd-json-decoding-in-go</guid><category><![CDATA[golang]]></category><category><![CDATA[Golang developer]]></category><category><![CDATA[json]]></category><dc:creator><![CDATA[Ryan Faerman]]></dc:creator><pubDate>Thu, 11 Jan 2024 16:14:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/g5f0BJq-FRs/upload/8d345a8a964f3c79974496fec189da69.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was recently writing a client for a JSON-based API that had some unique behavior. The API lets you look up ham radio callsigns and their associated information. If the callsign exists you get back a JSON object with those details.</p>
<p>The JSON of a valid callsign is as follows (redacted for privacy):</p>
<pre><code class="lang-json">{  
   <span class="hljs-attr">"hamdb"</span>:{  
      <span class="hljs-attr">"version"</span>:<span class="hljs-string">"1"</span>,
      <span class="hljs-attr">"callsign"</span>:{
         <span class="hljs-attr">"call"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"class"</span>:<span class="hljs-string">"E"</span>,
         <span class="hljs-attr">"expires"</span>:<span class="hljs-string">"09/25/2025"</span>,
         <span class="hljs-attr">"status"</span>:<span class="hljs-string">"A"</span>,
         <span class="hljs-attr">"grid"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"lat"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"lon"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"fname"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"mi"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"name"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"suffix"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"addr1"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"addr2"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"state"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"zip"</span>:<span class="hljs-string">"redacted"</span>,
         <span class="hljs-attr">"country"</span>:<span class="hljs-string">"United States"</span>
      },
      <span class="hljs-attr">"messages"</span>:{
         <span class="hljs-attr">"status"</span>:<span class="hljs-string">"OK"</span>
      }
   }
}
</code></pre>
<p>But look what happens when you query for a callsign that can't be found:</p>
<pre><code class="lang-json">{  
   <span class="hljs-attr">"hamdb"</span>:{  
      <span class="hljs-attr">"version"</span>:<span class="hljs-string">"1"</span>,
      <span class="hljs-attr">"callsign"</span>:{
         <span class="hljs-attr">"call"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"class"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"expires"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"status"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"grid"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"lat"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"lon"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"fname"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"mi"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"name"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"suffix"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"addr1"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"addr2"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"state"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"zip"</span>:<span class="hljs-string">"NOT_FOUND"</span>,
         <span class="hljs-attr">"country"</span>:<span class="hljs-string">"NOT_FOUND"</span>
      },
      <span class="hljs-attr">"messages"</span>:{
         <span class="hljs-attr">"status"</span>:<span class="hljs-string">"NOT_FOUND"</span>
      }
   }
}
</code></pre>
<p>Every field is <code>NOT_FOUND</code>. This makes unmarshalling that data into a struct a real challenge. The <code>expires</code> field can be either a parseable date or a string!</p>
<p>The solution I came up with was to introduce a new generic type that can handle this behavior:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Unknowable[K comparable] <span class="hljs-keyword">struct</span> {
    Value K
    Known <span class="hljs-keyword">bool</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(u *Unknowable[K])</span> <span class="hljs-title">UnmarshalJSON</span><span class="hljs-params">(b []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">error</span></span> {
    s := strings.Replace(<span class="hljs-keyword">string</span>(b), <span class="hljs-string">`"`</span>, <span class="hljs-string">``</span>, <span class="hljs-number">-1</span>)
    <span class="hljs-keyword">switch</span> s {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"NOT_FOUND"</span>:
        u.Known = <span class="hljs-literal">false</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    <span class="hljs-keyword">default</span>:
        u.Known = <span class="hljs-literal">true</span>
        <span class="hljs-keyword">var</span> v K

        <span class="hljs-keyword">switch</span> any(v).(<span class="hljs-keyword">type</span>) {
        <span class="hljs-keyword">case</span> <span class="hljs-keyword">float64</span>:
            f, err := strconv.ParseFloat(s, <span class="hljs-number">64</span>)
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                <span class="hljs-keyword">return</span> err
            }
            v = any(f).(K)

        <span class="hljs-keyword">case</span> <span class="hljs-keyword">int</span>:
            f, err := strconv.Atoi(s)
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                <span class="hljs-keyword">return</span> err
            }
            v = any(f).(K)

        <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">if</span> err := json.Unmarshal(b, &amp;v); err != <span class="hljs-literal">nil</span> {
                <span class="hljs-keyword">return</span> err
            }
        }

        u.Value = v
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>The goofy <code>any(v).(type)</code> switch is because I couldn't quite figure out how to get the concrete types to parse correctly without doing something different to each of them. This is partly because I wanted to have real types for things like the <code>lat</code> and <code>lon</code>, not just strings.</p>
<p>Now I can represent the types I want to unmarshal within the destination struct directly.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Callsign <span class="hljs-keyword">struct</span> {
    Call          <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"call"`</span>
    Class         LicenseClass          <span class="hljs-string">`json:"class"`</span>
    Expires       Unknowable[time.Time] <span class="hljs-string">`json:"expires"`</span>
    Status        <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"status"`</span>
    Grid          <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"grid"`</span>
    Lat           Unknowable[<span class="hljs-keyword">float64</span>]   <span class="hljs-string">`json:"lat"`</span>
    Lon           Unknowable[<span class="hljs-keyword">float64</span>]   <span class="hljs-string">`json:"lon"`</span>
    FirstName     <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"fname"`</span>
    MiddleInitial <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"mi"`</span>
    LastName      <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"name"`</span>
    Suffix        <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"suffix"`</span>
    Address       <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"addr1"`</span>
    City          <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"addr2"`</span>
    State         <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"state"`</span>
    Zip           Unknowable[<span class="hljs-keyword">int</span>]       <span class="hljs-string">`json:"zip"`</span>
    Country       <span class="hljs-keyword">string</span>                <span class="hljs-string">`json:"country"`</span>
}
</code></pre>
<p>This is one of those times where generics in Go has been super helpful. Granted, I'd still like a way to type-switch on a constraint, but this is still much cleaner than having multiple types with nearly identical implementations and names.</p>
]]></content:encoded></item></channel></rss>