classify

package
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: Jul 2, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package classify turns a request's User-Agent into a small, bounded device class ("desktop", "mobile", "tablet", "bot", …) for the `{device}` cache-key normalizer. It is the v2a classifier: a pure, deterministic, ordered ruleset (substring → class, first match wins, with a default) compiled once at config load and evaluated as a cheap per-request pre-pass — no I/O on the hot path.

The MECHANISM (UA → enum) lives here; the DATA (which substrings map to which class, and which classes to FOLD together) is configurable via the Cadishfile `device_detect { … }` block, with a sensible built-in ruleset (Default) when none is given.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultClassName

func DefaultClassName() string

DefaultClassName is the built-in fallback class ("desktop").

Types

type Classifier

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

Classifier is a compiled, immutable UA→class ruleset (plus optional folds). The zero value is not usable; build one with New or Default. A nil *Classifier classifies everything as "desktop", so callers need not nil-check.

func Default

func Default() *Classifier

Default is the built-in classifier, used when a site declares no `device_detect` block.

func FromSite

func FromSite(site *cadishfile.Site) (*Classifier, error)

FromSite builds the site's device classifier (for the {device} cache-key normalizer) from its optional `device_detect { … }` block. When the block is absent the built-in default ruleset is used, so `cache_key … {device}` works out of the box; the block overrides or extends it.

This is the SINGLE source of truth for compiling a `device_detect` block: both the runtime config layer (internal/config) and the pipeline (internal/pipeline, which projects the ruleset into the Edge IR so the worker self-classifies — D70) call it, so the Go server and the edge worker classify a User-Agent identically.

The block is an ordered ruleset (first match wins) plus optional folds:

device_detect {
    mobile  ua_contains Mobile Android iPhone
    tablet  ua_contains Android ua_excludes Mobile   # Android tablet
    tablet  ua_contains iPad Tablet
    bot     ua_contains bot crawler spider
    default desktop
}

Forms:

  • `CLASS ua_contains SUBSTR… [ua_excludes SUBSTR…]` — a rule: any contains substring (OR) selects CLASS, unless an excludes substring is also present.
  • `default CLASS` — the fallback class (default "desktop").
  • `fold FROM INTO` — remap class FROM onto INTO after matching (collapse the enum, e.g. `fold tablet desktop` / `fold bot desktop` for the cardinality-2 desktop/mobile case).

A block with ONLY folds/default (no rules) builds on the built-in ruleset, so `device_detect { fold tablet desktop; fold bot desktop }` reduces the default four classes to two.

func New

func New(rules []Rule, defaultCls string, folds ...Fold) *Classifier

New compiles rules into a Classifier. Substrings/excludes are lower-cased and blanks dropped; a rule with no substrings never matches (but its class still appears in Classes). An empty defaultCls falls back to "desktop". Folds with a blank side are ignored.

func (*Classifier) Classes

func (c *Classifier) Classes() []string

Classes returns the bounded, de-duplicated set of classes this classifier can actually emit — every rule's class plus the default, each run through the folds — in first-seen order. It lets `cadish check` confirm {device} is a low-cardinality key normalizer.

func (*Classifier) Classify

func (c *Classifier) Classify(ua string) string

Classify returns the device class for a User-Agent. An empty UA (or a nil classifier) yields the default class. The matched class is then run through any configured folds.

func (*Classifier) DefaultClass

func (c *Classifier) DefaultClass() string

DefaultClass returns the fallback class used when no rule matches.

func (*Classifier) Folds

func (c *Classifier) Folds() []Fold

Folds returns the configured class remaps (FROM -> INTO) for projection, sorted by FROM for deterministic output. Empty when the classifier has no folds.

func (*Classifier) IsDefault

func (c *Classifier) IsDefault() bool

IsDefault reports whether this classifier is the built-in default (the standard rules, the "desktop" default, and no folds) — so the edge projector can OMIT the ruleset from the IR when it would just be the default the worker already knows, keeping the zero-cost-when-unused invariant for a site that never customizes it.

func (*Classifier) Rules

func (c *Classifier) Rules() []Rule

Rules returns the compiled rule list (substrings/excludes already lower-cased) for projection into the Edge IR, so the worker can run the identical first-match scan. A nil classifier returns the built-in default rules.

type Fold

type Fold struct {
	From string
	Into string
}

Fold remaps a classified class onto another (e.g. tablet→desktop) so a site can collapse the enum to fewer buckets (the cardinality-2 desktop/mobile case in the v2 plan). Folds are applied AFTER rule matching.

type Rule

type Rule struct {
	Class      string
	Substrings []string
	Exclude    []string
}

Rule maps a device class to the User-Agent substrings that select it. A rule matches when ANY Substrings substring is present (OR) AND NO Exclude substring is present (the exclusion lets the built-in ruleset say "Android but not Mobile" ⇒ tablet). Rules are evaluated in order, first match wins. Matching is case-insensitive.

func DefaultRules

func DefaultRules() []Rule

DefaultRules is the built-in UA ruleset (exported so a `device_detect` block that only customizes folds/default can build on it). Order matters: bot is checked before tablet/mobile (a crawler UA may also contain "Mobile"); the tablet rules precede mobile (an iPad UA contains both "iPad" and "Mobile", and an Android TABLET sends "Android" WITHOUT "Mobile" while an Android phone includes "Mobile").

Jump to

Keyboard shortcuts

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