reactive_graph/client/
result.rs1use crate::client::error::CommandError;
2use crate::client::error::CommandError::NotImplemented;
3use crate::shared::output_format::OutputFormatArgs;
4use reactive_graph_serde::error::SerializationError;
5use reactive_graph_table_model::container::DefaultTableContainer;
6use reactive_graph_table_model::container::TableContainer;
7use reactive_graph_table_model::container::TableInlineFormatSetter;
8use reactive_graph_table_model::container::TableOptions;
9use serde::Serialize;
10use serde_json::Value;
11use std::fmt::Display;
12use std::fmt::Formatter;
13use std::marker::PhantomData;
14use tabled::Tabled;
15use toml::map::Map;
16
17pub enum CommandResponse {
18 Message(String),
19 Value(Value),
20 #[cfg(feature = "toml")]
21 TomlValue(toml::Value),
22 Table(Box<dyn TableContainer>),
23}
24
25pub type CommandResult = Result<CommandResponse, CommandError>;
26
27impl From<String> for CommandResponse {
28 fn from(message: String) -> Self {
29 CommandResponse::Message(message)
30 }
31}
32
33impl From<&str> for CommandResponse {
34 fn from(message: &str) -> Self {
35 CommandResponse::Message(message.to_string())
36 }
37}
38
39impl From<Value> for CommandResponse {
40 fn from(value: Value) -> Self {
41 CommandResponse::Value(value)
42 }
43}
44
45#[cfg(feature = "toml")]
46impl From<toml::Value> for CommandResponse {
47 fn from(value: toml::Value) -> Self {
48 CommandResponse::TomlValue(value)
49 }
50}
51
52impl<S: 'static, T: Clone + Tabled + From<S> + TableInlineFormatSetter + 'static, O: TableOptions + 'static> From<DefaultTableContainer<S, T, O>>
53 for CommandResponse
54{
55 fn from(t: DefaultTableContainer<S, T, O>) -> Self {
56 CommandResponse::Table(t.into_boxed())
57 }
58}
59
60impl Display for CommandResponse {
61 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
62 match self {
63 CommandResponse::Message(message) => write!(f, "{message}"),
64 CommandResponse::Value(value) => write!(f, "{}", serde_json::to_string_pretty(&value).unwrap_or_default()),
65 #[cfg(feature = "toml")]
66 CommandResponse::TomlValue(value) => write!(f, "{}", toml::to_string_pretty(&value).unwrap_or_default()),
67 CommandResponse::Table(table) => write!(f, "{table}"),
68 }
69 }
70}
71
72pub(crate) enum CommandResultBuilderContent<S: Serialize> {
73 Single(S),
74 Collection(Vec<S>),
75}
76
77pub(crate) struct CommandResultBuilder<S: Serialize, T: Clone + Tabled + From<S>, O: TableOptions> {
78 object_or_collection: CommandResultBuilderContent<S>,
79 output_format: Option<OutputFormatArgs>,
80 table_type: PhantomData<T>,
81 table_options: PhantomData<O>,
82}
83
84impl<S: Serialize + 'static, T: Clone + Tabled + From<S> + TableInlineFormatSetter + 'static, O: TableOptions + 'static> CommandResultBuilder<S, T, O> {
85 pub(crate) fn single(single_object: S, output_format: Option<OutputFormatArgs>) -> Self {
86 Self {
87 object_or_collection: CommandResultBuilderContent::Single(single_object),
88 output_format,
89 table_type: Default::default(),
90 table_options: Default::default(),
91 }
92 }
93
94 pub(crate) fn collection(collection: Vec<S>, output_format: Option<OutputFormatArgs>) -> Self {
95 CommandResultBuilder {
96 object_or_collection: CommandResultBuilderContent::Collection(collection),
97 output_format,
98 table_type: Default::default(),
99 table_options: Default::default(),
100 }
101 }
102
103 pub(crate) fn into_command_result(self) -> CommandResult {
104 match self.output_format.clone().unwrap_or_default() {
105 OutputFormatArgs::Table => match self.object_or_collection {
106 CommandResultBuilderContent::Single(single_object) => Ok(DefaultTableContainer::<S, T, O>::from(single_object).into()),
107 CommandResultBuilderContent::Collection(collection) => Ok(DefaultTableContainer::<S, T, O>::from(collection).into()),
108 },
109 OutputFormatArgs::HtmlTable => match self.object_or_collection {
110 CommandResultBuilderContent::Single(single_object) => Ok(DefaultTableContainer::<S, T, O>::from(single_object).into_html_table().into()),
111 CommandResultBuilderContent::Collection(collection) => Ok(DefaultTableContainer::<S, T, O>::from(collection).into_html_table().into()),
112 },
113 OutputFormatArgs::MarkdownTable => match self.object_or_collection {
114 CommandResultBuilderContent::Single(single_object) => Ok(DefaultTableContainer::<S, T, O>::from(single_object).into_markdown_table().into()),
115 CommandResultBuilderContent::Collection(collection) => Ok(DefaultTableContainer::<S, T, O>::from(collection).into_markdown_table().into()),
116 },
117 OutputFormatArgs::Count => match self.object_or_collection {
118 CommandResultBuilderContent::Single(_) => Ok("1 result".into()),
119 CommandResultBuilderContent::Collection(collection) => Ok(format!("{} result(s)", collection.len()).into()),
120 },
121 OutputFormatArgs::Json => match self.object_or_collection {
122 CommandResultBuilderContent::Single(single_object) => Ok(serde_json::to_value(single_object)
123 .map_err(|e| CommandError::SerializationError(SerializationError::Json(e)))?
124 .into()),
125 CommandResultBuilderContent::Collection(collection) => Ok(serde_json::to_value(collection)
126 .map_err(|e| CommandError::SerializationError(SerializationError::Json(e)))?
127 .into()),
128 },
129 #[cfg(feature = "json5")]
130 OutputFormatArgs::Json5 => Err(NotImplemented),
131 #[cfg(feature = "toml")]
132 OutputFormatArgs::Toml => match self.object_or_collection {
133 CommandResultBuilderContent::Single(single_object) => Ok(toml::Value::try_from(single_object)
134 .map_err(|e| CommandError::SerializationError(SerializationError::Toml(e)))?
135 .into()),
136 CommandResultBuilderContent::Collection(collection) => {
137 let inner = toml::Value::try_from(collection).map_err(|e| CommandError::SerializationError(SerializationError::Toml(e)))?;
138 let mut map: Map<String, toml::Value> = Map::new();
139 let type_name = std::any::type_name::<S>().rsplit_once("::").unwrap_or_default().1.to_string();
140 map.insert(type_name, inner);
141 let table = toml::Value::Table(map);
142 Ok(table.into())
143 }
144 },
145 }
146 }
147}