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 #[serde(default)]
42 pub supported_filenames: Vec<String>,
43 #[serde(default)]
46 pub skip_filenames: Vec<String>,
47 pub capabilities: Vec<Capability>,
49}
50
51impl DecreeMetadata {
52 #[must_use]
54 pub fn has_capability(&self, cap: Capability) -> bool {
55 self.capabilities.contains(&cap)
56 }
57
58 pub fn parse_version(version: &str) -> Result<(u32, u32, u32), String> {
65 let parts: Vec<&str> = version.split('.').collect();
66 if parts.len() != 3 {
67 return Err(format!("invalid version format: {version}"));
68 }
69 let major = parts[0]
70 .parse()
71 .map_err(|_| format!("invalid major: {}", parts[0]))?;
72 let minor = parts[1]
73 .parse()
74 .map_err(|_| format!("invalid minor: {}", parts[1]))?;
75 let patch = parts[2]
76 .parse()
77 .map_err(|_| format!("invalid patch: {}", parts[2]))?;
78 Ok((major, minor, patch))
79 }
80
81 pub fn validate_abi(&self, host_abi_version: &str) -> Result<(), String> {
87 let (host_maj, host_min, _) = Self::parse_version(host_abi_version)?;
88 let (decree_maj, decree_min, _) = Self::parse_version(&self.abi_version)?;
89
90 if host_maj == 0 {
92 if host_maj == decree_maj && host_min == decree_min {
93 return Ok(());
94 }
95 return Err(format!(
96 "ABI version mismatch: host {}, decree {}",
97 host_abi_version, self.abi_version
98 ));
99 }
100
101 if host_maj == decree_maj && decree_min <= host_min {
103 return Ok(());
104 }
105
106 Err(format!(
107 "ABI version incompatible: host {}, decree {}",
108 host_abi_version, self.abi_version
109 ))
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
115pub struct Span {
116 pub start: usize,
117 pub end: usize,
118}
119
120impl Span {
121 #[must_use]
123 pub const fn new(start: usize, end: usize) -> Self {
124 Self { start, end }
125 }
126
127 #[must_use]
129 pub const fn is_empty(&self) -> bool {
130 self.start >= self.end
131 }
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct Diagnostic {
136 pub rule: String,
138 pub message: String,
139 pub span: Span,
140 pub enforced: bool,
142}
143
144pub type Diagnostics = Vec<Diagnostic>;
145
146pub trait Decree: Send + Sync {
149 #[must_use]
151 fn name(&self) -> &str;
152
153 fn lint(&self, path: &str, source: &str) -> Diagnostics;
155
156 #[must_use]
158 fn metadata(&self) -> DecreeMetadata;
159
160 #[must_use]
162 fn rule(&self, rule_name: &str) -> String {
163 format!("{}/{}", self.name(), rule_name)
164 }
165}
166
167pub type BoxDecree = Box<dyn Decree>;
169
170pub type DecreeFactory = fn() -> BoxDecree;
172
173pub const DECREE_FACTORY_EXPORT: &str = "dictator_create_decree";