1use std::{
58 collections::VecDeque,
59 fmt::{Display, Formatter, Result as FmtResult},
60 marker::PhantomData,
61};
62
63use petgraph::{
64 graph::NodeIndex,
65 visit::{Bfs, EdgeFiltered, EdgeRef, Visitable},
66};
67
68use crate::{
69 ir::{
70 graph::CookedGraph,
71 types::{
72 GraphOperation, GraphParameter, GraphParameterInfo, GraphRequest, GraphResponse,
73 GraphType, OperationId, ParameterStyle,
74 },
75 },
76 parse::{
77 Method,
78 path::{PathQueryParameter, PathRuns, PathSegment},
79 },
80};
81
82use super::{HasResource, View, inline::InlineTypeView, ir::TypeView};
83
84#[derive(Debug)]
86pub struct OperationView<'graph, 'a> {
87 cooked: &'graph CookedGraph<'a>,
88 op: &'graph GraphOperation<'a>,
89}
90
91impl<'graph, 'a> OperationView<'graph, 'a> {
92 #[inline]
93 pub(in crate::ir) fn new(
94 cooked: &'graph CookedGraph<'a>,
95 op: &'graph GraphOperation<'a>,
96 ) -> Self {
97 Self { cooked, op }
98 }
99
100 #[inline]
102 pub fn id(&self) -> &'a OperationId {
103 OperationId::new(self.op.id)
104 }
105
106 #[inline]
108 pub fn method(&self) -> Method {
109 self.op.method
110 }
111
112 #[inline]
114 pub fn path(&self) -> OperationViewPath<'_, 'graph, 'a> {
115 OperationViewPath(self)
116 }
117
118 #[inline]
120 pub fn description(&self) -> Option<&'a str> {
121 self.op.description
122 }
123
124 #[inline]
126 pub fn query(&self) -> impl Iterator<Item = ParameterView<'_, 'graph, 'a, QueryParameter>> {
127 self.op.params.iter().filter_map(|param| match param {
128 GraphParameter::Query(info) => Some(ParameterView::new(self, info)),
129 _ => None,
130 })
131 }
132
133 #[inline]
135 pub fn request(&self) -> Option<RequestView<'graph, 'a>> {
136 self.op.request.as_ref().map(|ty| match ty {
137 GraphRequest::Json(index) => RequestView::Json(TypeView::new(self.cooked, *index)),
138 GraphRequest::Multipart => RequestView::Multipart,
139 })
140 }
141
142 #[inline]
144 pub fn response(&self) -> Option<ResponseView<'graph, 'a>> {
145 self.op.response.as_ref().map(|ty| match ty {
146 GraphResponse::Json(index) => ResponseView::Json(TypeView::new(self.cooked, *index)),
147 })
148 }
149}
150
151impl<'a> HasResource<'a> for OperationView<'_, 'a> {
152 #[inline]
155 fn resource(&self) -> Option<&'a str> {
156 self.op.resource
157 }
158}
159
160impl<'graph, 'a> View<'graph, 'a> for OperationView<'graph, 'a> {
161 #[inline]
164 fn inlines(&self) -> impl Iterator<Item = InlineTypeView<'graph, 'a>> + use<'graph, 'a> {
165 let cooked = self.cooked;
166 let filtered = EdgeFiltered::from_fn(&cooked.graph, |e| {
169 !e.weight().shadow() && matches!(cooked.graph[e.target()], GraphType::Inline(_))
170 });
171 let mut bfs = {
172 let stack: VecDeque<_> = self
173 .op
174 .types()
175 .copied()
176 .filter(|&index| {
177 matches!(cooked.graph[index], GraphType::Inline(_))
181 })
182 .collect();
183 let mut discovered = self.cooked.graph.visit_map();
184 discovered.extend(stack.iter().copied().map(NodeIndex::index));
185 Bfs { stack, discovered }
186 };
187 std::iter::from_fn(move || bfs.next(&filtered)).filter_map(|index| {
190 match cooked.graph[index] {
191 GraphType::Inline(ty) => Some(InlineTypeView::new(cooked, index, ty)),
192 _ => None,
193 }
194 })
195 }
196
197 #[inline]
200 fn used_by(&self) -> impl Iterator<Item = OperationView<'graph, 'a>> + use<'graph, 'a> {
201 std::iter::empty()
202 }
203
204 #[inline]
205 fn dependencies(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'graph, 'a> {
206 let cooked = self.cooked;
207 cooked.metadata.uses[self.op]
208 .ones()
209 .map(NodeIndex::new)
210 .map(|index| TypeView::new(cooked, index))
211 }
212
213 #[inline]
215 fn dependents(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'graph, 'a> {
216 std::iter::empty()
217 }
218
219 #[inline]
220 fn hashable(&self) -> bool {
221 false
222 }
223
224 #[inline]
225 fn defaultable(&self) -> bool {
226 false
227 }
228}
229
230#[derive(Clone, Copy, Debug)]
232pub struct OperationViewPath<'view, 'graph, 'a>(&'view OperationView<'graph, 'a>);
233
234impl<'view, 'graph, 'a> OperationViewPath<'view, 'graph, 'a> {
235 #[inline]
237 pub fn segments(self) -> std::slice::Iter<'view, PathSegment<'a>> {
238 self.0.op.path.segments.iter()
239 }
240
241 #[inline]
243 pub fn runs(self) -> PathRuns<'view, 'a> {
244 self.0.op.path.runs()
245 }
246
247 #[inline]
250 pub fn query(self) -> std::slice::Iter<'view, PathQueryParameter<'a>> {
251 self.0.op.path.query.iter()
252 }
253
254 #[inline]
256 pub fn params(self) -> impl Iterator<Item = ParameterView<'view, 'graph, 'a, PathParameter>> {
257 self.0.op.params.iter().filter_map(|param| match param {
258 GraphParameter::Path(info) => Some(ParameterView::new(self.0, info)),
259 _ => None,
260 })
261 }
262}
263
264impl Display for OperationViewPath<'_, '_, '_> {
265 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
266 write!(f, "{}", self.0.op.path)
267 }
268}
269
270#[derive(Debug)]
272pub struct ParameterView<'view, 'graph, 'a, T> {
273 op: &'view OperationView<'graph, 'a>,
274 info: &'a GraphParameterInfo<'a>,
275 phantom: PhantomData<T>,
276}
277
278impl<'view, 'graph, 'a, T> ParameterView<'view, 'graph, 'a, T> {
279 #[inline]
280 pub(in crate::ir) fn new(
281 op: &'view OperationView<'graph, 'a>,
282 info: &'a GraphParameterInfo<'a>,
283 ) -> Self {
284 Self {
285 op,
286 info,
287 phantom: PhantomData,
288 }
289 }
290
291 #[inline]
293 pub fn name(&self) -> &'a str {
294 self.info.name
295 }
296
297 #[inline]
299 pub fn ty(&self) -> TypeView<'graph, 'a> {
300 TypeView::new(self.op.cooked, self.info.ty)
301 }
302
303 #[inline]
305 pub fn required(&self) -> bool {
306 self.info.required
307 }
308
309 #[inline]
311 pub fn style(&self) -> Option<ParameterStyle> {
312 self.info.style
313 }
314}
315
316impl<'view, 'graph, 'a, T> View<'graph, 'a> for ParameterView<'view, 'graph, 'a, T> {
317 fn inlines(
318 &self,
319 ) -> impl Iterator<Item = InlineTypeView<'graph, 'a>> + use<'view, 'graph, 'a, T> {
320 let cooked = self.op.cooked;
321 let start = self.info.ty;
322 let filtered = EdgeFiltered::from_fn(&cooked.graph, |e| {
325 !e.weight().shadow() && matches!(cooked.graph[e.target()], GraphType::Inline(_))
326 });
327 let mut bfs = {
328 let stack = match cooked.graph[start] {
332 GraphType::Inline(_) => std::iter::once(start).collect(),
333 _ => VecDeque::new(),
334 };
335 let mut discovered = cooked.graph.visit_map();
336 discovered.extend(stack.iter().copied().map(NodeIndex::index));
337 Bfs { stack, discovered }
338 };
339 std::iter::from_fn(move || bfs.next(&filtered)).filter_map(|index| {
342 match cooked.graph[index] {
343 GraphType::Inline(ty) => Some(InlineTypeView::new(cooked, index, ty)),
344 _ => None,
345 }
346 })
347 }
348
349 fn used_by(
350 &self,
351 ) -> impl Iterator<Item = OperationView<'graph, 'a>> + use<'view, 'graph, 'a, T> {
352 std::iter::once(OperationView::new(self.op.cooked, self.op.op))
353 }
354
355 fn dependencies(
356 &self,
357 ) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'view, 'graph, 'a, T> {
358 let cooked = self.op.cooked;
359 cooked
360 .metadata
361 .closure
362 .dependencies_of(self.info.ty)
363 .map(|index| TypeView::new(cooked, index))
364 }
365
366 fn dependents(&self) -> impl Iterator<Item = TypeView<'graph, 'a>> + use<'view, 'graph, 'a, T> {
368 std::iter::empty()
369 }
370
371 #[inline]
372 fn hashable(&self) -> bool {
373 self.op.cooked.metadata.hashable[self.info.ty.index()]
374 }
375
376 #[inline]
377 fn defaultable(&self) -> bool {
378 self.op.cooked.metadata.defaultable[self.info.ty.index()]
379 }
380}
381
382#[derive(Clone, Copy, Debug)]
384pub enum PathParameter {}
385
386#[derive(Clone, Copy, Debug)]
388pub enum QueryParameter {}
389
390#[derive(Debug)]
392pub enum RequestView<'graph, 'a> {
393 Json(TypeView<'graph, 'a>),
394 Multipart,
395}
396
397#[derive(Debug)]
399pub enum ResponseView<'graph, 'a> {
400 Json(TypeView<'graph, 'a>),
401}