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 = "moritzbrantner-runtime-core";
259    pub const OWNER_TEXT_CORE: &str = "moritzbrantner-text-core";
260    pub const OWNER_TEXT_TRANSCRIPTS: &str = "moritzbrantner-text-transcripts";
261    pub const OWNER_TEXT_ANALYSIS: &str = "moritzbrantner-text-analysis";
262    pub const OWNER_TEXT_RETRIEVAL: &str = "moritzbrantner-text-retrieval";
263    pub const OWNER_IMAGE_ANALYSIS_CORE: &str = "moritzbrantner-image-analysis-core";
264    pub const OWNER_IMAGE_ANALYSIS_DETECTION: &str = "moritzbrantner-image-analysis-detection";
265    pub const OWNER_AUDIO_ANALYSIS_CORE: &str = "moritzbrantner-audio-analysis-core";
266    pub const OWNER_AUDIO_ANALYSIS_TRANSCRIPTION: &str =
267        "moritzbrantner-audio-analysis-transcription";
268    pub const OWNER_VISION_CORE: &str = "moritzbrantner-vision-core";
269    pub const OWNER_VECTOR_ANALYSIS_CORE: &str = "moritzbrantner-vector-analysis-core";
270    pub const OWNER_TENSOR_DATA: &str = "moritzbrantner-tensor-data";
271    pub const OWNER_NUMBERS_CORE: &str = "moritzbrantner-numbers-core";
272    pub const OWNER_MATH_GEOMETRY_2D: &str = "moritzbrantner-math-geometry-2d";
273    pub const OWNER_VIDEO_ANALYSIS_CORE: &str = "moritzbrantner-video-analysis-core";
274    pub const OWNER_VIDEO_ANALYSIS_DETECTORS: &str = "moritzbrantner-video-analysis-detectors";
275    pub const OWNER_VIDEO_ANALYSIS_OUTPUT: &str = "moritzbrantner-video-analysis-output";
276    pub const OWNER_VIDEO_ANALYSIS_RECONSTRUCTION: &str =
277        "moritzbrantner-video-analysis-reconstruction";
278    pub const OWNER_VIDEO_ANALYSIS_SFM: &str = "moritzbrantner-video-analysis-sfm";
279    pub const OWNER_VIDEO_ANALYSIS_RADIANCE_FIELDS: &str =
280        "moritzbrantner-video-analysis-radiance-fields";
281    pub const OWNER_VIDEO_ANALYSIS_RADIANCE_PIPELINE: &str =
282        "moritzbrantner-video-analysis-radiance-pipeline";
283
284    pub const RUNTIME_SURFACE_REQUEST: &str = "runtime.surfaceRequest";
285    pub const RUNTIME_SURFACE_RESPONSE: &str = "runtime.surfaceResponse";
286    pub const TEXT_DOCUMENT: &str = "text.document";
287    pub const TEXT_SEGMENT: &str = "text.segment";
288    pub const TEXT_TRANSCRIPT_SEGMENT: &str = "text.transcriptSegment";
289    pub const TEXT_ANALYSIS_REPORT: &str = "text.analysisReport";
290    pub const TEXT_RETRIEVAL_QUERY: &str = "text.retrievalQuery";
291    pub const TEXT_SEARCH_RESULT: &str = "text.searchResult";
292    pub const IMAGE_IMAGE: &str = "image.image";
293    pub const IMAGE_DETECTION_REQUEST: &str = "image.detectionRequest";
294    pub const AUDIO_FRAME: &str = "audio.frame";
295    pub const AUDIO_SOURCE: &str = "audio.source";
296    pub const AUDIO_TRANSCRIPTION_CONFIG: &str = "audio.transcriptionConfig";
297    pub const VISION_DETECTION: &str = "vision.detection";
298    pub const VISION_EMBEDDING: &str = "vision.embedding";
299    pub const VECTOR_VECTOR: &str = "vector.vector";
300    pub const TENSOR_F32_TENSOR: &str = "tensor.f32Tensor";
301    pub const NUMBERS_SUMMARY: &str = "numbers.summary";
302    pub const GEOMETRY_RECT_U32: &str = "geometry.rectU32";
303    pub const GEOMETRY_POINT2F: &str = "geometry.point2f";
304    pub const VIDEO_TIMECODE: &str = "video.timecode";
305    pub const VIDEO_FRAME: &str = "video.frame";
306    pub const VIDEO_SCENE: &str = "video.scene";
307    pub const VIDEO_SCENE_LIST: &str = "video.sceneList";
308    pub const VIDEO_DETECTOR_CONFIG: &str = "video.detectorConfig";
309    pub const VIDEO_CAMERA: &str = "video.camera";
310    pub const VIDEO_CAMERA_PATH: &str = "video.cameraPath";
311    pub const VIDEO_RECONSTRUCTION: &str = "video.reconstruction";
312    pub const VIDEO_SFM_MATCH_PLAN: &str = "video.sfmMatchPlan";
313    pub const VIDEO_RADIANCE_ASSET: &str = "video.radianceAsset";
314
315    pub fn runtime_surface_request() -> LandscapeTypeRef {
316        type_ref(
317            RUNTIME_SURFACE_REQUEST,
318            OWNER_RUNTIME_CORE,
319            "runtime_core::SurfaceRequest",
320        )
321    }
322
323    pub fn runtime_surface_response() -> LandscapeTypeRef {
324        type_ref(
325            RUNTIME_SURFACE_RESPONSE,
326            OWNER_RUNTIME_CORE,
327            "runtime_core::SurfaceResponse",
328        )
329    }
330
331    pub fn text_document() -> LandscapeTypeRef {
332        type_ref(
333            TEXT_DOCUMENT,
334            OWNER_TEXT_CORE,
335            "text_core::TextDocumentContract",
336        )
337    }
338
339    pub fn text_segment() -> LandscapeTypeRef {
340        type_ref(
341            TEXT_SEGMENT,
342            OWNER_TEXT_CORE,
343            "text_core::TextSegmentContract",
344        )
345    }
346
347    pub fn text_transcript_segment() -> LandscapeTypeRef {
348        type_ref(
349            TEXT_TRANSCRIPT_SEGMENT,
350            OWNER_TEXT_TRANSCRIPTS,
351            "text_transcripts::TranscriptSegmentContract",
352        )
353    }
354
355    pub fn text_analysis_report() -> LandscapeTypeRef {
356        type_ref(
357            TEXT_ANALYSIS_REPORT,
358            OWNER_TEXT_ANALYSIS,
359            "text_analysis::DocumentAnalysisReport",
360        )
361    }
362
363    pub fn text_retrieval_query() -> LandscapeTypeRef {
364        type_ref(
365            TEXT_RETRIEVAL_QUERY,
366            OWNER_TEXT_RETRIEVAL,
367            "text_retrieval::SearchQuery",
368        )
369    }
370
371    pub fn text_search_result() -> LandscapeTypeRef {
372        type_ref(
373            TEXT_SEARCH_RESULT,
374            OWNER_TEXT_RETRIEVAL,
375            "text_retrieval::SearchResult",
376        )
377    }
378
379    pub fn image_image() -> LandscapeTypeRef {
380        type_ref(
381            IMAGE_IMAGE,
382            OWNER_IMAGE_ANALYSIS_CORE,
383            "image_analysis_core::OwnedImage",
384        )
385    }
386
387    pub fn image_detection_request() -> LandscapeTypeRef {
388        type_ref(
389            IMAGE_DETECTION_REQUEST,
390            OWNER_IMAGE_ANALYSIS_DETECTION,
391            "image_analysis_detection::ImageDetectionRequest",
392        )
393    }
394
395    pub fn audio_frame() -> LandscapeTypeRef {
396        type_ref(
397            AUDIO_FRAME,
398            OWNER_AUDIO_ANALYSIS_CORE,
399            "video_analysis_core::OwnedAudioFrame",
400        )
401    }
402
403    pub fn audio_source() -> LandscapeTypeRef {
404        type_ref(
405            AUDIO_SOURCE,
406            OWNER_AUDIO_ANALYSIS_TRANSCRIPTION,
407            "audio_analysis_transcription::TranscriptionSource",
408        )
409    }
410
411    pub fn audio_transcription_config() -> LandscapeTypeRef {
412        type_ref(
413            AUDIO_TRANSCRIPTION_CONFIG,
414            OWNER_AUDIO_ANALYSIS_TRANSCRIPTION,
415            "audio_analysis_transcription::TranscriptionPipelineRequest",
416        )
417    }
418
419    pub fn vision_detection() -> LandscapeTypeRef {
420        type_ref(
421            VISION_DETECTION,
422            OWNER_VISION_CORE,
423            "vision_core::VisualDetection",
424        )
425    }
426
427    pub fn vision_embedding() -> LandscapeTypeRef {
428        type_ref(
429            VISION_EMBEDDING,
430            OWNER_VISION_CORE,
431            "vision_core::VisualEmbedding",
432        )
433    }
434
435    pub fn vector_vector() -> LandscapeTypeRef {
436        type_ref(
437            VECTOR_VECTOR,
438            OWNER_VECTOR_ANALYSIS_CORE,
439            "vector_analysis_core::DenseVector",
440        )
441    }
442
443    pub fn tensor_f32_tensor() -> LandscapeTypeRef {
444        type_ref(
445            TENSOR_F32_TENSOR,
446            OWNER_TENSOR_DATA,
447            "tensor_data::F32Tensor",
448        )
449    }
450
451    pub fn numbers_summary() -> LandscapeTypeRef {
452        type_ref(
453            NUMBERS_SUMMARY,
454            OWNER_NUMBERS_CORE,
455            "numbers_core::NumberSummary",
456        )
457    }
458
459    pub fn geometry_rect_u32() -> LandscapeTypeRef {
460        type_ref(
461            GEOMETRY_RECT_U32,
462            OWNER_MATH_GEOMETRY_2D,
463            "math_geometry_2d::RectU32",
464        )
465    }
466
467    pub fn geometry_point2f() -> LandscapeTypeRef {
468        type_ref(
469            GEOMETRY_POINT2F,
470            OWNER_MATH_GEOMETRY_2D,
471            "math_geometry_2d::Point2f",
472        )
473    }
474
475    pub fn video_timecode() -> LandscapeTypeRef {
476        type_ref(
477            VIDEO_TIMECODE,
478            OWNER_VIDEO_ANALYSIS_CORE,
479            "video_analysis_core::FrameTimecode",
480        )
481    }
482
483    pub fn video_frame() -> LandscapeTypeRef {
484        type_ref(
485            VIDEO_FRAME,
486            OWNER_VIDEO_ANALYSIS_CORE,
487            "video_analysis_core::VideoFrame",
488        )
489    }
490
491    pub fn video_scene() -> LandscapeTypeRef {
492        type_ref(
493            VIDEO_SCENE,
494            OWNER_VIDEO_ANALYSIS_CORE,
495            "video_analysis_core::Scene",
496        )
497    }
498
499    pub fn video_scene_list() -> LandscapeTypeRef {
500        type_ref(
501            VIDEO_SCENE_LIST,
502            OWNER_VIDEO_ANALYSIS_OUTPUT,
503            "video_analysis_output::SceneList",
504        )
505    }
506
507    pub fn video_detector_config() -> LandscapeTypeRef {
508        type_ref(
509            VIDEO_DETECTOR_CONFIG,
510            OWNER_VIDEO_ANALYSIS_DETECTORS,
511            "video_analysis_detectors::WeightedCompositeDetector",
512        )
513    }
514
515    pub fn video_camera() -> LandscapeTypeRef {
516        type_ref(
517            VIDEO_CAMERA,
518            OWNER_VIDEO_ANALYSIS_RADIANCE_FIELDS,
519            "video_analysis_radiance_fields::CameraView",
520        )
521    }
522
523    pub fn video_camera_path() -> LandscapeTypeRef {
524        type_ref(
525            VIDEO_CAMERA_PATH,
526            OWNER_VIDEO_ANALYSIS_RADIANCE_FIELDS,
527            "video_analysis_radiance_fields::CameraPath",
528        )
529    }
530
531    pub fn video_reconstruction() -> LandscapeTypeRef {
532        type_ref(
533            VIDEO_RECONSTRUCTION,
534            OWNER_VIDEO_ANALYSIS_RECONSTRUCTION,
535            "video_analysis_reconstruction::Reconstruction",
536        )
537    }
538
539    pub fn video_sfm_match_plan() -> LandscapeTypeRef {
540        type_ref(
541            VIDEO_SFM_MATCH_PLAN,
542            OWNER_VIDEO_ANALYSIS_SFM,
543            "video_analysis_sfm::SfmRequest",
544        )
545    }
546
547    pub fn video_radiance_asset() -> LandscapeTypeRef {
548        type_ref(
549            VIDEO_RADIANCE_ASSET,
550            OWNER_VIDEO_ANALYSIS_RADIANCE_PIPELINE,
551            "video_analysis_radiance_pipeline::RadianceAsset",
552        )
553    }
554
555    pub fn known_owner_packages() -> &'static [&'static str] {
556        &[
557            OWNER_RUNTIME_CORE,
558            OWNER_TEXT_CORE,
559            OWNER_TEXT_TRANSCRIPTS,
560            OWNER_TEXT_ANALYSIS,
561            OWNER_TEXT_RETRIEVAL,
562            OWNER_IMAGE_ANALYSIS_CORE,
563            OWNER_IMAGE_ANALYSIS_DETECTION,
564            OWNER_AUDIO_ANALYSIS_CORE,
565            OWNER_AUDIO_ANALYSIS_TRANSCRIPTION,
566            OWNER_VISION_CORE,
567            OWNER_VECTOR_ANALYSIS_CORE,
568            OWNER_TENSOR_DATA,
569            OWNER_NUMBERS_CORE,
570            OWNER_MATH_GEOMETRY_2D,
571            OWNER_VIDEO_ANALYSIS_CORE,
572            OWNER_VIDEO_ANALYSIS_DETECTORS,
573            OWNER_VIDEO_ANALYSIS_OUTPUT,
574            OWNER_VIDEO_ANALYSIS_RECONSTRUCTION,
575            OWNER_VIDEO_ANALYSIS_SFM,
576            OWNER_VIDEO_ANALYSIS_RADIANCE_FIELDS,
577            OWNER_VIDEO_ANALYSIS_RADIANCE_PIPELINE,
578        ]
579    }
580
581    pub fn known_type_ids() -> &'static [&'static str] {
582        &[
583            RUNTIME_SURFACE_REQUEST,
584            RUNTIME_SURFACE_RESPONSE,
585            TEXT_DOCUMENT,
586            TEXT_SEGMENT,
587            TEXT_TRANSCRIPT_SEGMENT,
588            TEXT_ANALYSIS_REPORT,
589            TEXT_RETRIEVAL_QUERY,
590            TEXT_SEARCH_RESULT,
591            IMAGE_IMAGE,
592            IMAGE_DETECTION_REQUEST,
593            AUDIO_FRAME,
594            AUDIO_SOURCE,
595            AUDIO_TRANSCRIPTION_CONFIG,
596            VISION_DETECTION,
597            VISION_EMBEDDING,
598            VECTOR_VECTOR,
599            TENSOR_F32_TENSOR,
600            NUMBERS_SUMMARY,
601            GEOMETRY_RECT_U32,
602            GEOMETRY_POINT2F,
603            VIDEO_TIMECODE,
604            VIDEO_FRAME,
605            VIDEO_SCENE,
606            VIDEO_SCENE_LIST,
607            VIDEO_DETECTOR_CONFIG,
608            VIDEO_CAMERA,
609            VIDEO_CAMERA_PATH,
610            VIDEO_RECONSTRUCTION,
611            VIDEO_SFM_MATCH_PLAN,
612            VIDEO_RADIANCE_ASSET,
613        ]
614    }
615
616    fn type_ref(
617        id: &'static str,
618        owner: &'static str,
619        rust_type: &'static str,
620    ) -> LandscapeTypeRef {
621        LandscapeTypeRef::new(LandscapeTypeId::new(id), owner).rust_type(rust_type)
622    }
623}