attach

package
v0.1.23 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package attach moves large payloads out of a message and puts them back.

An HL7 message carrying a scanned report or a PDF is ordinary in document workflows, and the payload is routinely hundreds of times larger than the message around it. Storing that inline means the message store grows at the rate of the documents rather than the traffic, a message queued for five destinations is copied five times, and the interface has to stream megabytes to show somebody a patient name.

So the payload is extracted, stored once by content, and replaced in the message with a token. Mirth calls these attachment handlers and it is a documented migration feature, which is why the token format here is designed to be recognisable rather than clever.

What this package does not decide

It does no storage and no I/O. Extraction produces the rewritten message and the payloads; a Store somewhere else decides where those go. That separation is what makes the extraction testable without a database, and it is also what lets the same logic run in the WASM playground where there is no database at all.

Index

Constants

View Source
const DefaultMinBytes = 4096

DefaultMinBytes is the threshold when a rule does not set one.

Four kilobytes: comfortably above any clinical text value, comfortably below any real document, and small enough that base64 of a one-page scan is caught.

View Source
const TokenPrefix = "${perfuse-attachment:"

TokenPrefix begins every token. Chosen to be visibly not clinical data: anybody reading a stored message and finding this needs to understand immediately that something was moved rather than lost.

View Source
const TokenSuffix = "}"

TokenSuffix closes a token.

Variables

This section is empty.

Functions

func Reassemble

func Reassemble(message []byte, lookup Lookup) ([]byte, error)

Reassemble puts payloads back into a message.

Every token must resolve. A partial reassembly is refused rather than delivered, because a message that is 90% correct is indistinguishable from a correct one at the receiving end.

Types

type Attachment

type Attachment struct {
	Digest Digest
	// Size is the payload's length in bytes, kept separately so the interface can report it without loading it.
	Size int
	// Path is where in the message it came from, as an HL7 path. Recorded so a re-inflation failure can say which
	// field is empty rather than only that something is missing.
	Path string
	// Payload is the bytes. Not held by anything long-lived; the Store takes them and this is discarded.
	Payload []byte
}

Attachment is one extracted payload.

type Digest

type Digest string

Digest identifies a payload by its content.

Content-addressed rather than sequential, which buys deduplication for free: the same document arriving twice, or fanned out to five destinations, is stored once. In document workflows resends are common enough that this is a large practical saving rather than a theoretical one.

func Compute

func Compute(payload []byte) Digest

Compute returns the digest of a payload.

func Tokens

func Tokens(message []byte) []Digest

Tokens lists the digests a message refers to, without needing the payloads.

Used to work out what a queued message depends on, so an attachment is not swept away while something still needs it. Sorted by first appearance and deduplicated.

func (Digest) Short

func (d Digest) Short() string

Short is the first twelve characters, for logs and interfaces. Never for lookup - a truncated digest can collide, and a collision here would attach the wrong document to a patient.

func (Digest) Token

func (d Digest) Token() string

Token renders the reference that replaces a payload in a message.

type ErrMissingAttachment

type ErrMissingAttachment struct {
	Digest Digest
	Path   string
}

ErrMissingAttachment means a token in a message refers to a payload the store does not have.

Its own error because the correct response is to fail the delivery rather than send a message containing a literal token. A receiver given "${perfuse-attachment:abc...}" where a report should be will file it as a report, and nobody finds out until somebody opens the record.

func (*ErrMissingAttachment) Error

func (e *ErrMissingAttachment) Error() string

type Lookup

type Lookup func(Digest) ([]byte, bool)

Lookup fetches a payload by digest. Returns false when it is not held.

type Result

type Result struct {
	// Message is the rewritten message, with tokens in place of payloads. This is what gets stored and what gets
	// queued, so it is small.
	Message []byte

	// Attachments are the payloads, in the order they appeared.
	Attachments []Attachment

	// Extracted is how many payloads were moved, and Saved how many bytes came out of the message. Both reported
	// because "3 attachments" and "3 attachments totalling 40 MB" are different facts and only the second explains
	// why the store stopped growing.
	Extracted int
	Saved     int
}

Result is what extraction produced.

func Extract

func Extract(message []byte, rules []Rule) (*Result, error)

Extract moves payloads out of a message according to the rules.

The message is rewritten by replacing values in place rather than by rebuilding it from parsed segments. That matters: rebuilding would normalise the message, and a stored message has to remain byte-for-byte evidence of what a sender sent apart from the payloads deliberately removed.

type Rule

type Rule struct {
	// Path is the field to extract, as an HL7 path such as OBX-5.5. Required.
	Path string `yaml:"path"`

	// MinBytes is the size below which a value is left alone. Defaults to 4096.
	//
	// A threshold rather than always extracting, because a short value in the same field as a long one is ordinary -
	// OBX-5 holds a numeric result on one message and an embedded PDF on the next - and moving a six-byte value out
	// of the message costs a row and a lookup to save nothing.
	MinBytes int `yaml:"min_bytes,omitempty"`

	// Repeats extracts every repeat of the field rather than only the first.
	//
	// Off by default: a message with one document is the common case, and quietly extracting from repeat five of a
	// field somebody thought was single would surprise them.
	Repeats bool `yaml:"repeats,omitempty"`
}

Rule says which field to extract and when.

func (*Rule) Validate

func (r *Rule) Validate() error

Validate fills defaults and refuses a rule that cannot work.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL