Skip to main content

runtime_core/
landscape.rs

1use serde::{Deserialize, Serialize};
2
3/// Stable identifier for a curated type in the package landscape.
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
5#[serde(transparent)]
6pub struct LandscapeTypeId(pub String);
7
8impl LandscapeTypeId {
9    pub fn new(value: impl Into<String>) -> Self {
10        Self(value.into())
11    }
12
13    pub fn as_str(&self) -> &str {
14        &self.0
15    }
16}
17
18impl From<&str> for LandscapeTypeId {
19    fn from(value: &str) -> Self {
20        Self(value.to_string())
21    }
22}
23
24impl From<String> for LandscapeTypeId {
25    fn from(value: String) -> Self {
26        Self(value)
27    }
28}
29
30/// Stable identifier for a curated function in the package landscape.
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
32#[serde(transparent)]
33pub struct LandscapeFunctionId(pub String);
34
35impl LandscapeFunctionId {
36    pub fn new(value: impl Into<String>) -> Self {
37        Self(value.into())
38    }
39
40    pub fn as_str(&self) -> &str {
41        &self.0
42    }
43}
44
45impl From<&str> for LandscapeFunctionId {
46    fn from(value: &str) -> Self {
47        Self(value.to_string())
48    }
49}
50
51impl From<String> for LandscapeFunctionId {
52    fn from(value: String) -> Self {
53        Self(value)
54    }
55}
56
57/// Reference to a curated type without depending on the owning domain crate.
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
59#[serde(rename_all = "camelCase")]
60pub struct LandscapeTypeRef {
61    pub id: LandscapeTypeId,
62    pub owner: String,
63    pub rust_type: Option<String>,
64    pub schema_ref: Option<String>,
65}
66
67impl LandscapeTypeRef {
68    pub fn new(id: impl Into<LandscapeTypeId>, owner: impl Into<String>) -> Self {
69        Self {
70            id: id.into(),
71            owner: owner.into(),
72            rust_type: None,
73            schema_ref: None,
74        }
75    }
76
77    pub fn rust_type(mut self, value: impl Into<String>) -> Self {
78        self.rust_type = Some(value.into());
79        self
80    }
81
82    pub fn schema_ref(mut self, value: impl Into<String>) -> Self {
83        self.schema_ref = Some(value.into());
84        self
85    }
86}
87
88/// A curated function input or output.
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
90#[serde(rename_all = "camelCase")]
91pub struct LandscapePort {
92    pub name: String,
93    #[serde(rename = "typeRef")]
94    pub type_ref: LandscapeTypeRef,
95    pub required: bool,
96    pub cardinality: LandscapeCardinality,
97}
98
99impl LandscapePort {
100    pub fn new(name: impl Into<String>, type_ref: LandscapeTypeRef) -> Self {
101        Self {
102            name: name.into(),
103            type_ref,
104            required: true,
105            cardinality: LandscapeCardinality::One,
106        }
107    }
108
109    pub fn optional(mut self) -> Self {
110        self.required = false;
111        self.cardinality = LandscapeCardinality::Optional;
112        self
113    }
114
115    pub fn many(mut self) -> Self {
116        self.cardinality = LandscapeCardinality::Many;
117        self
118    }
119}
120
121/// Cardinality of a curated function input or output.
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
123#[serde(rename_all = "camelCase")]
124pub enum LandscapeCardinality {
125    One,
126    Optional,
127    Many,
128}
129
130/// Curated function metadata for one operation.
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
132#[serde(rename_all = "camelCase")]
133pub struct LandscapeFunction {
134    pub id: LandscapeFunctionId,
135    pub owner: String,
136    pub inputs: Vec<LandscapePort>,
137    pub outputs: Vec<LandscapePort>,
138    pub stability: LandscapeStability,
139}
140
141impl LandscapeFunction {
142    pub fn new(id: impl Into<LandscapeFunctionId>, owner: impl Into<String>) -> Self {
143        Self {
144            id: id.into(),
145            owner: owner.into(),
146            inputs: Vec::new(),
147            outputs: Vec::new(),
148            stability: LandscapeStability::Stable,
149        }
150    }
151
152    pub fn input(mut self, port: LandscapePort) -> Self {
153        self.inputs.push(port);
154        self
155    }
156
157    pub fn output(mut self, port: LandscapePort) -> Self {
158        self.outputs.push(port);
159        self
160    }
161
162    pub fn stability(mut self, stability: LandscapeStability) -> Self {
163        self.stability = stability;
164        self
165    }
166}
167
168/// Release stability for curated landscape metadata.
169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
170#[serde(rename_all = "camelCase")]
171pub enum LandscapeStability {
172    Stable,
173    Experimental,
174    Internal,
175}
176
177/// Landscape metadata attached to one surface operation.
178#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
179#[serde(rename_all = "camelCase")]
180pub struct LandscapeOperationContract {
181    pub function: LandscapeFunction,
182}
183
184impl LandscapeOperationContract {
185    pub fn new(function: LandscapeFunction) -> Self {
186        Self { function }
187    }
188}
189
190/// Returns known package owners for curated landscape metadata.
191pub fn known_owner_packages() -> &'static [&'static str] {
192    well_known::known_owner_packages()
193}
194
195/// Validates a curated landscape contract without checking domain semantics.
196pub fn validate_landscape_contract(contract: &LandscapeOperationContract) -> Result<(), String> {
197    validate_landscape_function(&contract.function)
198}
199
200/// Validates one curated function declaration.
201pub fn validate_landscape_function(function: &LandscapeFunction) -> Result<(), String> {
202    if function.id.as_str().trim().is_empty() {
203        return Err("curated function id must not be empty".to_string());
204    }
205    validate_owner("curated function", &function.owner)?;
206    if !matches!(function.stability, LandscapeStability::Internal) {
207        if function.inputs.is_empty() {
208            return Err(format!(
209                "curated function `{}` must declare at least one input",
210                function.id.as_str()
211            ));
212        }
213        if function.outputs.is_empty() {
214            return Err(format!(
215                "curated function `{}` must declare at least one output",
216                function.id.as_str()
217            ));
218        }
219    }
220    for port in function.inputs.iter().chain(function.outputs.iter()) {
221        validate_port(function.id.as_str(), port)?;
222    }
223    Ok(())
224}
225
226fn validate_port(function_id: &str, port: &LandscapePort) -> Result<(), String> {
227    if port.name.trim().is_empty() {
228        return Err(format!(
229            "curated function `{function_id}` has a port with an empty name"
230        ));
231    }
232    if port.type_ref.id.as_str().trim().is_empty() {
233        return Err(format!(
234            "curated function `{function_id}` port `{}` has an empty type id",
235            port.name
236        ));
237    }
238    validate_owner(
239        &format!("curated function `{function_id}` port `{}`", port.name),
240        &port.type_ref.owner,
241    )
242}
243
244fn validate_owner(context: &str, owner: &str) -> Result<(), String> {
245    if owner.trim().is_empty() {
246        return Err(format!("{context} owner must not be empty"));
247    }
248    if !known_owner_packages().contains(&owner) {
249        return Err(format!("{context} owner `{owner}` is not known"));
250    }
251    Ok(())
252}
253
254/// Well-known curated type references for foundational contract owners.
255pub mod well_known {
256    use super::{LandscapeTypeId, LandscapeTypeRef};
257
258    pub const OWNER_RUNTIME_CORE: &str = "moenarch-runtime-core";
259    pub const OWNER_TEXT_CORE: &str = "moenarch-text-core";
260    pub const OWNER_TEXT_TRANSCRIPTS: &str = "moenarch-text-transcripts";
261    pub const OWNER_TEXT_ANALYSIS: &str = "moenarch-text-analysis";
262    pub const OWNER_TEXT_RETRIEVAL: &str = "moenarch-text-retrieval";
263    pub const OWNER_IMAGE_ANALYSIS_CORE: &str = "moenarch-image-analysis-core";
264    pub const OWNER_IMAGE_ANALYSIS_DETECTION: &str = "moenarch-image-analysis-detection";
265    pub const OWNER_AUDIO_ANALYSIS_CORE: &str = "moenarch-audio-analysis-core";
266    pub const OWNER_AUDIO_ANALYSIS_TRANSCRIPTION: &str = "moenarch-audio-analysis-transcription";
267    pub const OWNER_VISION_CORE: &str = "moenarch-vision-core";
268    pub const OWNER_VECTOR_ANALYSIS_CORE: &str = "moenarch-vector-analysis-core";
269    pub const OWNER_TENSOR_DATA: &str = "moenarch-tensor-data";
270    pub const OWNER_NUMBERS_CORE: &str = "moenarch-numbers-core";
271    pub const OWNER_MATH_GEOMETRY_2D: &str = "moenarch-math-geometry-2d";
272    pub const OWNER_VIDEO_ANALYSIS_CORE: &str = "moenarch-video-analysis-core";
273    pub const OWNER_VIDEO_ANALYSIS_DETECTORS: &str = "moenarch-video-analysis-detectors";
274    pub const OWNER_VIDEO_ANALYSIS_OUTPUT: &str = "moenarch-video-analysis-output";
275    pub const OWNER_VIDEO_ANALYSIS_RECONSTRUCTION: &str = "moenarch-video-analysis-reconstruction";
276    pub const OWNER_VIDEO_ANALYSIS_SFM: &str = "moenarch-video-analysis-sfm";
277    pub const OWNER_VIDEO_ANALYSIS_RADIANCE_FIELDS: &str =
278        "moenarch-video-analysis-radiance-fields";
279    pub const OWNER_VIDEO_ANALYSIS_RADIANCE_PIPELINE: &str =
280        "moenarch-video-analysis-radiance-pipeline";
281
282    pub const RUNTIME_SURFACE_REQUEST: &str = "runtime.surfaceRequest";
283    pub const RUNTIME_SURFACE_RESPONSE: &str = "runtime.surfaceResponse";
284    pub const TEXT_DOCUMENT: &str = "text.document";
285    pub const TEXT_SEGMENT: &str = "text.segment";
286    pub const TEXT_TRANSCRIPT_SEGMENT: &str = "text.transcriptSegment";
287    pub const TEXT_ANALYSIS_REPORT: &str = "text.analysisReport";
288    pub const TEXT_RETRIEVAL_QUERY: &str = "text.retrievalQuery";
289    pub const TEXT_SEARCH_RESULT: &str = "text.searchResult";
290    pub const IMAGE_IMAGE: &str = "image.image";
291    pub const IMAGE_DETECTION_REQUEST: &str = "image.detectionRequest";
292    pub const AUDIO_FRAME: &str = "audio.frame";
293    pub const AUDIO_SOURCE: &str = "audio.source";
294    pub const AUDIO_TRANSCRIPTION_CONFIG: &str = "audio.transcriptionConfig";
295    pub const VISION_DETECTION: &str = "vision.detection";
296    pub const VISION_EMBEDDING: &str = "vision.embedding";
297    pub const VECTOR_VECTOR: &str = "vector.vector";
298    pub const TENSOR_F32_TENSOR: &str = "tensor.f32Tensor";
299    pub const NUMBERS_SUMMARY: &str = "numbers.summary";
300    pub const GEOMETRY_RECT_U32: &str = "geometry.rectU32";
301    pub const GEOMETRY_POINT2F: &str = "geometry.point2f";
302    pub const VIDEO_TIMECODE: &str = "video.timecode";
303    pub const VIDEO_FRAME: &str = "video.frame";
304    pub const VIDEO_SCENE: &str = "video.scene";
305    pub const VIDEO_SCENE_LIST: &str = "video.sceneList";
306    pub const VIDEO_DETECTOR_CONFIG: &str = "video.detectorConfig";
307    pub const VIDEO_CAMERA: &str = "video.camera";
308    pub const VIDEO_CAMERA_PATH: &str = "video.cameraPath";
309    pub const VIDEO_RECONSTRUCTION: &str = "video.reconstruction";
310    pub const VIDEO_SFM_MATCH_PLAN: &str = "video.sfmMatchPlan";
311    pub const VIDEO_RADIANCE_ASSET: &str = "video.radianceAsset";
312
313    pub fn runtime_surface_request() -> LandscapeTypeRef {
314        type_ref(
315            RUNTIME_SURFACE_REQUEST,
316            OWNER_RUNTIME_CORE,
317            "runtime_core::SurfaceRequest",
318        )
319    }
320
321    pub fn runtime_surface_response() -> LandscapeTypeRef {
322        type_ref(
323            RUNTIME_SURFACE_RESPONSE,
324            OWNER_RUNTIME_CORE,
325            "runtime_core::SurfaceResponse",
326        )
327    }
328
329    pub fn text_document() -> LandscapeTypeRef {
330        type_ref(
331            TEXT_DOCUMENT,
332            OWNER_TEXT_CORE,
333            "text_core::TextDocumentContract",
334        )
335    }
336
337    pub fn text_segment() -> LandscapeTypeRef {
338        type_ref(
339            TEXT_SEGMENT,
340            OWNER_TEXT_CORE,
341            "text_core::TextSegmentContract",
342        )
343    }
344
345    pub fn text_transcript_segment() -> LandscapeTypeRef {
346        type_ref(
347            TEXT_TRANSCRIPT_SEGMENT,
348            OWNER_TEXT_TRANSCRIPTS,
349            "text_transcripts::TranscriptSegmentContract",
350        )
351    }
352
353    pub fn text_analysis_report() -> LandscapeTypeRef {
354        type_ref(
355            TEXT_ANALYSIS_REPORT,
356            OWNER_TEXT_ANALYSIS,
357            "text_analysis::DocumentAnalysisReport",
358        )
359    }
360
361    pub fn text_retrieval_query() -> LandscapeTypeRef {
362        type_ref(
363            TEXT_RETRIEVAL_QUERY,
364            OWNER_TEXT_RETRIEVAL,
365            "text_retrieval::SearchQuery",
366        )
367    }
368
369    pub fn text_search_result() -> LandscapeTypeRef {
370        type_ref(
371            TEXT_SEARCH_RESULT,
372            OWNER_TEXT_RETRIEVAL,
373            "text_retrieval::SearchResult",
374        )
375    }
376
377    pub fn image_image() -> LandscapeTypeRef {
378        type_ref(
379            IMAGE_IMAGE,
380            OWNER_IMAGE_ANALYSIS_CORE,
381            "image_analysis_core::OwnedImage",
382        )
383    }
384
385    pub fn image_detection_request() -> LandscapeTypeRef {
386        type_ref(
387            IMAGE_DETECTION_REQUEST,
388            OWNER_IMAGE_ANALYSIS_DETECTION,
389            "image_analysis_detection::ImageDetectionRequest",
390        )
391    }
392
393    pub fn audio_frame() -> LandscapeTypeRef {
394        type_ref(
395            AUDIO_FRAME,
396            OWNER_AUDIO_ANALYSIS_CORE,
397            "video_analysis_core::OwnedAudioFrame",
398        )
399    }
400
401    pub fn audio_source() -> LandscapeTypeRef {
402        type_ref(
403            AUDIO_SOURCE,
404            OWNER_AUDIO_ANALYSIS_TRANSCRIPTION,
405            "audio_analysis_transcription::TranscriptionSource",
406        )
407    }
408
409    pub fn audio_transcription_config() -> LandscapeTypeRef {
410        type_ref(
411            AUDIO_TRANSCRIPTION_CONFIG,
412            OWNER_AUDIO_ANALYSIS_TRANSCRIPTION,
413            "audio_analysis_transcription::TranscriptionPipelineRequest",
414        )
415    }
416
417    pub fn vision_detection() -> LandscapeTypeRef {
418        type_ref(
419            VISION_DETECTION,
420            OWNER_VISION_CORE,
421            "vision_core::VisualDetection",
422        )
423    }
424
425    pub fn vision_embedding() -> LandscapeTypeRef {
426        type_ref(
427            VISION_EMBEDDING,
428            OWNER_VISION_CORE,
429            "vision_core::VisualEmbedding",
430        )
431    }
432
433    pub fn vector_vector() -> LandscapeTypeRef {
434        type_ref(
435            VECTOR_VECTOR,
436            OWNER_VECTOR_ANALYSIS_CORE,
437            "vector_analysis_core::DenseVector",
438        )
439    }
440
441    pub fn tensor_f32_tensor() -> LandscapeTypeRef {
442        type_ref(
443            TENSOR_F32_TENSOR,
444            OWNER_TENSOR_DATA,
445            "tensor_data::F32Tensor",
446        )
447    }
448
449    pub fn numbers_summary() -> LandscapeTypeRef {
450        type_ref(
451            NUMBERS_SUMMARY,
452            OWNER_NUMBERS_CORE,
453            "numbers_core::NumberSummary",
454        )
455    }
456
457    pub fn geometry_rect_u32() -> LandscapeTypeRef {
458        type_ref(
459            GEOMETRY_RECT_U32,
460            OWNER_MATH_GEOMETRY_2D,
461            "math_geometry_2d::RectU32",
462        )
463    }
464
465    pub fn geometry_point2f() -> LandscapeTypeRef {
466        type_ref(
467            GEOMETRY_POINT2F,
468            OWNER_MATH_GEOMETRY_2D,
469            "math_geometry_2d::Point2f",
470        )
471    }
472
473    pub fn video_timecode() -> LandscapeTypeRef {
474        type_ref(
475            VIDEO_TIMECODE,
476            OWNER_VIDEO_ANALYSIS_CORE,
477            "video_analysis_core::FrameTimecode",
478        )
479    }
480
481    pub fn video_frame() -> LandscapeTypeRef {
482        type_ref(
483            VIDEO_FRAME,
484            OWNER_VIDEO_ANALYSIS_CORE,
485            "video_analysis_core::VideoFrame",
486        )
487    }
488
489    pub fn video_scene() -> LandscapeTypeRef {
490        type_ref(
491            VIDEO_SCENE,
492            OWNER_VIDEO_ANALYSIS_CORE,
493            "video_analysis_core::Scene",
494        )
495    }
496
497    pub fn video_scene_list() -> LandscapeTypeRef {
498        type_ref(
499            VIDEO_SCENE_LIST,
500            OWNER_VIDEO_ANALYSIS_OUTPUT,
501            "video_analysis_output::SceneList",
502        )
503    }
504
505    pub fn video_detector_config() -> LandscapeTypeRef {
506        type_ref(
507            VIDEO_DETECTOR_CONFIG,
508            OWNER_VIDEO_ANALYSIS_DETECTORS,
509            "video_analysis_detectors::WeightedCompositeDetector",
510        )
511    }
512
513    pub fn video_camera() -> LandscapeTypeRef {
514        type_ref(
515            VIDEO_CAMERA,
516            OWNER_VIDEO_ANALYSIS_RADIANCE_FIELDS,
517            "video_analysis_radiance_fields::CameraView",
518        )
519    }
520
521    pub fn video_camera_path() -> LandscapeTypeRef {
522        type_ref(
523            VIDEO_CAMERA_PATH,
524            OWNER_VIDEO_ANALYSIS_RADIANCE_FIELDS,
525            "video_analysis_radiance_fields::CameraPath",
526        )
527    }
528
529    pub fn video_reconstruction() -> LandscapeTypeRef {
530        type_ref(
531            VIDEO_RECONSTRUCTION,
532            OWNER_VIDEO_ANALYSIS_RECONSTRUCTION,
533            "video_analysis_reconstruction::Reconstruction",
534        )
535    }
536
537    pub fn video_sfm_match_plan() -> LandscapeTypeRef {
538        type_ref(
539            VIDEO_SFM_MATCH_PLAN,
540            OWNER_VIDEO_ANALYSIS_SFM,
541            "video_analysis_sfm::SfmRequest",
542        )
543    }
544
545    pub fn video_radiance_asset() -> LandscapeTypeRef {
546        type_ref(
547            VIDEO_RADIANCE_ASSET,
548            OWNER_VIDEO_ANALYSIS_RADIANCE_PIPELINE,
549            "video_analysis_radiance_pipeline::RadianceAsset",
550        )
551    }
552
553    pub fn known_owner_packages() -> &'static [&'static str] {
554        &[
555            OWNER_RUNTIME_CORE,
556            OWNER_TEXT_CORE,
557            OWNER_TEXT_TRANSCRIPTS,
558            OWNER_TEXT_ANALYSIS,
559            OWNER_TEXT_RETRIEVAL,
560            OWNER_IMAGE_ANALYSIS_CORE,
561            OWNER_IMAGE_ANALYSIS_DETECTION,
562            OWNER_AUDIO_ANALYSIS_CORE,
563            OWNER_AUDIO_ANALYSIS_TRANSCRIPTION,
564            OWNER_VISION_CORE,
565            OWNER_VECTOR_ANALYSIS_CORE,
566            OWNER_TENSOR_DATA,
567            OWNER_NUMBERS_CORE,
568            OWNER_MATH_GEOMETRY_2D,
569            OWNER_VIDEO_ANALYSIS_CORE,
570            OWNER_VIDEO_ANALYSIS_DETECTORS,
571            OWNER_VIDEO_ANALYSIS_OUTPUT,
572            OWNER_VIDEO_ANALYSIS_RECONSTRUCTION,
573            OWNER_VIDEO_ANALYSIS_SFM,
574            OWNER_VIDEO_ANALYSIS_RADIANCE_FIELDS,
575            OWNER_VIDEO_ANALYSIS_RADIANCE_PIPELINE,
576        ]
577    }
578
579    pub fn known_type_ids() -> &'static [&'static str] {
580        &[
581            RUNTIME_SURFACE_REQUEST,
582            RUNTIME_SURFACE_RESPONSE,
583            TEXT_DOCUMENT,
584            TEXT_SEGMENT,
585            TEXT_TRANSCRIPT_SEGMENT,
586            TEXT_ANALYSIS_REPORT,
587            TEXT_RETRIEVAL_QUERY,
588            TEXT_SEARCH_RESULT,
589            IMAGE_IMAGE,
590            IMAGE_DETECTION_REQUEST,
591            AUDIO_FRAME,
592            AUDIO_SOURCE,
593            AUDIO_TRANSCRIPTION_CONFIG,
594            VISION_DETECTION,
595            VISION_EMBEDDING,
596            VECTOR_VECTOR,
597            TENSOR_F32_TENSOR,
598            NUMBERS_SUMMARY,
599            GEOMETRY_RECT_U32,
600            GEOMETRY_POINT2F,
601            VIDEO_TIMECODE,
602            VIDEO_FRAME,
603            VIDEO_SCENE,
604            VIDEO_SCENE_LIST,
605            VIDEO_DETECTOR_CONFIG,
606            VIDEO_CAMERA,
607            VIDEO_CAMERA_PATH,
608            VIDEO_RECONSTRUCTION,
609            VIDEO_SFM_MATCH_PLAN,
610            VIDEO_RADIANCE_ASSET,
611        ]
612    }
613
614    fn type_ref(
615        id: &'static str,
616        owner: &'static str,
617        rust_type: &'static str,
618    ) -> LandscapeTypeRef {
619        LandscapeTypeRef::new(LandscapeTypeId::new(id), owner).rust_type(rust_type)
620    }
621}