1use super::types::{IrSpec, NormalizedName};
2use crate::config::SplitBy;
3use crate::transform::name_normalizer::normalize_name;
4use indexmap::IndexMap;
5
6#[derive(Debug, Clone)]
8pub struct OperationGroup {
9 pub name: NormalizedName,
10 pub operation_indices: Vec<usize>,
11}
12
13pub fn group_operations(ir: &IrSpec, split_by: SplitBy) -> Vec<OperationGroup> {
15 match split_by {
16 SplitBy::Tag => group_by_tag(ir),
17 SplitBy::Operation => group_by_operation(ir),
18 SplitBy::Route => group_by_route(ir),
19 }
20}
21
22fn group_by_tag(ir: &IrSpec) -> Vec<OperationGroup> {
24 ir.modules
25 .iter()
26 .map(|m| OperationGroup {
27 name: m.name.clone(),
28 operation_indices: m.operations.clone(),
29 })
30 .collect()
31}
32
33fn group_by_operation(ir: &IrSpec) -> Vec<OperationGroup> {
35 ir.operations
36 .iter()
37 .enumerate()
38 .map(|(i, op)| OperationGroup {
39 name: op.name.clone(),
40 operation_indices: vec![i],
41 })
42 .collect()
43}
44
45fn group_by_route(ir: &IrSpec) -> Vec<OperationGroup> {
47 let mut groups: IndexMap<String, Vec<usize>> = IndexMap::new();
48
49 for (i, op) in ir.operations.iter().enumerate() {
50 let prefix = extract_path_prefix(&op.path);
51 groups.entry(prefix).or_default().push(i);
52 }
53
54 groups
55 .into_iter()
56 .map(|(prefix, indices)| OperationGroup {
57 name: normalize_name(&prefix),
58 operation_indices: indices,
59 })
60 .collect()
61}
62
63fn extract_path_prefix(path: &str) -> String {
66 let segments: Vec<&str> = path
67 .split('/')
68 .filter(|s| !s.is_empty() && !s.starts_with('{'))
69 .collect();
70
71 segments.first().unwrap_or(&"default").to_string()
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn test_extract_path_prefix() {
80 assert_eq!(extract_path_prefix("/pets"), "pets");
81 assert_eq!(extract_path_prefix("/pets/{petId}"), "pets");
82 assert_eq!(extract_path_prefix("/store/inventory"), "store");
83 assert_eq!(extract_path_prefix("/chat/completions"), "chat");
84 assert_eq!(extract_path_prefix("/"), "default");
85 }
86}