Help us shape the future of Go! Take the 2026 Go Developer Survey

codemeta

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: MIT Imports: 13 Imported by: 0

README

codemeta

A Go library for parsing codemeta.json files and validating software metadata, with no third-party dependencies. Reads bytes, readers, or files and preserves source positions, unknown fields, and numeric spelling. The same source builds with Go and TinyGo.

Installation

go get github.com/git-pkgs/codemeta

Usage

package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/git-pkgs/codemeta"
)

func main() {
	doc, err := codemeta.ReadFile("codemeta.json", codemeta.ParseOptions{})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Name: %s\n", doc.Name())
	fmt.Printf("CodeMeta context: %s\n", doc.Version())
	fmt.Printf("Software version: %s\n", strings.Join(doc.Strings("version"), ", "))
	for _, author := range doc.Author() {
		fmt.Println(author.Name(), author.GivenName(), author.FamilyName())
	}
	for _, issue := range doc.Validate() {
		fmt.Println(issue.Line, issue.Column, issue.Path, issue.Code, issue.Message)
	}
}

Use Parse(data) for bytes from a Git blob or another source, ParseWithOptions(data, opts) to set limits, or Read(reader, opts) for an io.Reader. Read leaves the reader open. Repository discovery, paths, blob identity, and concurrency limits remain with the caller.

Documents are immutable and can be read concurrently. Parsing copies retained text, so callers may reuse input buffers after a call returns. Collection accessors return copies of their slices.

Metadata

Name and Description return the first nonempty projected string. CodeRepository, SoftwareVersion, License, Keywords, ProgrammingLanguages, DatePublished, DateModified, DevelopmentStatus, and Identifier return values that retain the written form. Version identifies the pinned CodeMeta context. It does not return the software version.

Use Get to access any field, including unknown fields and invalid metadata:

fmt.Println(doc.Get("issueTracker").Text())
for _, keyword := range doc.Keywords().Values() {
	fmt.Println(keyword.Text())
}

Document.Strings and Agent.Strings project a term into display text, unwrapping @list, @set, and @value and reading @id or name from objects. They resolve fields in their local context, return scalar spellings in source order, and skip empty strings and nulls:

fmt.Println(doc.Strings("keywords"))
fmt.Println(doc.Strings("license"))
for _, author := range doc.Author() {
	fmt.Println(author.Strings("givenName"), author.Strings("roleName"))
}

Values expose Kind, Text, Items, Fields, and Position. Items returns only array elements; Values returns array elements or a single scalar or object. A missing field has kind Missing and no values, while explicit JSON Null remains a value. Numbers retain their spelling and precision. Dates retain their text.

Author, Contributor, Maintainer, CopyrightHolder, and Funder return ordered agent views, accepting single values, arrays, and list or set containers within arrays. Null entries are skipped. Agent.Kind distinguishes people, organisations, strings, ID references, roles, and conflicting fields. Value preserves each original value; Get accesses its fields. Name, GivenName, and FamilyName return the first nonempty projected string; Name also returns a text agent's value. RoleName and Agents expose a role and its nested agents without discarding the wrapper.

Document.Get and Agent.Get resolve known aliases and prefixed or full term IRIs when the declared context is usable. An exact written key takes precedence. Value.Get always uses the exact key. Fields retains written keys and their positions, including both keys when aliases conflict. Both http://schema.org/ and https://schema.org/ resolve to the same schema term. CodeMeta namespace IRIs remain specific to each pinned context.

Errors

The library returns errors without logging or terminating the process. Use errors.Is with ErrSyntax, ErrType, ErrUnsupported, ErrLimit, ErrOptions, or ErrIO to distinguish failures. errors.As exposes a *codemeta.Error with a diagnostic. I/O errors retain their original cause, including fs.ErrNotExist.

Validate reports independent diagnostics in source order, with a field-path tie-breaker. Positions use one-based lines and Unicode character columns. Diagnostic codes and paths are intended for programmatic use; messages explain the problem.

Compatibility

Parsing and validation are separate. Parsing requires valid UTF-8 JSON with an object root. Duplicate keys, comments, trailing commas, non-finite numbers, and lone surrogate escapes are errors. A UTF-8 BOM produces ErrUnsupported; escaped NUL is preserved, while a raw control character is a syntax error. A successful parse alone does not establish CodeMeta validity.

Validation uses checked-in CodeMeta 2.0, 3.0, and master contexts, plus type expectations generated from upstream properties_description.csv. Source revisions and checksums pin those inputs. The master URL always refers to the checked-in snapshot; it never changes with the date or a network response. The DOI URL for 2.0, published w3id URLs, raw context URLs, HTTP spellings, and trailing slashes are recognised.

