Skip to main content

ferrin_spec/dynamic/
model_ref.rs

1//! Generic reference wrappers.
2
3use std::sync::Arc;
4
5/// A reference to a model: either a resolved instance or a `provider:model`
6/// id that the application layer resolves through its registry.
7///
8/// `D` is the object-safe trait object type (`dyn DynLanguageModel`, ...).
9/// Provider implementations always return resolved references; the id form
10/// exists so that entry points can accept `impl Into<LanguageModelRef>` for
11/// both model instances and strings.
12pub struct ModelRef<D: ?Sized>(Inner<D>);
13
14enum Inner<D: ?Sized> {
15    Model(Arc<D>),
16    Id(String),
17}
18
19impl<D: ?Sized> ModelRef<D> {
20    /// Wraps a resolved model.
21    #[must_use]
22    pub fn from_arc(model: Arc<D>) -> Self {
23        Self(Inner::Model(model))
24    }
25
26    /// Creates an unresolved reference by id (for example `openai:gpt-5`).
27    #[must_use]
28    pub fn from_id(id: impl Into<String>) -> Self {
29        Self(Inner::Id(id.into()))
30    }
31
32    /// Returns the model when the reference is resolved.
33    #[must_use]
34    pub fn model(&self) -> Option<&Arc<D>> {
35        match &self.0 {
36            Inner::Model(model) => Some(model),
37            Inner::Id(_) => None,
38        }
39    }
40
41    /// Returns the id when the reference is unresolved.
42    #[must_use]
43    pub fn unresolved_id(&self) -> Option<&str> {
44        match &self.0 {
45            Inner::Model(_) => None,
46            Inner::Id(id) => Some(id),
47        }
48    }
49
50    /// Returns `true` when the reference holds a model instance.
51    #[must_use]
52    pub fn is_resolved(&self) -> bool {
53        matches!(self.0, Inner::Model(_))
54    }
55
56    /// Unwraps the model, or returns the unresolved id as the error.
57    ///
58    /// # Errors
59    ///
60    /// Returns the id when the reference was created with [`ModelRef::from_id`].
61    pub fn into_model(self) -> Result<Arc<D>, String> {
62        match self.0 {
63            Inner::Model(model) => Ok(model),
64            Inner::Id(id) => Err(id),
65        }
66    }
67}
68
69impl<D: ?Sized> Clone for ModelRef<D> {
70    fn clone(&self) -> Self {
71        Self(match &self.0 {
72            Inner::Model(model) => Inner::Model(Arc::clone(model)),
73            Inner::Id(id) => Inner::Id(id.clone()),
74        })
75    }
76}
77
78impl<D: ?Sized> std::fmt::Debug for ModelRef<D> {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        match &self.0 {
81            Inner::Model(_) => f.write_str("ModelRef::Model(..)"),
82            Inner::Id(id) => f.debug_tuple("ModelRef::Id").field(id).finish(),
83        }
84    }
85}
86
87impl<D: ?Sized> From<Arc<D>> for ModelRef<D> {
88    fn from(model: Arc<D>) -> Self {
89        Self::from_arc(model)
90    }
91}
92
93impl<D: ?Sized> From<String> for ModelRef<D> {
94    fn from(id: String) -> Self {
95        Self::from_id(id)
96    }
97}
98
99impl<D: ?Sized> From<&str> for ModelRef<D> {
100    fn from(id: &str) -> Self {
101        Self::from_id(id)
102    }
103}
104
105/// A reference to a provider service (files, skills, batch, realtime
106/// factory): always resolved.
107pub struct ServiceRef<D: ?Sized>(Arc<D>);
108
109impl<D: ?Sized> ServiceRef<D> {
110    /// Wraps a service instance.
111    #[must_use]
112    pub fn from_arc(service: Arc<D>) -> Self {
113        Self(service)
114    }
115
116    /// Returns the service.
117    #[must_use]
118    pub fn inner(&self) -> &Arc<D> {
119        &self.0
120    }
121
122    /// Unwraps the service.
123    #[must_use]
124    pub fn into_inner(self) -> Arc<D> {
125        self.0
126    }
127}
128
129impl<D: ?Sized> Clone for ServiceRef<D> {
130    fn clone(&self) -> Self {
131        Self(Arc::clone(&self.0))
132    }
133}
134
135impl<D: ?Sized> std::fmt::Debug for ServiceRef<D> {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        f.write_str("ServiceRef(..)")
138    }
139}
140
141impl<D: ?Sized> std::ops::Deref for ServiceRef<D> {
142    type Target = D;
143
144    fn deref(&self) -> &D {
145        &self.0
146    }
147}
148
149impl<D: ?Sized> From<Arc<D>> for ServiceRef<D> {
150    fn from(service: Arc<D>) -> Self {
151        Self::from_arc(service)
152    }
153}
154
155/// Implements `From<T>` and `From<Arc<T>>` for a reference alias.
156macro_rules! ref_conversions {
157    ($alias:ident, $trait_:ident, $dyn_trait:ident) => {
158        impl<T: $trait_> From<T> for $alias {
159            fn from(model: T) -> Self {
160                Self::from_arc(::std::sync::Arc::new(model))
161            }
162        }
163
164        impl<T: $trait_> From<::std::sync::Arc<T>> for $alias {
165            fn from(model: ::std::sync::Arc<T>) -> Self {
166                Self::from_arc(model)
167            }
168        }
169    };
170}
171
172pub(crate) use ref_conversions;