88
99namespace ip_analyzer {
1010
11- void PrintCopperBar ()
11+ void PrintCopperBar (bool color_enabled )
1212{
13+ if (!color_enabled)
14+ {
15+ fmt::print (" {}\n " , std::string (kWidth , ' =' ));
16+ return ;
17+ }
18+
1319 const auto copper_gradient = [](int i)
1420 {
1521 constexpr int kMaxColor = 255 ;
@@ -26,30 +32,66 @@ void PrintCopperBar()
2632 fmt::print (" \n " );
2733}
2834
29- void PrintHeader (const std::string &text)
35+ void PrintHeader (const std::string &text, bool color_enabled )
3036{
31- PrintCopperBar ();
32- fmt::print (OutputColors::kHeader , " {:^{}}\n " , text, kWidth );
33- PrintCopperBar ();
37+ PrintCopperBar (color_enabled);
38+ if (color_enabled)
39+ {
40+ fmt::print (OutputColors::kHeader , " {:^{}}\n " , text, kWidth );
41+ }
42+ else
43+ {
44+ fmt::print (" {:^{}}\n " , text, kWidth );
45+ }
46+ PrintCopperBar (color_enabled);
3447}
3548
36- void PrintRow (const std::string &label, const std::string &value, const std::string &binary)
49+ void PrintRow (const std::string &label, const std::string &value, const std::string &binary,
50+ bool color_enabled)
3751{
38- fmt::print (OutputColors::kLabel , " {:<20}" , label);
52+ if (color_enabled)
53+ {
54+ fmt::print (OutputColors::kLabel , " {:<20}" , label);
55+ }
56+ else
57+ {
58+ fmt::print (" {:<20}" , label);
59+ }
3960 if (binary.empty ())
4061 {
41- fmt::print (OutputColors::kValue , " {}\n " , value);
62+ if (color_enabled)
63+ {
64+ fmt::print (OutputColors::kValue , " {}\n " , value);
65+ }
66+ else
67+ {
68+ fmt::print (" {}\n " , value);
69+ }
4270 }
4371 else
4472 {
45- fmt::print (OutputColors::kValue , " {:<20}" , value);
46- fmt::print (OutputColors::kBinary , " {}\n " , binary);
73+ if (color_enabled)
74+ {
75+ fmt::print (OutputColors::kValue , " {:<20}" , value);
76+ fmt::print (OutputColors::kBinary , " {}\n " , binary);
77+ }
78+ else
79+ {
80+ fmt::print (" {:<20}{}\n " , value, binary);
81+ }
4782 }
4883}
4984
5085void IPAnalyzerApp::PrintPrompt () const
5186{
52- fmt::print (OutputColors::kPrompt , " Enter IP address with CIDR (e.g., 192.168.0.1/24): " );
87+ if (color_enabled_)
88+ {
89+ fmt::print (OutputColors::kPrompt , " Enter IP address with CIDR (e.g., 192.168.0.1/24): " );
90+ }
91+ else
92+ {
93+ fmt::print (" Enter IP address with CIDR (e.g., 192.168.0.1/24): " );
94+ }
5395}
5496
5597void IPAnalyzerApp::PrintVersion () const
@@ -65,14 +107,23 @@ void IPAnalyzerApp::PrintHelp() const
65107 fmt::print (" Options:\n " );
66108 fmt::print (" -h, --help Show this help message and exit\n " );
67109 fmt::print (" -v, --version Show version information and exit\n\n " );
110+ fmt::print (" --json Output results as JSON\n " );
111+ fmt::print (" --compact Use compact, non-decorated output\n " );
112+ fmt::print (" --no-color Disable colored output\n " );
113+ fmt::print (" --stdin Read IP/CIDR values from stdin\n " );
114+ fmt::print (" --ip <value> Provide the IP/CIDR without prompting\n\n " );
68115 fmt::print (" Examples:\n " );
69116 fmt::print (" {} 192.168.1.1/24\n " , kAppName );
70117 fmt::print (" {} 2001:db8::1/64\n " , kAppName );
118+ fmt::print (" {} 2001:db8::/64\n " , kAppName );
71119}
72120
73121void IPAnalyzerApp::PrintResults (const IPAnalyzer &analyzer) const
74122{
75- PrintHeader (" IP Analysis Results" );
123+ if (!compact_)
124+ {
125+ PrintHeader (" IP Analysis Results" , color_enabled_);
126+ }
76127
77128 const auto ip = analyzer.get_ip ();
78129 const auto [first, last] = analyzer.get_host_range ();
@@ -95,20 +146,147 @@ void IPAnalyzerApp::PrintResults(const IPAnalyzer &analyzer) const
95146 rows.emplace_back (" Broadcast Address" , analyzer.get_broadcast ()->to_string (), " " );
96147 }
97148
98- for (const auto &[label, value, binary] : rows)
149+ if (compact_)
150+ {
151+ for (const auto &[label, value, binary] : rows)
152+ {
153+ fmt::print (" {}: {}\n " , label, value);
154+ }
155+ }
156+ else
157+ {
158+ for (const auto &[label, value, binary] : rows)
159+ {
160+ PrintRow (label, value, binary, color_enabled_);
161+ }
162+ }
163+
164+ if (!compact_)
165+ {
166+ PrintCopperBar (color_enabled_);
167+ }
168+ }
169+
170+ void IPAnalyzerApp::PrintJsonResults (const IPAnalyzer &analyzer) const
171+ {
172+ PrintJsonObject (analyzer, " " , false );
173+ }
174+
175+ void IPAnalyzerApp::PrintJsonObject (const IPAnalyzer &analyzer, const std::string &indent,
176+ bool trailing_comma) const
177+ {
178+ const auto ip = analyzer.get_ip ();
179+ const auto [first, last] = analyzer.get_host_range ();
180+
181+ fmt::print (" {}{{\n " , indent);
182+ fmt::print (" {} \" schema\" : \" ip-analyzer/1\" ,\n " , indent);
183+ fmt::print (" {} \" version\" : \" {}\" ,\n " , indent, kVersion );
184+ fmt::print (" {} \" ip\" : \" {}\" ,\n " , indent, ip->to_string ());
185+ fmt::print (" {} \" network\" : \" {}\" ,\n " , indent, analyzer.get_network ()->to_string ());
186+ fmt::print (" {} \" netmask\" : \" {}\" ,\n " , indent, analyzer.get_netmask ()->to_string ());
187+ fmt::print (" {} \" cidr\" : {},\n " , indent, analyzer.get_cidr ());
188+ fmt::print (" {} \" range_first\" : \" {}\" ,\n " , indent, first->to_string ());
189+ fmt::print (" {} \" range_last\" : \" {}\" ,\n " , indent, last->to_string ());
190+ fmt::print (" {} \" num_hosts\" : {},\n " , indent, analyzer.get_num_hosts ());
191+ fmt::print (" {} \" private\" : {},\n " , indent, analyzer.is_private () ? " true" : " false" );
192+
193+ if (ip->is_ipv6 ())
194+ {
195+ fmt::print (" {} \" scope\" : \" {}\"\n " , indent, GetIPv6Scope (ip));
196+ }
197+ else
198+ {
199+ fmt::print (" {} \" broadcast\" : \" {}\"\n " , indent, analyzer.get_broadcast ()->to_string ());
200+ }
201+
202+ fmt::print (" {}}}{}\n " , indent, trailing_comma ? " ," : " " );
203+ }
204+
205+ int IPAnalyzerApp::RunFromStdin ()
206+ {
207+ std::vector<std::string> inputs;
208+ std::string line;
209+ while (std::getline (std::cin, line))
210+ {
211+ if (!line.empty ())
212+ {
213+ inputs.push_back (line);
214+ }
215+ }
216+
217+ if (inputs.empty ())
99218 {
100- PrintRow (label, value, binary) ;
219+ return 1 ;
101220 }
102221
103- PrintCopperBar ();
222+ if (output_json_)
223+ {
224+ fmt::print (" [\n " );
225+ }
226+
227+ for (size_t index = 0 ; index < inputs.size (); ++index)
228+ {
229+ try
230+ {
231+ IPAnalyzer analyzer (inputs[index]);
232+ if (output_json_)
233+ {
234+ PrintJsonObject (analyzer, " " , index + 1 < inputs.size ());
235+ }
236+ else
237+ {
238+ if (index > 0 )
239+ {
240+ fmt::print (" \n " );
241+ }
242+ PrintResults (analyzer);
243+ }
244+ }
245+ catch (const std::exception &e)
246+ {
247+ if (output_json_)
248+ {
249+ fmt::print (" ]\n " );
250+ }
251+ PrintError (e.what ());
252+ return 1 ;
253+ }
254+ }
255+
256+ if (output_json_)
257+ {
258+ fmt::print (" ]\n " );
259+ }
260+
261+ return 0 ;
104262}
105263
106264std::string IPAnalyzerApp::GetIPv6Scope (const std::shared_ptr<IPAddress> &ip) const
107265{
108266 auto ipv6 = std::dynamic_pointer_cast<IPv6Address>(ip);
109267 auto bytes = ipv6->to_bytes ();
268+ bool is_unspecified = true ;
269+ for (auto byte : bytes)
270+ {
271+ if (byte != 0 )
272+ {
273+ is_unspecified = false ;
274+ break ;
275+ }
276+ }
277+ if (is_unspecified)
278+ return " Unspecified" ;
279+ if (bytes[0 ] == 0 && bytes[1 ] == 0 && bytes[2 ] == 0 && bytes[3 ] == 0 &&
280+ bytes[4 ] == 0 && bytes[5 ] == 0 && bytes[6 ] == 0 && bytes[7 ] == 0 &&
281+ bytes[8 ] == 0 && bytes[9 ] == 0 && bytes[10 ] == 0 && bytes[11 ] == 0 &&
282+ bytes[12 ] == 0 && bytes[13 ] == 0 && bytes[14 ] == 0 && bytes[15 ] == 1 )
283+ return " Loopback" ;
284+ if (bytes[0 ] == 0x20 && bytes[1 ] == 0x01 && bytes[2 ] == 0x0d && bytes[3 ] == 0xb8 )
285+ return " Documentation" ;
110286 if (bytes[0 ] == 0xfe && (bytes[1 ] & 0xc0 ) == 0x80 )
111287 return " Link-Local" ;
288+ if (bytes[0 ] == 0xfe && (bytes[1 ] & 0xc0 ) == 0xc0 )
289+ return " Site-Local" ;
112290 if (bytes[0 ] == 0xfd || bytes[0 ] == 0xfc )
113291 return " Unique Local" ;
114292 if (bytes[0 ] == 0xff )
@@ -118,7 +296,14 @@ std::string IPAnalyzerApp::GetIPv6Scope(const std::shared_ptr<IPAddress> &ip) co
118296
119297void IPAnalyzerApp::PrintError (const std::string &message) const
120298{
121- fmt::print (OutputColors::kError , " Error: {}\n " , message);
299+ if (color_enabled_)
300+ {
301+ fmt::print (OutputColors::kError , " Error: {}\n " , message);
302+ }
303+ else
304+ {
305+ fmt::print (" Error: {}\n " , message);
306+ }
122307}
123308
124- }
309+ }
0 commit comments