clawspec_core/split/splitter.rs
1//! The OpenApiSplitter trait for splitting OpenAPI specifications.
2
3use serde::Serialize;
4use utoipa::openapi::OpenApi;
5
6use super::SplitResult;
7
8/// Trait for splitting an OpenAPI specification into multiple files.
9///
10/// Implementations of this trait define different strategies for organizing
11/// schemas and other components into separate files for better modularity.
12///
13/// # Type Parameters
14///
15/// The associated type `Fragment` defines what type of content is extracted
16/// into separate files. Common choices include:
17///
18/// * [`Components`](utoipa::openapi::Components) - Extract just the components section
19/// * [`OpenApi`] - Extract complete sub-specifications
20/// * Custom types implementing [`Serialize`]
21///
22/// # Implementing Custom Splitters
23///
24/// ```rust,ignore
25/// use clawspec_core::split::{OpenApiSplitter, SplitResult, Fragment};
26/// use utoipa::openapi::{Components, OpenApi};
27/// use std::path::PathBuf;
28///
29/// struct MyCustomSplitter {
30/// output_dir: PathBuf,
31/// }
32///
33/// impl OpenApiSplitter for MyCustomSplitter {
34/// type Fragment = Components;
35///
36/// fn split(&self, spec: OpenApi) -> SplitResult<Self::Fragment> {
37/// // Analyze the spec and extract schemas
38/// // Update refs in main spec to point to external files
39/// // Return the modified main spec and fragments
40/// todo!()
41/// }
42/// }
43/// ```
44///
45/// # Built-in Implementations
46///
47/// * [`SplitSchemasByTag`](super::SplitSchemasByTag) - Split schemas based on which tags use them
48/// * [`ExtractSchemasByPredicate`](super::ExtractSchemasByPredicate) - Extract schemas matching a predicate
49pub trait OpenApiSplitter {
50 /// The type of content extracted into fragments.
51 type Fragment: Serialize;
52
53 /// Splits the OpenAPI specification into a main spec and fragments.
54 ///
55 /// This method consumes the input specification and returns:
56 /// - A modified main spec with `$ref` pointing to external files
57 /// - A collection of fragments to be written to separate files
58 ///
59 /// # Arguments
60 ///
61 /// * `spec` - The OpenAPI specification to split
62 ///
63 /// # Returns
64 ///
65 /// A [`SplitResult`] containing the main spec and extracted fragments.
66 fn split(&self, spec: OpenApi) -> SplitResult<Self::Fragment>;
67}
68
69/// Extension trait for convenient splitting of OpenAPI specifications.
70pub trait OpenApiSplitExt {
71 /// Splits this specification using the provided splitter.
72 ///
73 /// This is a convenience method that calls `splitter.split(self)`.
74 ///
75 /// # Example
76 ///
77 /// ```rust,ignore
78 /// use clawspec_core::split::{OpenApiSplitExt, SplitSchemasByTag};
79 ///
80 /// let spec: OpenApi = /* ... */;
81 /// let result = spec.split_with(SplitSchemasByTag::new("common.yaml"));
82 /// ```
83 fn split_with<S: OpenApiSplitter>(self, splitter: S) -> SplitResult<S::Fragment>;
84}
85
86impl OpenApiSplitExt for OpenApi {
87 fn split_with<S: OpenApiSplitter>(self, splitter: S) -> SplitResult<S::Fragment> {
88 splitter.split(self)
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use utoipa::openapi::OpenApiBuilder;
96
97 struct NoOpSplitter;
98
99 impl OpenApiSplitter for NoOpSplitter {
100 type Fragment = String;
101
102 fn split(&self, spec: OpenApi) -> SplitResult<Self::Fragment> {
103 SplitResult::new(spec)
104 }
105 }
106
107 #[test]
108 fn should_implement_split_ext() {
109 let spec = OpenApiBuilder::new().build();
110 let result = spec.split_with(NoOpSplitter);
111
112 assert!(result.is_unsplit());
113 }
114}