1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! 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(),
}
}
}