Skip to main content

webspec_index/
model.rs

1use serde::{Deserialize, Serialize};
2
3/// Options for PR-aware queries.
4#[derive(Debug, Clone)]
5pub struct PrOpts {
6    pub pr_number: i64,
7    pub force_update: bool,
8}
9
10/// Type of a section
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "lowercase")]
13pub enum SectionType {
14    Heading,
15    Algorithm,
16    Definition,
17    Idl,
18    Prose,
19}
20
21impl SectionType {
22    pub fn as_str(&self) -> &'static str {
23        match self {
24            SectionType::Heading => "heading",
25            SectionType::Algorithm => "algorithm",
26            SectionType::Definition => "definition",
27            SectionType::Idl => "idl",
28            SectionType::Prose => "prose",
29        }
30    }
31}
32
33impl std::str::FromStr for SectionType {
34    type Err = ();
35
36    fn from_str(s: &str) -> Result<Self, Self::Err> {
37        match s {
38            "heading" => Ok(SectionType::Heading),
39            "algorithm" => Ok(SectionType::Algorithm),
40            "definition" => Ok(SectionType::Definition),
41            "idl" => Ok(SectionType::Idl),
42            "prose" => Ok(SectionType::Prose),
43            _ => Err(()),
44        }
45    }
46}
47
48/// A parsed section from the spec HTML
49#[derive(Debug, Clone)]
50pub struct ParsedSection {
51    pub anchor: String,
52    pub title: Option<String>,
53    pub content_text: Option<String>,
54    pub section_type: SectionType,
55    pub parent_anchor: Option<String>,
56    pub prev_anchor: Option<String>,
57    pub next_anchor: Option<String>,
58    pub depth: Option<u8>, // 2-6 for headings
59}
60
61/// A cross-reference found in the spec
62#[derive(Debug, Clone)]
63pub struct ParsedReference {
64    pub from_anchor: String,
65    pub to_spec: String, // Target spec name (same as source for intra-spec refs)
66    pub to_anchor: String,
67}
68
69/// A parsed WebIDL definition from `dfn[data-dfn-type]`
70#[derive(Debug, Clone)]
71pub struct ParsedIdlDefinition {
72    pub anchor: String,
73    pub name: String,
74    pub owner: Option<String>,
75    pub kind: String,
76    pub canonical_name: String,
77    pub idl_text: Option<String>,
78}
79
80/// Complete parsed spec
81#[derive(Debug)]
82pub struct ParsedSpec {
83    pub sections: Vec<ParsedSection>,
84    pub references: Vec<ParsedReference>,
85    pub idl_definitions: Vec<ParsedIdlDefinition>,
86}
87
88/// JSON output for query command
89#[derive(Debug, Clone, Serialize)]
90pub struct QueryResult {
91    pub spec: String,
92    pub sha: String,
93    pub anchor: String,
94    pub title: Option<String>,
95    #[serde(rename = "type")]
96    pub section_type: String,
97    pub content: Option<String>,
98    pub navigation: Navigation,
99    pub outgoing_refs: Vec<RefEntry>,
100    pub incoming_refs: Vec<RefEntry>,
101}
102
103#[derive(Debug, Clone, Serialize)]
104pub struct Navigation {
105    pub parent: Option<NavEntry>,
106    pub prev: Option<NavEntry>,
107    pub next: Option<NavEntry>,
108    pub children: Vec<NavEntry>,
109}
110
111#[derive(Debug, Clone, Serialize)]
112pub struct NavEntry {
113    pub anchor: String,
114    pub title: Option<String>,
115}
116
117#[derive(Debug, Clone, Serialize)]
118pub struct RefEntry {
119    pub spec: String,
120    pub anchor: String,
121}
122
123/// JSON output for exists command
124#[derive(Debug, Serialize)]
125pub struct ExistsResult {
126    pub exists: bool,
127    pub spec: String,
128    pub anchor: String,
129    #[serde(skip_serializing_if = "Option::is_none")]
130    #[serde(rename = "type")]
131    pub section_type: Option<String>,
132}
133
134/// JSON output for anchors command
135#[derive(Debug, Serialize)]
136pub struct AnchorsResult {
137    pub pattern: String,
138    pub results: Vec<AnchorEntry>,
139}
140
141#[derive(Debug, Serialize)]
142pub struct AnchorEntry {
143    pub spec: String,
144    pub anchor: String,
145    pub title: Option<String>,
146    #[serde(rename = "type")]
147    pub section_type: String,
148}
149
150/// JSON output for search command
151#[derive(Debug, Serialize)]
152pub struct SearchResult {
153    pub query: String,
154    pub results: Vec<SearchEntry>,
155}
156
157#[derive(Debug, Serialize)]
158pub struct SearchEntry {
159    pub spec: String,
160    pub anchor: String,
161    pub title: Option<String>,
162    #[serde(rename = "type")]
163    pub section_type: String,
164    pub snippet: String,
165}
166
167/// JSON output for list command
168#[derive(Debug, Serialize)]
169pub struct ListEntry {
170    pub anchor: String,
171    pub title: Option<String>,
172    pub depth: u8,
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub parent: Option<String>,
175}
176
177/// JSON output for spec_urls command
178#[derive(Debug, Serialize)]
179pub struct SpecUrlEntry {
180    pub spec: String,
181    pub base_url: String,
182}
183
184/// JSON output for update command
185#[derive(Debug, Serialize)]
186pub struct UpdateEntry {
187    pub spec: String,
188    pub updated: bool,
189}
190
191/// JSON output for graph command
192#[derive(Debug, Serialize)]
193pub struct GraphResult {
194    pub root: GraphRoot,
195    pub direction: String,
196    pub max_depth: usize,
197    pub max_nodes: usize,
198    pub truncated: bool,
199    pub nodes: Vec<GraphNode>,
200    pub edges: Vec<GraphEdge>,
201}
202
203#[derive(Debug, Serialize)]
204pub struct GraphRoot {
205    pub spec: String,
206    pub anchor: String,
207}
208
209#[derive(Debug, Serialize)]
210pub struct GraphNode {
211    pub id: String, // "{spec}#{anchor}"
212    pub spec: String,
213    pub anchor: String,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub title: Option<String>,
216    #[serde(skip_serializing_if = "Option::is_none")]
217    #[serde(rename = "type")]
218    pub section_type: Option<String>,
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub filter_role: Option<String>, // root | matched | bridge
221}
222
223#[derive(Debug, Serialize)]
224pub struct GraphEdge {
225    pub from: String, // node id
226    pub to: String,   // node id
227    pub kind: String, // currently always "reference"
228}
229
230/// JSON output for refs command
231#[derive(Debug, Serialize)]
232pub struct RefsResult {
233    pub query: String,
234    pub direction: String,
235    pub matches: Vec<RefsMatch>,
236}
237
238#[derive(Debug, Serialize)]
239pub struct RefsMatch {
240    pub spec: String,
241    pub anchor: String,
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub title: Option<String>,
244    #[serde(rename = "type")]
245    pub section_type: String,
246    pub resolution: String, // exact | heuristic
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub outgoing: Option<Vec<RefEntry>>,
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub incoming: Option<Vec<RefEntry>>,
251}
252
253/// JSON output for idl command
254#[derive(Debug, Serialize)]
255pub struct IdlResult {
256    pub query: String,
257    pub matches: Vec<IdlEntry>,
258}
259
260/// JSON output for pr-diff command
261#[derive(Debug, Serialize)]
262pub struct PrDiffResult {
263    pub spec: String,
264    pub pr_number: i64,
265    pub head_sha: String,
266    pub merge_base_sha: String,
267    pub summary: PrDiffSummary,
268    pub changes: Vec<PrDiffEntry>,
269}
270
271#[derive(Debug, Serialize)]
272pub struct PrDiffSummary {
273    pub added: usize,
274    pub removed: usize,
275    pub modified: usize,
276}
277
278#[derive(Debug, Serialize)]
279pub struct PrDiffEntry {
280    pub anchor: String,
281    #[serde(skip_serializing_if = "Option::is_none")]
282    pub title: Option<String>,
283    pub change_type: String,
284    #[serde(skip_serializing_if = "Option::is_none")]
285    pub old_content: Option<String>,
286    #[serde(skip_serializing_if = "Option::is_none")]
287    pub new_content: Option<String>,
288}
289
290#[derive(Debug, Serialize)]
291pub struct IdlEntry {
292    pub spec: String,
293    pub anchor: String,
294    pub kind: String,
295    pub name: String,
296    #[serde(skip_serializing_if = "Option::is_none")]
297    pub owner: Option<String>,
298    pub canonical_name: String,
299    #[serde(skip_serializing_if = "Option::is_none")]
300    pub title: Option<String>,
301    #[serde(skip_serializing_if = "Option::is_none")]
302    pub idl_text: Option<String>,
303}