dictator_decree_abi/
lib.rs1#![warn(rust_2024_compatibility, clippy::all)]
2
3use serde::{Deserialize, Serialize};
4
5pub const ABI_VERSION: &str = "0.1.0";
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14pub enum Capability {
15 Lint,
17 AutoFix,
19 Streaming,
21 RuntimeConfig,
23 RichDiagnostics,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct DecreeMetadata {
30 pub abi_version: String,
32 pub decree_version: String,
34 pub description: String,
36 pub dectauthors: Option<String>,
38 pub supported_extensions: Vec<String>,
40 pub capabilities: Vec<Capability>,
42}
43
44impl DecreeMetadata {
45 #[must_use]
47 pub fn has_capability(&self, cap: Capability) -> bool {
48 self.capabilities.contains(&cap)
49 }
50
51 pub fn parse_version(version: &str) -> Result<(u32, u32, u32), String> {
58 let parts: Vec<&str> = version.split('.').collect();
59 if parts.len() != 3 {
60 return Err(format!("invalid version format: {version}"));
61 }
62 let major = parts[0]
63 .parse()
64 .map_err(|_| format!("invalid major: {}", parts[0]))?;
65 let minor = parts[1]
66 .parse()
67 .map_err(|_| format!("invalid minor: {}", parts[1]))?;
68 let patch = parts[2]
69 .parse()
70 .map_err(|_| format!("invalid patch: {}", parts[2]))?;
71 Ok((major, minor, patch))
72 }
73
74 pub fn validate_abi(&self, host_abi_version: &str) -> Result<(), String> {
80 let (host_maj, host_min, _) = Self::parse_version(host_abi_version)?;
81 let (decree_maj, decree_min, _) = Self::parse_version(&self.abi_version)?;
82
83 if host_maj == 0 {
85 if host_maj == decree_maj && host_min == decree_min {
86 return Ok(());
87 }
88 return Err(format!(
89 "ABI version mismatch: host {}, decree {}",
90 host_abi_version, self.abi_version
91 ));
92 }
93
94 if host_maj == decree_maj && decree_min <= host_min {
96 return Ok(());
97 }
98
99 Err(format!(
100 "ABI version incompatible: host {}, decree {}",
101 host_abi_version, self.abi_version
102 ))
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
108pub struct Span {
109 pub start: usize,
110 pub end: usize,
111}
112
113impl Span {
114 #[must_use]
116 pub const fn new(start: usize, end: usize) -> Self {
117 Self { start, end }
118 }
119
120 #[must_use]
122 pub const fn is_empty(&self) -> bool {
123 self.start >= self.end
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct Diagnostic {
129 pub rule: String,
131 pub message: String,
132 pub span: Span,
133 pub enforced: bool,
135}
136
137pub type Diagnostics = Vec<Diagnostic>;
138
139pub trait Decree: Send + Sync {
142 #[must_use]
144 fn name(&self) -> &str;
145
146 fn lint(&self, path: &str, source: &str) -> Diagnostics;
148
149 #[must_use]
151 fn metadata(&self) -> DecreeMetadata;
152
153 #[must_use]
155 fn rule(&self, rule_name: &str) -> String {
156 format!("{}/{}", self.name(), rule_name)
157 }
158}
159
160pub type BoxDecree = Box<dyn Decree>;
162
163pub type DecreeFactory = fn() -> BoxDecree;
165
166pub const DECREE_FACTORY_EXPORT: &str = "dictator_create_decree";