feature-flag 0.1.0

Server-side feature flag evaluation for async Rust: targeting rules, sticky percentage rollouts, hot reload, zero RNG.
Documentation
//! The thing flags are evaluated against.

use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use serde_json::Value;

/// The entity a flag is being evaluated for. `id` is the only required field
/// and is used as the sticky-bucketing key. Arbitrary string-keyed attributes
/// (`country`, `plan`, `email`, …) are supported by the [`crate::Predicate`] DSL.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Subject {
    /// Stable identifier for sticky bucketing. Usually a user/tenant/device id.
    pub id: String,
    /// Free-form string attributes for targeting (`{"country": "US", ...}`).
    #[serde(default)]
    pub attrs: HashMap<String, Value>,
}

impl Subject {
    /// Build a new subject with the given id.
    pub fn new<S: Into<String>>(id: S) -> Self {
        Self {
            id: id.into(),
            attrs: HashMap::new(),
        }
    }

    /// Builder helper: attach an attribute and return `self`.
    #[must_use]
    pub fn with_attr<K, V>(mut self, key: K, value: V) -> Self
    where
        K: Into<String>,
        V: Into<Value>,
    {
        self.attrs.insert(key.into(), value.into());
        self
    }

    /// Look up an attribute by name.
    pub fn attr(&self, key: &str) -> Option<&Value> {
        self.attrs.get(key)
    }
}