Manifest Generation

The Go library provides a ManifestBuilder with a fluent API for generating block manifest data programmatically. This keeps your manifest and implementation in sync by deriving type information directly from Go types.

The ManifestBuilder🔗

Create a builder, chain declarations, and call Build() to produce a map[string]any suitable for YAML serialization:

import spade "github.com/spade-dev/spade"

manifest := spade.NewManifestBuilder().
    Description("Reprojects a raster to a target resolution").
    Build()

Declaring inputs🔗

Use the generic ManifestInput[T] function to declare an input. T must implement SpadeType:

b := spade.NewManifestBuilder().
    Description("Clips a raster to a boundary")

spade.ManifestInput[spade.RasterFile](b, "source")
spade.ManifestInput[spade.VectorFile](b, "boundary")
spade.ManifestInput[spade.NumberType](b, "buffer")

manifest := b.Build()

Because Go generics do not support method-level type parameters on a receiver, ManifestInput and ManifestOutput are package-level functions that take the builder as their first argument. They return the builder for chaining:

spade.ManifestInput[spade.RasterFile](b, "source")
// equivalent to: b = ManifestInput[RasterFile](b, "source")

Declaring outputs🔗

Use the generic ManifestOutput[T] function:

spade.ManifestOutput[spade.RasterFile](b, "raster")
spade.ManifestOutput[spade.JsonFile](b, "stats")

Complete example🔗

package main

import (
    "fmt"

    spade "github.com/spade-dev/spade"
    "gopkg.in/yaml.v3"
)

func main() {
    b := spade.NewManifestBuilder().
        Description("Reprojects a raster to a target resolution")

    spade.ManifestInput[spade.RasterFile](b, "source")
    spade.ManifestInput[spade.NumberType](b, "resolution")
    spade.ManifestOutput[spade.RasterFile](b, "raster")

    manifest := b.Build()

    data, _ := yaml.Marshal(manifest)
    fmt.Println(string(data))
}

Output:

description: Reprojects a raster to a target resolution
inputs:
  source:
    type: file
    format: GeoTIFF
  resolution:
    type: number
outputs:
  raster:
    type: file
    format: GeoTIFF

Type-to-manifest mapping🔗

Each Go type maps to a manifest entry through its ManifestEntry() method:

Go typeManifest typeformatitem_type
Filefile----
RasterFilefileGeoTIFF--
VectorFilefileGeoJSON--
TabularFilefileCSV--
JsonFilejson----
Directorydirectory----
FileCollectioncollection--file
RasterFileCollectioncollectionGeoTIFFfile
VectorFileCollectioncollectionGeoJSONfile
TabularFileCollectioncollectionCSVfile
StringTypestring----
NumberTypenumber----
BoolTypeboolean----

The ManifestInfo struct🔗

Each type's ManifestEntry() returns a ManifestInfo:

type ManifestInfo struct {
    TypeName string // "file", "directory", "collection", "json", etc.
    Format   string // "GeoTIFF", "GeoJSON", "CSV", or empty
    ItemType string // "file" for collections, or empty
}

The builder converts this to a nested map: { "type": "file", "format": "GeoTIFF" }. Empty fields are omitted.

The Build() output🔗

Build() returns a map[string]any with the following structure:

map[string]any{
    "description": "...",
    "inputs": map[string]any{
        "source": map[string]any{
            "type": "file",
            "format": "GeoTIFF",
        },
    },
    "outputs": map[string]any{
        "raster": map[string]any{
            "type": "file",
            "format": "GeoTIFF",
        },
    },
}

Serialize this with yaml.Marshal or json.Marshal as needed.

Mixed inputs example🔗

Combine file inputs with scalar parameter types:

b := spade.NewManifestBuilder().
    Description("Normalizes raster data")

spade.ManifestInput[spade.RasterFile](b, "raster")
spade.ManifestInput[spade.NumberType](b, "buffer")
spade.ManifestInput[spade.BoolType](b, "normalize")
spade.ManifestOutput[spade.RasterFile](b, "raster")

manifest := b.Build()

This generates inputs for a file, a number, and a boolean alongside a raster output.