1#![allow(dead_code)]
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7pub struct SourceLoc {
8 pub file: String,
9 pub offset: usize,
10 pub line: u32,
11}
12
13impl SourceLoc {
14 pub fn from_offset(file: impl Into<String>, offset: usize, source: &str) -> Self {
16 let line = source[..offset.min(source.len())]
17 .chars()
18 .filter(|&c| c == '\n')
19 .count() as u32
20 + 1;
21 SourceLoc {
22 file: file.into(),
23 offset,
24 line,
25 }
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
31#[serde(rename_all = "snake_case")]
32pub enum NodeKind {
33 Function,
34 Alias,
35 TypeDef,
36 Module,
37}
38
39impl NodeKind {
40 pub fn prefix(&self) -> &'static str {
41 match self {
42 NodeKind::Function => "function",
43 NodeKind::Alias => "alias",
44 NodeKind::TypeDef => "type",
45 NodeKind::Module => "module",
46 }
47 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
52#[serde(rename_all = "snake_case")]
53pub enum Visibility {
54 Private,
55 Public,
56 Export,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
61#[serde(tag = "kind", rename_all = "snake_case")]
62pub enum NodePayload {
63 Function {
64 params: Vec<(String, String)>,
66 returns: String,
67 },
68 Record {
69 fields: Vec<(String, String)>,
71 },
72 Alias {
73 underlying: String,
74 opaque: bool,
75 },
76 Union {
77 variants: Vec<(String, Option<String>)>,
78 },
79 Module {
80 exports: Vec<String>,
81 },
82 None,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
87pub struct Node {
88 pub id: String,
90 pub kind: NodeKind,
91 pub name: String,
92 pub module_path: Vec<String>,
94 pub visibility: Visibility,
95 pub doc: Option<String>,
97 pub source: Option<SourceLoc>,
98 pub payload: NodePayload,
99}
100
101impl Node {
102 pub fn make_id(kind: &NodeKind, module_path: &[String], name: &str) -> String {
104 if module_path.is_empty() {
105 format!("{}:{}", kind.prefix(), name)
106 } else {
107 format!("{}:{}/{}", kind.prefix(), module_path.join("/"), name)
108 }
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
114#[serde(rename_all = "kebab-case")]
115pub enum EdgeKind {
116 HasField,
117 Returns,
118 ProducesType,
119 ParameterOf,
120 ConsumesType,
121 BelongsToModule,
122 Uses,
123 UsedBy,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
128pub struct Edge {
129 pub from: String,
130 pub to: String,
131 pub kind: EdgeKind,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct Spg {
137 #[serde(rename = "@context")]
139 pub context: String,
140 pub package: String,
141 pub version: String,
142 pub nodes: Vec<Node>,
143 pub edges: Vec<Edge>,
144}
145
146impl Spg {
147 pub fn new(package: impl Into<String>, version: impl Into<String>) -> Self {
148 Spg {
149 context: "https://typr-lang.dev/spg/v1/context.jsonld".into(),
150 package: package.into(),
151 version: version.into(),
152 nodes: Vec::new(),
153 edges: Vec::new(),
154 }
155 }
156
157 pub fn add_node(&mut self, node: Node) {
158 self.nodes.push(node);
159 }
160
161 pub fn add_edge(&mut self, edge: Edge) {
162 self.edges.push(edge);
163 }
164}