MobDev.Plugin.Manifest (mob_dev v0.7.22)

Copy Markdown View Source

Reads, validates, and classifies a plugin's priv/mob_plugin.exs manifest.

The manifest is data, not code (see MOB_PLUGINS.md): a plain Elixir map describing what a plugin contributes. This module is the single place that turns that map into a validated, classified description — tier, hot-push status, activation — that mix mob.plugins reports and the compile-time merge will later consume.

A tier-0 plugin has no manifest at all; load/1 returns {:ok, nil} for that case, and tier(nil) is 0.

Summary

Functions

Whether the plugin's contributions can be hot-pushed without a native rebuild.

Loads the manifest for a plugin checked out at plugin_dir.

Returns true when plugin_dir has a priv/mob_plugin.exs, false otherwise. Distinguishes tier-0 plugins (no manifest, no signature needed) from tier-1+ plugins (manifest present, signature required) without evaluating the manifest itself. See Verify.load_verified/1.

Whether the native build compiles this nifs: entry for platform (:android, :ios, or :all) — the one platform rule every NIF consumer shares: the build args, the driver_tab, and the MOB-281 native-build record.

The language of a nifs: entry. Defaults to :c so existing (haptic) plugins are unaffected; lang: :zig etc. opt into other compile paths.

Classifies the plugin tier (0–4) from which capability sections are present.

Whether an ios.plist_keys key is a privacy usage description (NSBluetoothAlwaysUsageDescription, NFCReaderUsageDescription, …): the string iOS shows in a permission prompt to explain why the app asks. Several plugins may each need the same permission for their own reason, so these keys combine across plugins instead of colliding (MobDev.Plugin.Merge.plist_keys/1, MobDev.Plugin.Validator.cross_validate/2).

Validates a manifest map against the spec's required top-level fields.

Functions

hot_pushable(m)

@spec hot_pushable(map() | nil) :: true | false | :partial

Whether the plugin's contributions can be hot-pushed without a native rebuild.

Computed from populated sections, not from tier: true for pure-Elixir plugins, false for native-only ones (NIFs / components), and :partial when a plugin mixes native code with hot-pushable Elixir (screens, lifecycle).

load(plugin_dir)

@spec load(Path.t()) :: {:ok, map() | nil} | {:error, String.t()}

Loads the manifest for a plugin checked out at plugin_dir.

Returns {:ok, nil} when there is no priv/mob_plugin.exs (a tier-0 plugin), {:ok, map} when one is present and evaluates to a map, or {:error, reason} when the file is unreadable or doesn't yield a map. Does not validate field contents — call validate/1 for that.

manifest_present?(plugin_dir)

@spec manifest_present?(Path.t()) :: boolean()

Returns true when plugin_dir has a priv/mob_plugin.exs, false otherwise. Distinguishes tier-0 plugins (no manifest, no signature needed) from tier-1+ plugins (manifest present, signature required) without evaluating the manifest itself. See Verify.load_verified/1.

nif_for_platform?(nif, platform)

@spec nif_for_platform?(map(), :android | :ios | :all) :: boolean()

Whether the native build compiles this nifs: entry for platform (:android, :ios, or :all) — the one platform rule every NIF consumer shares: the build args, the driver_tab, and the MOB-281 native-build record.

An entry with no :platform is compiled on every platform; one tagged :ios/:android only on that platform. lang: :objc is implicitly Apple-only: Objective-C has no Android runtime, so an objc NIF authored without an explicit platform: :ios must still be excluded from the Android build args + driver_tab (otherwise zig tries to compile a .m source Android cannot build).

nif_lang(nif)

@spec nif_lang(map()) :: atom()

The language of a nifs: entry. Defaults to :c so existing (haptic) plugins are unaffected; lang: :zig etc. opt into other compile paths.

tier(m)

@spec tier(map() | nil) :: 0..4

Classifies the plugin tier (0–4) from which capability sections are present.

Highest matching section wins. nil (no manifest) is tier 0. A manifest with required fields but no capability sections is the tier-1 floor (the "minimum viable manifest").

usage_description_key?(key)

@spec usage_description_key?(atom() | String.t()) :: boolean()

Whether an ios.plist_keys key is a privacy usage description (NSBluetoothAlwaysUsageDescription, NFCReaderUsageDescription, …): the string iOS shows in a permission prompt to explain why the app asks. Several plugins may each need the same permission for their own reason, so these keys combine across plugins instead of colliding (MobDev.Plugin.Merge.plist_keys/1, MobDev.Plugin.Validator.cross_validate/2).

validate(manifest)

@spec validate(map() | nil) :: {:ok, map() | nil} | {:error, [String.t()]}

Validates a manifest map against the spec's required top-level fields.

Returns {:ok, manifest} or {:error, reasons} with a list of every problem found (validation never stops at the first error). nil (no manifest) is valid — a tier-0 plugin has nothing to validate.