Missing contexts produce missing_context; unknown contexts produce unsupported_version. Inline contexts equivalent to a pinned context are recognised. Other inline definitions produce context_modified, and combinations of different CodeMeta versions produce context_conflict. Unsupported or modified contexts disable term and range checks in their scope, while preserving all input. Nested contexts apply to their own objects. Version can still identify a recognised context in an array that also contains unsupported entries; inspect diagnostics before using that as a validity signal.

Context arrays are inspected in order. A partial inline definition such as {"name":"schema:name"} is accepted after a recognised context if it leaves the pinned definition unchanged. Placing it before that context produces context_modified; later entries do not retroactively resolve it. The detected Version is retained, but resolved accessors and term checks are disabled for that scope.

Terms found only in another pinned context produce term_version, while undefined terms produce unknown_term. Validation checks JSON value shapes, URL syntax, and calendar dates, without requiring any metadata properties. Date checks accept year, year-month, or calendar-date forms; date-time ranges also accept RFC 3339. URL checks do not resolve repositories, register ORCIDs, or check SPDX identifiers against a current list.

Validation is limited to pinned-context term resolution and the checks described above. JSON-LD expansion and compaction are outside its scope, so a clean result does not establish JSON-LD conformance. Writing, crosswalks, citation formatting, repository scanning, and network resolution are also outside this library's scope.

Limits

Default limits are 1 MiB of input, depth 64, 100,000 nodes including object keys, 4 MiB of decoded string content and numeric text, and 100 validation diagnostics. The root is depth one. ParseOptions allows larger limits; zero selects the defaults and negative limits are errors. MaxBytes must be less than math.MaxInt64, leaving room to read one extra byte for limit detection; math.MaxInt64 returns ErrOptions. When diagnostics are omitted, the last entry is diagnostic_limit, within the requested cap. A blocked reader requires a deadline supplied by its caller.

Testing

go test ./...
go test -race ./...
go test -run '^$' -fuzz=FuzzParse -fuzztime=30s
go test -run '^$' -fuzz=FuzzRead -fuzztime=30s
CODEMETA_CORPUS=all go test -run TestCorpus ./...
go test -run '^$' -bench=. -benchmem

Tests use checked-in fixtures and run offline. The compact corpus runs by default; CODEMETA_CORPUS=all includes the larger compatibility sample and its benchmarks. Manifests record original bytes' hashes, source occurrences, parse outcomes, context versions, and diagnostics. Generator comparisons record tested differences and their reasons.

go generate ./... regenerates validation tables from the pinned contexts and property descriptions. Generation rejects unsupported context constructs and unknown type expressions. CI checks generated output, the dependency graph, race tests, fuzz targets, and a TinyGo consumer build without build tags or fallback implementations.

Run the example with go run ./examples/read path/to/codemeta.json, or supply - to read a Git blob through standard input. The history integration test adds, edits, moves, deletes, and reintroduces metadata in a temporary Git repository, and checks continuation after a malformed blob.

License

MIT. Imported fixtures and context documents retain their upstream attribution and licenses.

Documentation

Overview

Package codemeta reads and validates CodeMeta metadata offline.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrSyntax      = errors.New("invalid JSON")
	ErrUnsupported = errors.New("unsupported syntax")
	ErrType        = errors.New("unrepresentable document")
	ErrLimit       = errors.New("resource limit exceeded")
	ErrOptions     = errors.New("invalid parse options")
	ErrIO          = errors.New("input/output failure")
)

Functions

This section is empty.

Types

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

Agent is an immutable view. Value retains the original object or scalar.

func (Agent) Agents

func (a Agent) Agents() []Agent

Agents returns the agents nested under this role's original relationship.

func (Agent) FamilyName

func (a Agent) FamilyName() string

func (Agent) Get

func (a Agent) Get(term string) Value

func (Agent) GivenName

func (a Agent) GivenName() string

func (Agent) Identifier

func (a Agent) Identifier() Value

func (Agent) Kind

func (a Agent) Kind() AgentKind

func (Agent) Name

func (a Agent) Name() string

func (Agent) RoleName

func (a Agent) RoleName() Value

func (Agent) Strings added in v0.3.0

func (a Agent) Strings(term string) []string

Strings projects an agent field using the agent's local context.

func (Agent) Value

func (a Agent) Value() Value

type AgentKind

type AgentKind uint8

AgentKind describes the retained form, including conflicting person fields.

const (
	AgentUnknown AgentKind = iota
	AgentText
	AgentReference
	AgentPerson
	AgentOrganization
	AgentRole
	AgentConflict
)

