runmat_package/manifest/
target.rs1use crate::ManifestError;
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeSet;
4use std::fmt::{Display, Formatter};
5use std::str::FromStr;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "kebab-case")]
9pub enum HostCapability {
10 BrowserFilesystem,
11 Network,
12 WebGpu,
13 Worker,
14 SharedMemory,
15 NativeLibrary,
16 Mex,
17 Jvm,
18 Subprocess,
19}
20
21impl HostCapability {
22 pub const fn as_str(self) -> &'static str {
23 match self {
24 Self::BrowserFilesystem => "browser-filesystem",
25 Self::Network => "network",
26 Self::WebGpu => "webgpu",
27 Self::Worker => "worker",
28 Self::SharedMemory => "shared-memory",
29 Self::NativeLibrary => "native-library",
30 Self::Mex => "mex",
31 Self::Jvm => "jvm",
32 Self::Subprocess => "subprocess",
33 }
34 }
35}
36
37impl Display for HostCapability {
38 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
39 formatter.write_str(self.as_str())
40 }
41}
42
43impl FromStr for HostCapability {
44 type Err = ManifestError;
45
46 fn from_str(value: &str) -> Result<Self, Self::Err> {
47 match value {
48 "browser-filesystem" => Ok(Self::BrowserFilesystem),
49 "network" => Ok(Self::Network),
50 "webgpu" => Ok(Self::WebGpu),
51 "worker" => Ok(Self::Worker),
52 "shared-memory" => Ok(Self::SharedMemory),
53 "native-library" => Ok(Self::NativeLibrary),
54 "mex" => Ok(Self::Mex),
55 "jvm" => Ok(Self::Jvm),
56 "subprocess" => Ok(Self::Subprocess),
57 _ => Err(ManifestError::InvalidCapability(value.to_string())),
58 }
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
63#[serde(tag = "kind", content = "value", rename_all = "kebab-case")]
64pub enum TargetPredicate {
65 Triple(String),
66 Capability(HostCapability),
67}
68
69impl Display for TargetPredicate {
70 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
71 match self {
72 Self::Triple(triple) => formatter.write_str(triple),
73 Self::Capability(capability) => write!(formatter, "capability:{capability}"),
74 }
75 }
76}
77
78impl FromStr for TargetPredicate {
79 type Err = ManifestError;
80
81 fn from_str(value: &str) -> Result<Self, Self::Err> {
82 let value = value.trim();
83 if let Some(capability) = value.strip_prefix("capability:") {
84 return Ok(Self::Capability(capability.parse()?));
85 }
86 if value.is_empty()
87 || value.chars().any(char::is_whitespace)
88 || value.contains(['/', '\\', ':'])
89 {
90 return Err(ManifestError::InvalidTarget {
91 value: value.to_string(),
92 reason: "expected a normalized target triple or `capability:<name>`".to_string(),
93 });
94 }
95 let segment_count = value.split('-').count();
96 if segment_count < 3 {
97 return Err(ManifestError::InvalidTarget {
98 value: value.to_string(),
99 reason: "target triples must contain at least architecture, vendor, and system"
100 .to_string(),
101 });
102 }
103 Ok(Self::Triple(value.to_ascii_lowercase()))
104 }
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub struct TargetEnvironment {
109 pub triple: String,
110 pub capabilities: BTreeSet<HostCapability>,
111}
112
113impl TargetEnvironment {
114 pub fn supports(&self, predicate: &TargetPredicate) -> bool {
115 match predicate {
116 TargetPredicate::Triple(triple) => &self.triple == triple,
117 TargetPredicate::Capability(capability) => self.capabilities.contains(capability),
118 }
119 }
120}