reasoninglayer 0.2.1

Rust client SDK for the Reasoning Layer API
Documentation
//! OSF (Order-Sorted Feature) clause DTOs used by the `/reasoning/entailment` and
//! `/reasoning/disentailment` endpoints.
//!
//! Distinct from the homoiconic [`TermInputDto`](super::homoiconic::TermInputDto) used by the
//! `/inference/*` endpoints. The OSF surface is constraint-based: a clause is a list of
//! [`OsfConstraintDto`] instances expressing variable sorts, feature bindings, equalities, and
//! nested entailments.
//!
//! Wire format mirrors the backend's `crate::dto::inference::OsfClauseDto` family.

use serde::{Deserialize, Serialize};

/// Tagged value used inside an OSF feature constraint. Matches the backend's
/// `crate::dto::inference::FeatureValueDto` exactly (adjacent `type`/`value` tagging).
///
/// Note: this is a *different* type from the SDK's
/// [`ValueDto`](crate::types::values::ValueDto) used by term CRUD, even though both are tagged.
/// The OSF surface only exposes the primitive value cases (no fuzzy or set values).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum OsfFeatureValueDto {
    /// String value.
    String(String),
    /// Signed 64-bit integer.
    Integer(i64),
    /// 64-bit float.
    Real(f64),
    /// Boolean.
    Boolean(bool),
    /// Reference to another term by UUID.
    Reference(String),
    /// Nested list of values.
    List(Vec<OsfFeatureValueDto>),
}

/// Target of an OSF feature constraint — either a variable reference (bare string like `"?X"`)
/// or a typed value object. Untagged: variables come over the wire as bare JSON strings,
/// values as `{"type": ..., "value": ...}` objects.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OsfFeatureTargetDto {
    /// A typed value (object form).
    TypedValue(OsfFeatureValueDto),
    /// A variable reference (e.g., `"?X"`).
    Variable(String),
}

impl OsfFeatureTargetDto {
    /// Variable reference helper.
    pub fn var(name: impl Into<String>) -> Self {
        Self::Variable(name.into())
    }

    /// Typed string value helper.
    pub fn string(s: impl Into<String>) -> Self {
        Self::TypedValue(OsfFeatureValueDto::String(s.into()))
    }

    /// Typed integer value helper.
    pub fn integer(i: i64) -> Self {
        Self::TypedValue(OsfFeatureValueDto::Integer(i))
    }

    /// Typed real value helper.
    pub fn real(r: f64) -> Self {
        Self::TypedValue(OsfFeatureValueDto::Real(r))
    }

    /// Typed boolean value helper.
    pub fn boolean(b: bool) -> Self {
        Self::TypedValue(OsfFeatureValueDto::Boolean(b))
    }

    /// Term reference helper.
    pub fn term_ref(uuid: impl Into<String>) -> Self {
        Self::TypedValue(OsfFeatureValueDto::Reference(uuid.into()))
    }
}

/// One constraint inside an OSF clause. Tagged by `type`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum OsfConstraintDto {
    /// `var : sort_id` — assert a variable's sort.
    Sort {
        var: String,
        /// Sort UUID.
        sort_id: String,
    },
    /// `var1 = var2` — assert two variables are equal.
    Equality { var1: String, var2: String },
    /// `var.feature = value-or-variable` — assert a feature value/binding.
    Feature {
        var: String,
        feature: String,
        value: OsfFeatureTargetDto,
    },
    /// `antecedent ⊢ consequent` — nested entailment with optional confidence.
    Entailment {
        antecedent: Box<OsfClauseDto>,
        consequent: Box<OsfClauseDto>,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        confidence: Option<f64>,
    },
}

/// An OSF clause: a conjunction of [`OsfConstraintDto`]s.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct OsfClauseDto {
    /// Constraints in the clause.
    #[serde(default)]
    pub constraints: Vec<OsfConstraintDto>,
}

impl OsfClauseDto {
    /// Build a clause from an iterator of constraints.
    pub fn new<I: IntoIterator<Item = OsfConstraintDto>>(constraints: I) -> Self {
        Self {
            constraints: constraints.into_iter().collect(),
        }
    }
}