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}