code_moniker_core/declare/
mod.rs1use crate::core::code_graph::CodeGraph;
2use crate::core::moniker::Moniker;
3use crate::core::uri::{UriConfig, UriError, from_uri};
4
5mod build;
6mod parse;
7mod serialize;
8
9pub use crate::lang::Lang;
10pub use build::build_graph;
11pub use parse::parse_spec;
12pub use serialize::{SerializeError, graph_to_spec};
13
14pub(crate) fn parse_moniker_uri(uri: &str) -> Result<Moniker, UriError> {
15 let scheme_end = uri.find("://").ok_or(UriError::MissingProject)?;
16 from_uri(
17 uri,
18 &UriConfig {
19 scheme: &uri[..scheme_end + 3],
20 },
21 )
22}
23
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub struct DeclareSpec {
26 pub root: Moniker,
27 pub lang: Lang,
28 pub symbols: Vec<DeclSymbol>,
29 pub edges: Vec<DeclEdge>,
30}
31
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub struct DeclSymbol {
34 pub moniker: Moniker,
35 pub kind: String,
36 pub parent: Moniker,
37 pub visibility: Option<String>,
38 pub signature: Option<String>,
39}
40
41#[derive(Clone, Debug, Eq, PartialEq)]
42pub struct DeclEdge {
43 pub from: Moniker,
44 pub kind: EdgeKind,
45 pub to: Moniker,
46}
47
48#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
49pub enum EdgeKind {
50 DependsOn,
51 Calls,
52 InjectsProvide,
53 InjectsRequire,
54}
55
56impl EdgeKind {
57 pub fn from_tag(s: &str) -> Option<Self> {
58 match s {
59 "depends_on" => Some(Self::DependsOn),
60 "calls" => Some(Self::Calls),
61 "injects:provide" => Some(Self::InjectsProvide),
62 "injects:require" => Some(Self::InjectsRequire),
63 _ => None,
64 }
65 }
66
67 pub fn tag(self) -> &'static str {
68 match self {
69 Self::DependsOn => "depends_on",
70 Self::Calls => "calls",
71 Self::InjectsProvide => "injects:provide",
72 Self::InjectsRequire => "injects:require",
73 }
74 }
75}
76
77#[derive(Clone, Debug, Eq, PartialEq)]
78pub enum DeclareError {
79 JsonParse(String),
80 NotAnObject(&'static str),
81 MissingField {
82 path: String,
83 field: &'static str,
84 },
85 InvalidType {
86 path: String,
87 expected: &'static str,
88 },
89 UnknownLang(String),
90 UnknownEdgeKind(String),
91 InvalidMoniker {
92 path: String,
93 value: String,
94 reason: String,
95 },
96 KindNotInProfile {
97 lang: &'static str,
98 kind: String,
99 },
100 VisibilityNotInProfile {
101 lang: &'static str,
102 visibility: String,
103 },
104 KindMismatchMoniker {
105 path: String,
106 declared_kind: String,
107 moniker_last_kind: String,
108 },
109 DuplicateMoniker {
110 moniker: String,
111 },
112 UnknownParent {
113 path: String,
114 parent: String,
115 },
116 UnknownEdgeSource {
117 path: String,
118 from: String,
119 },
120 LangMismatch {
121 expected: &'static str,
122 actual: String,
123 },
124 GraphError(String),
125}
126
127impl std::fmt::Display for DeclareError {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 use DeclareError::*;
130 match self {
131 JsonParse(msg) => write!(f, "spec is not valid JSON: {msg}"),
132 NotAnObject(what) => write!(f, "{what} must be a JSON object"),
133 MissingField { path, field } => {
134 write!(f, "{path}: required field `{field}` is missing")
135 }
136 InvalidType { path, expected } => write!(f, "{path}: expected {expected}"),
137 UnknownLang(s) => write!(
138 f,
139 "unknown lang `{s}` (expected ts | rs | java | python | go | cs | sql)"
140 ),
141 UnknownEdgeKind(s) => write!(
142 f,
143 "unknown edge kind `{s}` (expected depends_on | calls | injects:provide | injects:require)"
144 ),
145 InvalidMoniker {
146 path,
147 value,
148 reason,
149 } => write!(f, "{path}: invalid moniker URI `{value}`: {reason}"),
150 KindNotInProfile { lang, kind } => write!(
151 f,
152 "kind `{kind}` is not allowed for lang={lang} (see profile)"
153 ),
154 VisibilityNotInProfile { lang, visibility } => write!(
155 f,
156 "visibility `{visibility}` is not allowed for lang={lang} (see profile)"
157 ),
158 KindMismatchMoniker {
159 path,
160 declared_kind,
161 moniker_last_kind,
162 } => write!(
163 f,
164 "{path}: declared kind `{declared_kind}` does not match the moniker's last segment kind `{moniker_last_kind}`"
165 ),
166 DuplicateMoniker { moniker } => {
167 write!(f, "duplicate moniker in symbols: {moniker}")
168 }
169 UnknownParent { path, parent } => write!(
170 f,
171 "{path}: parent `{parent}` is neither the root nor a previously declared symbol"
172 ),
173 UnknownEdgeSource { path, from } => {
174 write!(f, "{path}: edge `from` `{from}` is not a declared symbol")
175 }
176 LangMismatch { expected, actual } => write!(
177 f,
178 "spec.lang `{actual}` does not match the typed extractor's `{expected}` (use the dynamic-dispatch entry point if you do not know the language ahead of time)"
179 ),
180 GraphError(msg) => write!(f, "graph build error: {msg}"),
181 }
182 }
183}
184
185impl std::error::Error for DeclareError {}
186
187pub fn declare_from_json_str(json: &str) -> Result<CodeGraph, DeclareError> {
188 let value: serde_json::Value =
189 serde_json::from_str(json).map_err(|e| DeclareError::JsonParse(e.to_string()))?;
190 declare_from_json_value(&value)
191}
192
193pub fn declare_from_json_value(json: &serde_json::Value) -> Result<CodeGraph, DeclareError> {
194 let spec = parse_spec(json)?;
195 build_graph(&spec)
196}