fallow_api/
list_output.rs1use fallow_output::{ListEntryPointOutput, ListOutput, ListPluginOutput, WorkspacesOutput};
4use serde::Serialize;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ListJsonEnvelope {
9 Plain,
11 Boundaries,
13 Workspaces,
15}
16
17pub struct ListJsonOutputInput<Boundaries, Diagnostic> {
19 pub plugins: Option<Vec<String>>,
21 pub files: Option<Vec<String>>,
23 pub entry_points: Option<Vec<ListEntryPointOutput>>,
26 pub boundaries: Option<Boundaries>,
28 pub workspaces: Option<WorkspacesOutput<Diagnostic>>,
31 pub plugin_diagnostics: Vec<Diagnostic>,
37}
38
39#[must_use]
41pub fn build_list_json_output<Boundaries, Diagnostic>(
42 input: ListJsonOutputInput<Boundaries, Diagnostic>,
43) -> ListOutput<Boundaries, Diagnostic> {
44 let plugins = input.plugins.map(|plugins| {
45 plugins
46 .into_iter()
47 .map(|name| ListPluginOutput { name })
48 .collect()
49 });
50 let file_count = input.files.as_ref().map(Vec::len);
51 let entry_point_count = input.entry_points.as_ref().map(Vec::len);
52 let (workspace_count, workspaces, mut workspace_diagnostics) =
53 input.workspaces.map_or((None, None, None), |workspaces| {
54 (
55 Some(workspaces.workspace_count),
56 Some(workspaces.workspaces),
57 Some(workspaces.workspace_diagnostics),
58 )
59 });
60 if !input.plugin_diagnostics.is_empty() {
61 workspace_diagnostics
62 .get_or_insert_with(Vec::new)
63 .extend(input.plugin_diagnostics);
64 }
65
66 ListOutput {
67 plugins,
68 file_count,
69 files: input.files,
70 entry_point_count,
71 entry_points: input.entry_points,
72 boundaries: input.boundaries,
73 workspace_count,
74 workspaces,
75 workspace_diagnostics,
76 }
77}
78
79pub fn serialize_list_json_output<Boundaries, Diagnostic>(
86 input: ListJsonOutputInput<Boundaries, Diagnostic>,
87 envelope: ListJsonEnvelope,
88) -> Result<serde_json::Value, serde_json::Error>
89where
90 Boundaries: Serialize,
91 Diagnostic: Serialize,
92{
93 let output = build_list_json_output(input);
94 match envelope {
95 ListJsonEnvelope::Plain => serde_json::to_value(output),
96 ListJsonEnvelope::Boundaries => {
97 fallow_output::serialize_list_boundaries_json_output(output)
98 }
99 ListJsonEnvelope::Workspaces => {
100 fallow_output::serialize_list_workspaces_json_output(output)
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use fallow_output::ListEntryPointOutput;
108 use serde_json::json;
109
110 use super::*;
111
112 #[test]
113 fn list_json_output_preserves_plain_legacy_body() {
114 let value = serialize_list_json_output::<serde_json::Value, serde_json::Value>(
115 ListJsonOutputInput {
116 plugins: Some(vec!["react".to_string()]),
117 files: Some(vec!["src/index.ts".to_string()]),
118 entry_points: Some(vec![ListEntryPointOutput {
119 path: "src/index.ts".to_string(),
120 source: "package.json main".to_string(),
121 }]),
122 boundaries: None,
123 workspaces: None,
124 plugin_diagnostics: Vec::new(),
125 },
126 ListJsonEnvelope::Plain,
127 )
128 .expect("list output should serialize");
129
130 assert_eq!(value["plugins"][0]["name"], "react");
131 assert_eq!(value["file_count"], 1);
132 assert_eq!(value["files"], json!(["src/index.ts"]));
133 assert_eq!(value["entry_point_count"], 1);
134 assert!(value.get("kind").is_none());
135 }
136
137 #[test]
138 fn list_json_output_wraps_boundary_payloads() {
139 let value = serialize_list_json_output::<serde_json::Value, serde_json::Value>(
140 ListJsonOutputInput {
141 plugins: None,
142 files: None,
143 entry_points: None,
144 boundaries: Some(json!({"configured": false})),
145 workspaces: None,
146 plugin_diagnostics: Vec::new(),
147 },
148 ListJsonEnvelope::Boundaries,
149 )
150 .expect("list output should serialize");
151
152 assert_eq!(value["kind"], "list-boundaries");
153 assert_eq!(value["boundaries"]["configured"], false);
154 }
155}