type Diagnostic

type Diagnostic struct {
	Code    string `json:"code"`
	Path    string `json:"path,omitempty"`
	Message string `json:"message"`
	Position
}

type Document

type Document struct {
	// contains filtered or unexported fields
}

func Parse

func Parse(data []byte) (*Document, error)

func ParseWithOptions

func ParseWithOptions(data []byte, opts ParseOptions) (*Document, error)

func Read

func Read(r io.Reader, opts ParseOptions) (*Document, error)

Read leaves r open and consumes at most MaxBytes+1 bytes.

func ReadFile

func ReadFile(path string, opts ParseOptions) (*Document, error)

ReadFile owns and closes the opened file.

func (*Document) Author

func (d *Document) Author() []Agent

func (*Document) CodeRepository

func (d *Document) CodeRepository() Value

func (*Document) Contributor

func (d *Document) Contributor() []Agent

func (*Document) CopyrightHolder

func (d *Document) CopyrightHolder() []Agent

func (*Document) DateModified

func (d *Document) DateModified() Value

func (*Document) DatePublished

func (d *Document) DatePublished() Value

func (*Document) Description

func (d *Document) Description() string

func (*Document) DevelopmentStatus

func (d *Document) DevelopmentStatus() Value

func (*Document) Fields

func (d *Document) Fields() []Field

func (*Document) Funder

func (d *Document) Funder() []Agent

func (*Document) Get

func (d *Document) Get(name string) Value

func (*Document) Identifier

func (d *Document) Identifier() Value

func (*Document) Keywords

func (d *Document) Keywords() Value

func (*Document) License

func (d *Document) License() Value

func (*Document) Maintainer

func (d *Document) Maintainer() []Agent

func (*Document) Name

func (d *Document) Name() string

func (*Document) ProgrammingLanguages

func (d *Document) ProgrammingLanguages() Value

func (*Document) SoftwareVersion

func (d *Document) SoftwareVersion() Value

func (*Document) Strings added in v0.2.0

func (d *Document) Strings(term string) []string

Strings projects a term's scalar spelling, object identifiers, or object names in source order. It unwraps JSON-LD lists, sets, and value objects and resolves keys using each object's context. Empty strings and nulls are skipped. The original values remain available through Get.

func (*Document) Validate

func (d *Document) Validate() []Diagnostic

Validate returns diagnostics in source order, capped including a truncation entry.

func (*Document) Version

func (d *Document) Version() Version

type Error

type Error struct {
	Diagnostic
	Category error
	Cause    error
}

Error retains a machine-readable category and the original I/O error, if any.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

func (*Error) Unwrap

func (e *Error) Unwrap() error

type Field

type Field struct {
	Name     string
	Value    Value
	Position Position
}

type Kind

type Kind uint8

Kind distinguishes JSON values. A zero Value is Missing.

const (
	Missing Kind = iota
	Null
	String
	Number
	Boolean
	Array
	Object
)

type ParseOptions

type ParseOptions struct {
	MaxBytes       int64
	MaxDepth       int
	MaxNodes       int
	MaxStringBytes int
	MaxDiagnostics int
}

ParseOptions bounds untrusted input. Zero selects the documented defaults. MaxBytes must be less than math.MaxInt64 to allow one extra byte for limit detection.

type Position

type Position struct {
	Line   int `json:"line,omitempty"`
	Column int `json:"column,omitempty"`
}

Position identifies a one-based Unicode character line and column. Zero means no source position is available.

type Value

type Value struct {
	// contains filtered or unexported fields
}

Value owns its text and preserves source order and numeric spelling.

func (Value) Fields

func (v Value) Fields() []Field

func (Value) Get

func (v Value) Get(name string) Value

Get looks up the exact written key, without context resolution.

func (Value) Items

func (v Value) Items() []Value

func (Value) Kind

func (v Value) Kind() Kind

func (Value) Position

func (v Value) Position() Position

func (Value) Text

func (v Value) Text() string

func (Value) Values

func (v Value) Values() []Value

Values returns array items, a single scalar or object, or nil for Missing. An explicit null remains a single Null value.

type Version

type Version string

Version identifies a pinned context, independently of software metadata.

const (
	UnknownVersion Version = ""
	Version2       Version = "2.0"
	Version3       Version = "3.0"
	VersionMaster  Version = "master"
)

Directories

Path Synopsis
examples
read command
Command read demonstrates reading metadata from a file or standard input.
Command read demonstrates reading metadata from a file or standard input.
internal
generate command
Command generate compiles the pinned contexts and property descriptions.
Command generate compiles the pinned contexts and property descriptions.

Jump to

Keyboard shortcuts

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