• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lduchosal / ipnetwork / 23998941155

05 Apr 2026 09:43AM UTC coverage: 89.233% (-2.7%) from 91.952%
23998941155

push

travis-pro

lduchosal
feat: parse errors in formatter pipeline, action examples, llms.txt, bump 4.2.0

- Parse errors no longer write raw text to stdout; they are accumulated
  in ProgramContext.ParseErrors and rendered by the active formatter
- Errors display only the error message, not the full usage
- Each action in ArgsList now carries an example
- TextFormatter/JsonFormatter render examples in usage output
- Add llms.txt at repo root for LLM-friendly project documentation
- Bump version to 4.2.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

790 of 894 branches covered (88.37%)

Branch coverage included in aggregate %.

31 of 56 new or added lines in 7 files covered. (55.36%)

19 existing lines in 1 file now uncovered.

2177 of 2431 relevant lines covered (89.55%)

493340.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.69
/src/ConsoleApplication/Program.cs
1
// <copyright file="Program.cs" company="IPNetwork">
2
// Copyright (c) IPNetwork. All rights reserved.
3
// </copyright>
4

5
namespace System.Net;
6

7
using Gnu.Getopt;
8
using System.Collections.Generic;
9
using System.Diagnostics.CodeAnalysis;
10

11
/// <summary>
12
/// Console app for IPNetwork.
13
/// </summary>
14
public static class Program
15
{
16
    private static readonly Dictionary<int, ArgParsed> Args = new ();
1✔
17

18
    private static readonly ArgParsed[] ArgsList =
1✔
19
    [
1✔
20
        // Print options
1✔
21
        new ArgParsed('i', "Print options", "network", (ac, _) => { ac.IPNetwork = true; }),
3✔
22
        new ArgParsed('n', "Print options", "network address", (ac, _) => { ac.Network = true; }),
9✔
23
        new ArgParsed('m', "Print options", "netmask", (ac, _) => { ac.Netmask = true; }),
6✔
24
        new ArgParsed('c', "Print options", "cidr", (ac, _) => { ac.Cidr = true; }),
9✔
25
        new ArgParsed('b', "Print options", "broadcast", (ac, _) => { ac.Broadcast = true; }),
6✔
26
        new ArgParsed('f', "Print options", "first usable ip address", (ac, _) => { ac.FirstUsable = true; }),
6✔
27
        new ArgParsed('l', "Print options", "last usable ip address", (ac, _) => { ac.LastUsable = true; }),
6✔
28
        new ArgParsed('u', "Print options", "number of usable ip addresses", (ac, _) => { ac.Usable = true; }),
6✔
29
        new ArgParsed('t', "Print options", "total number of ip addresses", (ac, _) => { ac.Total = true; }),
3✔
30

1✔
31
        // Output options
1✔
32
        new ArgParsed('j', "Output options", "JSON output", (ac, _) => { ac.Json = true; }),
45✔
33

1✔
34
        // Parse options
1✔
35
        new ArgParsed('D', "Parse options", "IPv4 only - use default cidr (ClassA/8, ClassB/16, ClassC/24)",
1✔
36
            (ac, _) => { ac.CidrParse = CidrParse.Default; }),
×
37
        new ArgParsed('d', "Parse options", "use cidr if not provided (default /32)", (ac, arg) =>
1✔
38
        {
2✔
39
            if (!IPNetwork2.TryParseCidr(arg, Sockets.AddressFamily.InterNetwork, out byte? cidr))
2!
40
            {
×
NEW
41
                ac.ParseErrors.Add(string.Format("Invalid cidr {0}", cidr));
×
42
                ac.Action = Action.Usage;
×
43
                return;
×
44
            }
1✔
45

1✔
46
            ac.CidrParse = CidrParse.Value;
2✔
47
            ac.CidrParsed = (byte)cidr;
2✔
48
        }, argName: "cidr"),
2✔
49

1✔
50
        // Actions
1✔
51
        new ArgParsed('h', "Actions", "help message",
1✔
NEW
52
            (ac, _) => { ac.Action = Action.Usage; },
×
53
            example: "ipnetwork -h"),
1✔
54
        new ArgParsed('s', "Actions", "split network into cidr subnets", (ac, arg) =>
1✔
55
        {
2✔
56
            if (!IPNetwork2.TryParseCidr(arg, Sockets.AddressFamily.InterNetwork, out byte? cidr))
2!
57
            {
×
NEW
58
                ac.ParseErrors.Add(string.Format("Invalid cidr {0}", cidr));
×
UNCOV
59
                ac.Action = Action.Usage;
×
UNCOV
60
                return;
×
61
            }
1✔
62

1✔
63
            ac.Action = Action.Subnet;
2✔
64
            ac.SubnetCidr = (byte)cidr;
2✔
65
        }, argName: "cidr", example: "ipnetwork -s 24 10.0.0.0/8"),
2✔
66
        new ArgParsed('w', "Actions", "supernet networks into smallest possible subnets",
1✔
67
            (ac, _) => { ac.Action = Action.Supernet; },
6✔
68
            example: "ipnetwork -w 10.0.0.0/24 10.0.1.0/24"),
1✔
69
        new ArgParsed('W', "Actions", "supernet networks into one single subnet",
1✔
70
            (ac, _) => { ac.Action = Action.WideSupernet; },
3✔
71
            example: "ipnetwork -W 10.0.0.0/24 10.0.10.0/24"),
1✔
72
        new ArgParsed('x', "Actions", "list all ip addresses in networks",
1✔
73
            (ac, _) => { ac.Action = Action.ListIPAddress; },
6✔
74
            example: "ipnetwork -x 10.0.0.0/30"),
1✔
75
        new ArgParsed('C', "Actions", "network contain networks", (ac, arg) =>
1✔
76
        {
3✔
77
            if (!TryParseIPNetwork(arg, ac.CidrParse, ac.CidrParsed, out IPNetwork2? ipnetwork))
3!
UNCOV
78
            {
×
NEW
79
                ac.ParseErrors.Add($"Unable to parse ipnetwork {arg}");
×
UNCOV
80
                ac.Action = Action.Usage;
×
81
                return;
×
82
            }
1✔
83

1✔
84
            ac.Action = Action.ContainNetwork;
3✔
85
            ac.ContainNetwork = ipnetwork;
3✔
86
        }, argName: "network", example: "ipnetwork -C 10.0.0.0/8 10.0.1.0/24"),
3✔
87
        new ArgParsed('o', "Actions", "network overlap networks", (ac, arg) =>
1✔
88
        {
3✔
89
            if (!TryParseIPNetwork(arg, ac.CidrParse, ac.CidrParsed, out IPNetwork2? ipnetwork))
3!
UNCOV
90
            {
×
NEW
91
                ac.ParseErrors.Add(string.Format("Unable to parse ipnetwork {0}", arg));
×
UNCOV
92
                ac.Action = Action.Usage;
×
93
                return;
×
94
            }
1✔
95

1✔
96
            ac.Action = Action.OverlapNetwork;
3✔
97
            ac.OverlapNetwork = ipnetwork;
3✔
98
        }, argName: "network", example: "ipnetwork -o 10.0.0.0/8 192.168.0.0/16"),
3✔
99
        new ArgParsed('S', "Actions", "subtract network from networks", (ac, arg) =>
1✔
100
        {
2✔
101
            if (!TryParseIPNetwork(arg, ac.CidrParse, ac.CidrParsed, out IPNetwork2? ipnetwork))
2!
UNCOV
102
            {
×
NEW
103
                ac.ParseErrors.Add(string.Format("Unable to parse ipnetwork {0}", arg));
×
UNCOV
104
                ac.Action = Action.Usage;
×
UNCOV
105
                return;
×
106
            }
1✔
107

1✔
108
            ac.Action = Action.SubtractNetwork;
2✔
109
            ac.SubtractNetwork = ipnetwork;
2✔
110
        }, argName: "network", example: "ipnetwork -S 10.0.1.0/24 10.0.0.0/23"),
2✔
111

1✔
112
        // Hidden
1✔
113
        new ArgParsed('?', (_, _) => { }),
4✔
114
    ];
1✔
115

116
    /// <summary>
117
    /// Program entry point.
118
    /// </summary>
119
    /// <param name="args">program arguments.</param>
120
    public static void Main(string[] args)
121
    {
26✔
122
        ProgramContext ac = ParseArgs(args);
26✔
123
        ActionOutput output = ActionComputer.Compute(ac, ArgsList);
26✔
124
        IFormatter formatter = ac.Json ? new JsonFormatter(Console.Out) : new TextFormatter(Console.Out);
26✔
125
        formatter.Write(output, ac);
26✔
126
    }
26✔
127

128
    static Program()
129
    {
1✔
130
        foreach (ArgParsed ap in ArgsList)
45✔
131
        {
21✔
132
            Args.Add(ap.Arg, ap);
21✔
133
        }
21✔
134
    }
1✔
135

136
    private static ProgramContext ParseArgs(string[] args)
137
    {
26✔
138
        int c;
139
        var g = new Getopt("ipnetwork", args, "jinmcbfltud:Dhs:wWxC:o:S:");
26✔
140
        var ac = new ProgramContext();
26✔
141

142
        while ((c = g.getopt()) != -1)
78✔
143
        {
52✔
144
            string? optArg = g.Optarg;
52✔
145
            Args[c].Run(ac, optArg ?? string.Empty);
52✔
146
        }
52✔
147

148
        var ipnetworks = new List<string>();
26✔
149
        for (int i = g.Optind; i < args.Length; i++)
142✔
150
        {
45✔
151
            if (!string.IsNullOrEmpty(args[i]))
45✔
152
            {
45✔
153
                ipnetworks.Add(args[i]);
45✔
154
            }
45✔
155
        }
45✔
156

157
        ac.NetworksString = ipnetworks.ToArray();
26✔
158
        ParseIPNetworks(ac);
26✔
159

160
        if (ac.Networks.Length == 0 && ac.Action != Action.Usage)
26✔
161
        {
1✔
162
            ac.ParseErrors.Add("Provide at least one ipnetwork");
1✔
163
            ac.Action = Action.Usage;
1✔
164
        }
1✔
165

166
        if (ac.Action == Action.Supernet
26!
167
            && ipnetworks.Count < 2)
26✔
UNCOV
168
        {
×
NEW
169
            ac.ParseErrors.Add("Supernet action required at least two ipnetworks");
×
UNCOV
170
            ac.Action = Action.Usage;
×
UNCOV
171
        }
×
172

173
        if (ac.Action == Action.WideSupernet
26!
174
            && ipnetworks.Count < 2)
26✔
UNCOV
175
        {
×
NEW
176
            ac.ParseErrors.Add("WideSupernet action required at least two ipnetworks");
×
UNCOV
177
            ac.Action = Action.Usage;
×
UNCOV
178
        }
×
179

180
        if (PrintNoValue(ac))
26✔
181
        {
21✔
182
            PrintAll(ac);
21✔
183
        }
21✔
184

185
        if (g.Optind == 0)
26✔
186
        {
3✔
187
            PrintAll(ac);
3✔
188
        }
3✔
189

190
        return ac;
26✔
191
    }
26✔
192

193
    private static void ParseIPNetworks(ProgramContext ac)
194
    {
26✔
195
        var ipnetworks = new List<IPNetwork2>();
26✔
196
        foreach (string ips in ac.NetworksString)
168✔
197
        {
45✔
198
            if (!TryParseIPNetwork(ips, ac.CidrParse, ac.CidrParsed, out IPNetwork2? ipnetwork))
45✔
199
            {
1✔
200
                ac.ParseErrors.Add(string.Format("Unable to parse ipnetwork {0}", ips));
1✔
201
                continue;
1✔
202
            }
203

204
            ipnetworks.Add(ipnetwork);
44✔
205
        }
44✔
206

207
        ac.Networks = ipnetworks.ToArray();
26✔
208
    }
26✔
209

210
    private static bool TryParseIPNetwork(string ip, CidrParse cidrParse, byte cidr, [NotNullWhen(true)] out IPNetwork2? ipn)
211
    {
53✔
212
        IPNetwork2? ipnetwork = null;
53✔
213
        switch (cidrParse)
53!
214
        {
215
            case CidrParse.Default when !IPNetwork2.TryParse(ip, out ipnetwork):
×
216
                ipn = null;
×
UNCOV
217
                return false;
×
218
            case CidrParse.Value:
219
            {
53✔
220
                if (!IPNetwork2.TryParse(ip, cidr, out ipnetwork)
53✔
221
                    && !IPNetwork2.TryParse(ip, out ipnetwork))
53✔
222
                {
1✔
223
                    ipn = null;
1✔
224
                    return false;
1✔
225
                }
226

227
                break;
52✔
228
            }
229
        }
230

231
        if (ipnetwork is null)
52!
UNCOV
232
        {
×
UNCOV
233
            ipn = null;
×
UNCOV
234
            return false;
×
235
        }
236

237
        ipn = ipnetwork;
52✔
238
        return true;
52✔
239
    }
53✔
240

241
    private static bool PrintNoValue(ProgramContext ac)
242
    {
26✔
243
        ArgumentNullException.ThrowIfNull(ac);
26✔
244

245
        return ac is { IPNetwork: false, Network: false }
26✔
246
               && !ac.Netmask
26✔
247
               && !ac.Cidr
26✔
248
               && !ac.Broadcast
26✔
249
               && !ac.FirstUsable
26✔
250
               && !ac.LastUsable
26✔
251
               && !ac.Total
26✔
252
               && !ac.Usable;
26✔
253
    }
26✔
254

255
    private static void PrintAll(ProgramContext ac)
256
    {
24✔
257
        ArgumentNullException.ThrowIfNull(ac);
24✔
258

259
        ac.IPNetwork = true;
24✔
260
        ac.Network = true;
24✔
261
        ac.Netmask = true;
24✔
262
        ac.Cidr = true;
24✔
263
        ac.Broadcast = true;
24✔
264
        ac.FirstUsable = true;
24✔
265
        ac.LastUsable = true;
24✔
266
        ac.Usable = true;
24✔
267
        ac.Total = true;
24✔
268
    }
24✔
269

270
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc