All checks were successful
Release / build-and-release (push) Successful in 25m59s
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>
70 lines
2.4 KiB
Rust
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));
|
|
}
|
|
}
|