1use std::{collections::VecDeque, marker::PhantomData};
58
59use petgraph::{
60 graph::NodeIndex,
61 visit::{Bfs, EdgeFiltered, EdgeRef, Visitable},
62};
63
64use crate::{
65 ir::{
66 graph::CookedGraph,
67 types::{
68 GraphOperation, GraphParameter, GraphParameterInfo, GraphRequest, GraphResponse,
69 GraphType, ParameterStyle,
70 },
71 },
72 parse::{
73 Method,
74 path::{PathQueryParameter, PathSegment},
75 },
76};
77
78use super::{View, inline::InlineTypeView, ir::TypeView};
79
80#[derive(Debug)]
82pub struct OperationView<'graph, 'a> {
83 cooked: &'graph CookedGraph<'a>,
84 op: &'graph GraphOperation<'a>,
85}
86
87impl<'graph, 'a> OperationView<'graph, 'a> {
88 #[inline]
89 pub(in crate::ir) fn new(
90 cooked: &'graph CookedGraph<'a>,
91 op: &'graph GraphOperation<'a>,
92 ) -> Self {
93 Self { cooked, op }
94 }
95
96 #[inline]
98 pub fn id(&self) -> &'a str {
99 self.op.id
100 }
101
102 #[inline]
104 pub fn method(&self) -> Method {
105 self.op.method
106 }
107
108 #[inline]
110 pub fn path(&self) -> OperationViewPath<'_, 'graph, 'a> {
111 OperationViewPath(self)
112 }
113
114 #[inline]
116 pub fn description(&self) -> Option<&'a str> {
117 self.op.description
118 }
119
120 #[inline]
122 pub fn query(&self) -> impl Iterator<Item = ParameterView<'_, 'graph, 'a, QueryParameter>> {
123 self.op.params.iter().filter_map(|param| match param {
124 GraphParameter::Query(info) => Some(ParameterView::new(self, info)),
125 _ => None,
126 })
127 }
128
129 #[inline]
131 pub fn request(&self) -> Option<RequestView<'graph, 'a>> {
132 self.op.request.as_ref().map(|ty| match ty {
133 GraphRequest::Json(index) => RequestView::Json(TypeView::new(self.cooked, *index)),
134 GraphRequest::Multipart => RequestView::Multipart,
135 })
136 }
137
138 #[inline]
140 pub fn response(&self) -> Option<ResponseView<'graph, 'a>> {
141 self.op.response.as_ref().map(|ty| match ty {
142 GraphResponse::Json(index) => ResponseView::Json(TypeView::new(self.cooked, *index)),
143 })
144 }
145
146 #[inline]
149 pub fn resource(&self) -> Option<&'a str> {
150 self.op.resource
151 }
152}
153
154impl<'graph, 'a> View<'graph, 'a> for OperationView<'graph, 'a> {
155 #[inline]
158 fn inlines(&self) -> impl Iterator<Item = InlineTypeView<'graph, 'a>> + use<'graph, 'a> {
159 let cooked = self.cooked;
160 let filtered = EdgeFiltered::from_fn(&cooked.graph, |e| {
163 !e.weight().shadow() && matches!(cooked.graph[e.target()], GraphType::Inline(_))
164 });
165 let mut bfs = {
166 let stack: VecDeque<_> = self
167 .op
168 .types()
169 .copied()
170 .filter(|&index| {
171 matches!(cooked.graph[index], GraphType::Inline(_))
175 })
176 .collect();
177 let mut discovered = self.cooked.graph.visit_map();
178 discovered.extend(stack.iter().copied().map(NodeIndex::index));
179 Bfs { stack, discovered }
180 };
181 std::iter::from_fn(move || bfs.next(&filtered)).filter_map(|index| {
184 match cooked.graph[index] {
185 GraphType::Inline(ty) => Some(InlineTypeView::new(cooked, index, ty)),
186 _ => None,
187 }
188 })
189 }
190
191 #[inline]
194 fn used_by(&self) -> impl Iterator<Item = OperationView<'graph, 'a>> + use<'graph, 'a> {
195 std::iter::empty()
196 }
197
198 #[inline]
199 fn dependencies(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'graph, 'a> {
200 let cooked = self.cooked;
201 cooked.metadata.uses[self.op]
202 .ones()
203 .map(NodeIndex::new)
204 .map(|index| TypeView::new(cooked, index))
205 }
206
207 #[inline]
209 fn dependents(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'graph, 'a> {
210 std::iter::empty()
211 }
212
213 #[inline]
214 fn hashable(&self) -> bool {
215 false
216 }
217
218 #[inline]
219 fn defaultable(&self) -> bool {
220 false
221 }
222}
223
224#[derive(Clone, Copy, Debug)]
226pub struct OperationViewPath<'view, 'graph, 'a>(&'view OperationView<'graph, 'a>);
227
228impl<'view, 'graph, 'a> OperationViewPath<'view, 'graph, 'a> {
229 #[inline]
231 pub fn segments(self) -> std::slice::Iter<'view, PathSegment<'a>> {
232 self.0.op.path.segments.iter()
233 }
234
235 #[inline]
238 pub fn query(self) -> std::slice::Iter<'view, PathQueryParameter<'a>> {
239 self.0.op.path.query.iter()
240 }
241
242 #[inline]
244 pub fn params(self) -> impl Iterator<Item = ParameterView<'view, 'graph, 'a, PathParameter>> {
245 self.0.op.params.iter().filter_map(|param| match param {
246 GraphParameter::Path(info) => Some(ParameterView::new(self.0, info)),
247 _ => None,
248 })
249 }
250}
251
252#[derive(Debug)]
254pub struct ParameterView<'view, 'graph, 'a, T> {
255 op: &'view OperationView<'graph, 'a>,
256 info: &'a GraphParameterInfo<'a>,
257 phantom: PhantomData<T>,
258}
259
260impl<'view, 'graph, 'a, T> ParameterView<'view, 'graph, 'a, T> {
261 #[inline]
262 pub(in crate::ir) fn new(
263 op: &'view OperationView<'graph, 'a>,
264 info: &'a GraphParameterInfo<'a>,
265 ) -> Self {
266 Self {
267 op,
268 info,
269 phantom: PhantomData,
270 }
271 }
272
273 #[inline]
275 pub fn name(&self) -> &'a str {
276 self.info.name
277 }
278
279 #[inline]
281 pub fn ty(&self) -> TypeView<'graph, 'a> {
282 TypeView::new(self.op.cooked, self.info.ty)
283 }
284
285 #[inline]
287 pub fn required(&self) -> bool {
288 self.info.required
289 }
290
291 #[inline]
293 pub fn style(&self) -> Option<ParameterStyle> {
294 self.info.style
295 }
296}
297
298impl<'view, 'graph, 'a, T> View<'graph, 'a> for ParameterView<'view, 'graph, 'a, T> {
299 fn inlines(
300 &self,
301 ) -> impl Iterator<Item = InlineTypeView<'graph, 'a>> + use<'view, 'graph, 'a, T> {
302 let cooked = self.op.cooked;
303 let start = self.info.ty;
304 let filtered = EdgeFiltered::from_fn(&cooked.graph, |e| {
307 !e.weight().shadow() && matches!(cooked.graph[e.target()], GraphType::Inline(_))
308 });
309 let mut bfs = {
310 let stack = match cooked.graph[start] {
314 GraphType::Inline(_) => std::iter::once(start).collect(),
315 _ => VecDeque::new(),
316 };
317 let mut discovered = cooked.graph.visit_map();
318 discovered.extend(stack.iter().copied().map(NodeIndex::index));
319 Bfs { stack, discovered }
320 };
321 std::iter::from_fn(move || bfs.next(&filtered)).filter_map(|index| {
324 match cooked.graph[index] {
325 GraphType::Inline(ty) => Some(InlineTypeView::new(cooked, index, ty)),
326 _ => None,
327 }
328 })
329 }
330
331 fn used_by(
332 &self,
333 ) -> impl Iterator<Item = OperationView<'graph, 'a>> + use<'view, 'graph, 'a, T> {
334 std::iter::once(OperationView::new(self.op.cooked, self.op.op))
335 }
336
337 fn dependencies(
338 &self,
339 ) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'view, 'graph, 'a, T> {
340 let cooked = self.op.cooked;
341 cooked
342 .metadata
343 .closure
344 .dependencies_of(self.info.ty)
345 .map(|index| TypeView::new(cooked, index))
346 }
347
348 fn dependents(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'view, 'graph, 'a, T> {
350 std::iter::empty()
351 }
352
353 #[inline]
354 fn hashable(&self) -> bool {
355 self.op.cooked.metadata.hashable[self.info.ty.index()]
356 }
357
358 #[inline]
359 fn defaultable(&self) -> bool {
360 self.op.cooked.metadata.defaultable[self.info.ty.index()]
361 }
362}
363
364#[derive(Clone, Copy, Debug)]
366pub enum PathParameter {}
367
368#[derive(Clone, Copy, Debug)]
370pub enum QueryParameter {}
371
372#[derive(Debug)]
374pub enum RequestView<'graph, 'a> {
375 Json(TypeView<'graph, 'a>),
376 Multipart,
377}
378
379#[derive(Debug)]
381pub enum ResponseView<'graph, 'a> {
382 Json(TypeView<'graph, 'a>),
383}