Simpl-Resultat/src-tauri/src/commands/entitlements.rs
le king fu f14ac3c6f8
All checks were successful
Release / build-and-release (push) Successful in 25m59s
fix: temporarily open auto-update to Free edition
The auto-update gate added in #48 requires the Base edition, but the
license server (#49) needed to grant Base does not exist yet. This
chicken-and-egg left the only current user — myself — unable to
receive the critical v0.7.1 OAuth callback fix via auto-update.

Add EDITION_FREE to the auto-update feature tiers as a temporary
measure. The gate will be restored to [BASE, PREMIUM] once paid
activation works end-to-end via the Phase 2 license server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 14:11:16 -04:00

70 lines
2.4 KiB
Rust

// Centralized feature → tier mapping for license entitlements.
//
// This module is the single source of truth for which features are gated by which tier.
// To change what is gated where, modify FEATURE_TIERS only — never sprinkle edition checks
// throughout the codebase.
/// Editions, ordered from least to most privileged.
pub const EDITION_FREE: &str = "free";
pub const EDITION_BASE: &str = "base";
pub const EDITION_PREMIUM: &str = "premium";
/// Maps feature name → list of editions allowed to use it.
/// A feature absent from this list is denied for all editions.
const FEATURE_TIERS: &[(&str, &[&str])] = &[
// auto-update is temporarily open to FREE until the license server (issue #49)
// is live. Re-gate to [BASE, PREMIUM] once paid activation works end-to-end.
("auto-update", &[EDITION_FREE, EDITION_BASE, EDITION_PREMIUM]),
("web-sync", &[EDITION_PREMIUM]),
("cloud-backup", &[EDITION_PREMIUM]),
("advanced-reports", &[EDITION_PREMIUM]),
];
/// Pure check: does `edition` grant access to `feature`?
pub fn is_feature_allowed(feature: &str, edition: &str) -> bool {
FEATURE_TIERS
.iter()
.find(|(name, _)| *name == feature)
.map(|(_, tiers)| tiers.contains(&edition))
.unwrap_or(false)
}
#[tauri::command]
pub fn check_entitlement(app: tauri::AppHandle, feature: String) -> Result<bool, String> {
let edition = crate::commands::license_commands::current_edition(&app);
Ok(is_feature_allowed(&feature, &edition))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn free_allows_auto_update_temporarily() {
// Temporary: auto-update is open to FREE until the license server is live.
assert!(is_feature_allowed("auto-update", EDITION_FREE));
}
#[test]
fn base_unlocks_auto_update() {
assert!(is_feature_allowed("auto-update", EDITION_BASE));
}
#[test]
fn premium_unlocks_everything() {
assert!(is_feature_allowed("auto-update", EDITION_PREMIUM));
assert!(is_feature_allowed("web-sync", EDITION_PREMIUM));
assert!(is_feature_allowed("cloud-backup", EDITION_PREMIUM));
}
#[test]
fn base_does_not_unlock_premium_features() {
assert!(!is_feature_allowed("web-sync", EDITION_BASE));
assert!(!is_feature_allowed("cloud-backup", EDITION_BASE));
}
#[test]
fn unknown_feature_denied() {
assert!(!is_feature_allowed("nonexistent", EDITION_PREMIUM));
}
}