mime

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

README

mime — go-freedesktop

ci Go Reference License Go Coverage

The freedesktop Shared MIME-info Database layer for a launcher — the piece a file manager or Spotlight-style finder needs to answer the one question "what type is this file?" From a file's name, its content, or both, it returns a canonical MIME type string. Pure Go, CGO-free, with base-directory resolution reused from adrg/xdg.

Scope — what this does, and what it reuses

It reads the on-disk Shared MIME-info database that lives under $XDG_DATA_HOME/mime and each $XDG_DATA_DIRS/mime — the same database update-mime-database produces and every compliant desktop tool consumes — and resolves types against it. Base-directory lookup reuses github.com/adrg/xdg (MIT), as the rest of the go-freedesktop family does.

On top of that it implements the spec's matching machinery:

  • Glob matching — filename → type with the spec's precedence: literal file-name matches beat *.ext suffix matches beat full fnmatch globs; the longest matching suffix wins; per-glob weight breaks ties; the case-sensitive flag is honoured.
  • Magic matching — content sniffing over the magic rule tree: absolute offset ranges, value masks, word-size byte-swapping, nested (AND/OR) match rules, and priority selecting the best type.
  • Combined lookupTypeByNameAndContent follows the spec's recommended glob-then-magic order and its tiebreak (a more specific sniffed subtype wins; otherwise the file name is authoritative unless magic priority ≥ 80).
  • Aliases & subclassesUnalias resolves alternative type strings; IsSubclassOf answers is-a queries transitively, including the implicit rules (every non-inode/* type is-a application/octet-stream, every text/* type is-a text/plain).
  • Fallbacks — an empty file is application/x-zerosize; otherwise unknown content is text/plain when it sniffs as text and application/octet-stream when it does not.

Two ingest paths build the database: the generated files (globs2/globs, the binary magic, aliases, subclasses) and, as a fallback when those are absent, the source packages/*.xml.

Install

go get github.com/go-freedesktop/mime

Quickstart

package main

import (
	"fmt"
	"os"

	"github.com/go-freedesktop/mime"
)

func main() {
	// Name only — the fast path a listing view uses.
	fmt.Println(mime.TypeByName("report.pdf")) // application/pdf

	// Name + a content sniff — what "Get Info" / "Open With" should use.
	f, _ := os.Open("/tmp/download")
	head := make([]byte, 256)
	n, _ := f.Read(head)
	fmt.Println(mime.TypeByNameAndContent("download", head[:n]))

	// Relationships.
	fmt.Println(mime.Unalias("application/x-gzip"))               // application/gzip
	fmt.Println(mime.IsSubclassOf("image/svg+xml", "text/plain")) // true
}

The package-level helpers use a process-wide database loaded once from the system directories. For tests, sandboxes, or a bundled database, build your own with Load, LoadDir, or New + AddXML and call the methods on it.

Public API

Symbol Purpose
Load() (*Database, error) read & merge every system …/mime database (via adrg/xdg)
LoadDir(dir) (*Database, error) read one mime directory (generated files, else packages/*.xml)
New() *Database empty database to populate manually
(*Database).AddXML(r) error merge one Shared MIME-info XML document
(*Database).AddPackagesDir(dir) error merge every *.xml in a packages/ directory
(*Database).TypeByName(name) string best type from the file name, or ""
(*Database).TypesByName(name) []string all top-tier glob matches, weight-ordered
(*Database).TypeByContent(data) string best type from a content sniff (with fallbacks)
(*Database).TypeByNameAndContent(name, data) string combined lookup with the spec tiebreak
(*Database).Unalias(t) string / Aliases(t) []string alias resolution both ways
(*Database).IsSubclassOf(t, parent) bool / Parents(t) []string is-a queries
Default() *Database the process-wide system database
TypeByName / TypeByContent / TypeByNameAndContent / Unalias / IsSubclassOf package-level helpers over Default()
OctetStream, PlainText, ZeroSize fallback type constants
ErrBadMagic sentinel for a malformed binary magic file
The combined tiebreak

TypeByNameAndContent(name, data):

  1. No data (nil) → the glob result, else application/octet-stream.
  2. Empty data ([]byte{}) → application/x-zerosize.
  3. No glob match → the content result (magic, else text/binary fallback).
  4. No magic match → the glob result.
  5. Both match → the glob type if they agree; the more specific of the two when one is-a the other; otherwise the file name wins unless magic priority is ≥ 80.

wasmdesk / Spotlight integration

This library is the "what type is this?" resolver in the wasmdesk file-manager and Spotlight-style flow. Paired with its go-freedesktop siblings it drives the whole Open With experience:

  • mime (this repo) resolves a selected file → a canonical type.
  • desktopentry + a mimeapps.list handler map that type → the application entries that declare it in their MimeType= list, yielding the ranked Open With menu and the default handler.
  • icontheme turns the type (and the chosen app) into an icon.
  • go-thumbnail uses the sniffed type to decide how to render a preview.

TypeByNameAndContent is the call the finder makes on selection; the returned canonical string is the key every other stage keys off.

Tests & coverage

CGO_ENABLED=0 go test ./...100% statement coverage, including every error branch, driven by fixtures under testdata/ (a generated-file database and a packages/*.xml-only database, plus a hand-built binary magic). CI additionally runs the suite on the six supported 64-bit targets (amd64/arm64 natively, riscv64/loong64/ppc64le/s390x under qemu-user). The -race coverage gate is the only step that enables cgo; the arch matrix proves the library itself is CGO-free.

License

BSD-3-Clause. Copyright (c) the go-freedesktop/mime authors.


Note: the go-freedesktop org landing page and MkDocs site are deferred to the Wave-2 documentation sweep; this repo ships the README and .github workflow for now.

Documentation

Overview

Package mime implements the freedesktop.org Shared MIME-info Database specification in pure Go (CGO-free): the piece a file manager or Spotlight-style finder needs to answer "what type is this file?".

It resolves a canonical MIME type from a file's name (glob matching), from its content (magic sniffing), or from both together following the spec's glob-versus-magic tiebreak. It also resolves aliases and answers subclass ("is-a") queries.

The database is read from the on-disk Shared MIME-info directories ($XDG_DATA_HOME/mime and $XDG_DATA_DIRS/mime), either from the generated files an update-mime-database run produces (globs2, magic, aliases, subclasses) or directly from the source packages/*.xml. Base-directory resolution reuses github.com/adrg/xdg, matching the rest of the go-freedesktop family.

Spec: https://specifications.freedesktop.org/shared-mime-info-spec/latest/

Index

Examples

Constants

View Source
const (
	// OctetStream is the fallback for content that does not look like text.
	OctetStream = "application/octet-stream"
	// PlainText is the fallback for content that looks like text.
	PlainText = "text/plain"
	// ZeroSize is the type reported for an empty (zero-byte) file.
	ZeroSize = "application/x-zerosize"
)

Well-known canonical types the spec singles out for fallbacks.

Variables

View Source
var ErrBadMagic = errors.New("mime: malformed magic database")

ErrBadMagic reports a malformed binary magic file.

Functions

func IsSubclassOf

func IsSubclassOf(t, parent string) bool

IsSubclassOf is IsSubclassOf on the Default database.

func TypeByContent

func TypeByContent(data []byte) string

TypeByContent is TypeByContent on the Default database.

func TypeByName

func TypeByName(name string) string

TypeByName is TypeByName on the Default database.

func TypeByNameAndContent

func TypeByNameAndContent(name string, data []byte) string

TypeByNameAndContent is TypeByNameAndContent on the Default database.

func Unalias

func Unalias(t string) string

Unalias is Unalias on the Default database.

Types

type Database

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

Database is a parsed Shared MIME-info database. The zero value is not ready for use; build one with New, Load, or LoadDir. A Database is safe for concurrent reads once fully built.

Example

ExampleDatabase shows a file manager resolving a file's type from its name, its content, and both together, using a small database built from a Shared MIME-info XML package.

package main

import (
	"fmt"
	"strings"

	"github.com/go-freedesktop/mime"
)

func main() {
	db := mime.New()
	_ = db.AddXML(strings.NewReader(`
      <mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
        <mime-type type="text/plain"><glob pattern="*.txt"/></mime-type>
        <mime-type type="application/pdf">
          <glob pattern="*.pdf"/>
          <magic priority="50"><match type="string" value="%PDF-" offset="0"/></magic>
        </mime-type>
      </mime-info>`))

	fmt.Println(db.TypeByName("notes.txt"))
	fmt.Println(db.TypeByContent([]byte("%PDF-1.7")))
	// A file misnamed .txt but whose content sniffs as PDF: here the weak
	// (priority < 80) magic does not override the file name.
	fmt.Println(db.TypeByNameAndContent("invoice.txt", []byte("%PDF-1.7")))
}
Output:
text/plain
application/pdf
text/plain

func Default

func Default() *Database

Default returns the process-wide database, loaded once from the system Shared MIME-info directories with Load. If loading fails it returns an empty database so lookups degrade to the spec fallbacks rather than panicking.

func Load

func Load() (*Database, error)

Load reads and merges every Shared MIME-info database found on the system: $XDG_DATA_HOME/mime first, then each $XDG_DATA_DIRS/mime, resolved through github.com/adrg/xdg. Missing directories are skipped; a directory that exists but contains malformed data yields an error.

func LoadDir

func LoadDir(dir string) (*Database, error)

LoadDir reads a single Shared MIME-info directory (the "mime" directory that holds globs2, magic, aliases, subclasses and packages/). It prefers the generated files; if none are present it falls back to parsing packages/*.xml.

func New

func New() *Database

New returns an empty Database ready to be populated with AddXML, AddPackagesDir, or the loader helpers.

func (*Database) AddPackagesDir

func (db *Database) AddPackagesDir(dir string) error

AddPackagesDir parses every *.xml file in dir (a Shared MIME-info packages/ directory) into the database, in sorted file-name order. A missing directory is not an error; a malformed file is.

func (*Database) AddXML

func (db *Database) AddXML(r io.Reader) error

AddXML parses one Shared MIME-info XML document (a <mime-info> element) from r and merges its globs, magic rules, aliases and subclass relations.

func (*Database) Aliases

func (db *Database) Aliases(t string) []string

Aliases returns the alternative type strings declared as aliases of the canonical type t, in the order they were registered.

func (*Database) IsSubclassOf

func (db *Database) IsSubclassOf(t, parent string) bool

IsSubclassOf reports whether t is the same as, or a subclass ("is-a") of, parent. It follows declared sub-class-of relations transitively and honours the spec's implicit rules: every non-inode type is a subclass of application/octet-stream, and every text/* type is a subclass of text/plain.

func (*Database) Parents

func (db *Database) Parents(t string) []string

Parents returns the directly declared parent types (sub-class-of) of t.

func (*Database) TypeByContent

func (db *Database) TypeByContent(data []byte) string

TypeByContent sniffs data and returns the best matching canonical type. An empty slice yields ZeroSize; if no magic rule matches, the result is PlainText when the data looks like text and OctetStream otherwise.

func (*Database) TypeByName

func (db *Database) TypeByName(name string) string

TypeByName returns the single best canonical type for a file name using glob matching, or "" when no glob matches. Only the file's base name is considered.

func (*Database) TypeByNameAndContent

func (db *Database) TypeByNameAndContent(name string, data []byte) string

TypeByNameAndContent combines glob and magic matching following the spec's recommended checking order and its glob-versus-magic tiebreak, and always returns a concrete type (falling back to ZeroSize / PlainText / OctetStream). Pass data==nil to indicate the content is unavailable; an empty non-nil slice means a genuinely zero-byte file.

func (*Database) TypesByName

func (db *Database) TypesByName(name string) []string

TypesByName returns every canonical type in the highest-precedence glob tier that matches name, ordered by descending weight. It is empty when nothing matches. Precedence follows the spec: literal file-name matches beat suffix (*.ext) matches beat full fnmatch globs, and among suffix matches the longest matching suffix wins.

func (*Database) Unalias

func (db *Database) Unalias(t string) string

Unalias resolves t through the alias table to its canonical type. If t is not an alias it is returned unchanged.

Jump to

Keyboard shortcuts

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