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