Journal 2022-06-18

Rust

Custom Serde Serialize

It could be difficult to make a custom implementation of the serde::Serialize trait. Like serializing a Vec into a tree or graph structure. This is because you need to consume the serde::Serializer to create a data type like structure or sequence. A trick I implemented to overcome this limitation is to implement the visitor patten to a custom enum that implements Serialize itself.

You start by implementing the Serialize for your structure:

use serde::Serialize;

impl<T> Serialize for Vec<T>
where
    T: Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut seq = serializer.serialize_seq(Some(self.len()))?;
        for e in self {
            ...
        }
        seq.end()
    }
}

And when you need an additional structure you create an enum and implement Serialize for it:


enum ElementSerialize<T> {
    Node{ elements: &[T], ...},
    Childs{ elements: &[T], ...}
}
...

impl<T> Serialize for ElementSerialize<T>
where
    T: Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Node{...} => todo!(), // Can now consume the serializer inside the match
            Childs{...} => todo!(),
        }
    }
}

And you can wrap the value in the Serialize of the structure:

...
        // Inside impl Serialize for Vec
        for e in self {
            seq.serialize_element(ElementSerialize::Node(...))?;
        }
...

Publish a package

To publish a packages to crates.io:

Publish with GitHub Actions

A simple workflow to publish a create when on a new release is the following:

name: Publish

on:
  release:
    types: [published]
  workflow_dispatch:

env:
  CARGO_TERM_COLOR: always

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/bin/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
      - name: Check
        run: cargo check --all-targets --all-features --verbose --locked
      - name: Build
        run: cargo build --verbose --locked
      - name: Run tests
        run: cargo test --all-targets --all-features --verbose --locked
      - name: Publish crate
        run: cargo publish --verbose --locked
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
<!-- Morning --> <!-- What do I want to do today? --> <!-- Evening --> <!-- What did I learn today? --> <!-- Things I learned --> <!-- Useful tools and libraries -->