Skip to content

Commit edb685f

Browse files
committed
Add package core:flags
1 parent 0861242 commit edb685f

15 files changed

+3599
-0
lines changed

core/flags/LICENSE

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2024, Feoramund
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

core/flags/constants.odin

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package flags
2+
3+
import "core:time"
4+
5+
// Set to true to compile with support for core named types disabled, as a
6+
// fallback in the event your platform does not support one of the types, or
7+
// you have no need for them and want a smaller binary.
8+
NO_CORE_NAMED_TYPES :: #config(ODIN_CORE_FLAGS_NO_CORE_NAMED_TYPES, false)
9+
10+
// Override support for parsing `time` types.
11+
IMPORTING_TIME :: #config(ODIN_CORE_FLAGS_USE_TIME, time.IS_SUPPORTED)
12+
13+
// Override support for parsing `net` types.
14+
// TODO: Update this when the BSDs are supported.
15+
IMPORTING_NET :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin)
16+
17+
TAG_ARGS :: "args"
18+
SUBTAG_NAME :: "name"
19+
SUBTAG_POS :: "pos"
20+
SUBTAG_REQUIRED :: "required"
21+
SUBTAG_HIDDEN :: "hidden"
22+
SUBTAG_VARIADIC :: "variadic"
23+
SUBTAG_FILE :: "file"
24+
SUBTAG_PERMS :: "perms"
25+
SUBTAG_INDISTINCT :: "indistinct"
26+
27+
TAG_USAGE :: "usage"
28+
29+
UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>"
30+
31+
INTERNAL_VARIADIC_FLAG :: "varg"
32+
33+
RESERVED_HELP_FLAG :: "help"
34+
RESERVED_HELP_FLAG_SHORT :: "h"
35+
36+
// If there are more than this number of flags in total, only the required and
37+
// positional flags will be shown in the one-line usage summary.
38+
ONE_LINE_FLAG_CUTOFF_COUNT :: 16

core/flags/doc.odin

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
package flags implements a command-line argument parser.
3+
4+
It works by using Odin's run-time type information to determine where and how
5+
to store data on a struct provided by the program. Type conversion is handled
6+
automatically and errors are reported with useful messages.
7+
8+
9+
Command-Line Syntax:
10+
11+
Arguments are treated differently depending on how they're formatted.
12+
The format is similar to the Odin binary's way of handling compiler flags.
13+
14+
```
15+
type handling
16+
------------ ------------------------
17+
<positional> depends on struct layout
18+
-<flag> set a bool true
19+
-<flag:option> set flag to option
20+
-<flag=option> set flag to option, alternative syntax
21+
-<map>:<key>=<value> set map[key] to value
22+
```
23+
24+
25+
Struct Tags:
26+
27+
Users of the `core:encoding/json` package may be familiar with using tags to
28+
annotate struct metadata. The same technique is used here to annotate where
29+
arguments should go and which are required.
30+
31+
Under the `args` tag, there are the following subtags:
32+
33+
- `name=S`: set `S` as the flag's name.
34+
- `pos=N`: place positional argument `N` into this flag.
35+
- `hidden`: hide this flag from the usage documentation.
36+
- `required`: cause verification to fail if this argument is not set.
37+
- `variadic`: take all remaining arguments when set, UNIX-style only.
38+
- `file`: for `os.Handle` types, file open mode.
39+
- `perms`: for `os.Handle` types, file open permissions.
40+
- `indistinct`: allow the setting of distinct types by their base type.
41+
42+
`required` may be given a range specifier in the following formats:
43+
```
44+
min
45+
<max
46+
min<max
47+
```
48+
49+
`max` is not inclusive in this range, as noted by the less-than `<` sign, so if
50+
you want to require 3 and only 3 arguments in a dynamic array, you would
51+
specify `required=3<4`.
52+
53+
54+
`variadic` may be given a number (`variadic=N`) above 1 to limit how many extra
55+
arguments it consumes.
56+
57+
58+
`file` determines the file open mode for an `os.Handle`.
59+
It accepts a string of flags that can be mixed together:
60+
- r: read
61+
- w: write
62+
- c: create, create the file if it doesn't exist
63+
- a: append, add any new writes to the end of the file
64+
- t: truncate, erase the file on open
65+
66+
67+
`perms` determines the file open permissions for an `os.Handle`.
68+
69+
The permissions are represented by three numbers in octal format. The first
70+
number is the owner, the second is the group, and the third is other. Read is
71+
represented by 4, write by 2, and execute by 1.
72+
73+
These numbers are added together to get combined permissions. For example, 644
74+
represents read/write for the owner, read for the group, and read for other.
75+
76+
Note that this may only have effect on UNIX-like platforms. By default, `perms`
77+
is set to 444 when only reading and 644 when writing.
78+
79+
80+
`indistinct` tells the parser that it's okay to treat distinct types as their
81+
underlying base type. Normally, the parser will hand those types off to the
82+
custom type setter (more about that later) if one is available, if it doesn't
83+
know how to handle the type.
84+
85+
86+
Usage Tag:
87+
88+
There is also the `usage` tag, which is a plain string to be printed alongside
89+
the flag in the usage output. If `usage` contains a newline, it will be
90+
properly aligned when printed.
91+
92+
All surrounding whitespace is trimmed when formatting with multiple lines.
93+
94+
95+
Supported Flag Data Types:
96+
97+
- all booleans
98+
- all integers
99+
- all floats
100+
- all enums
101+
- all complex numbers
102+
- all quaternions
103+
- all bit_sets
104+
- `string` and `cstring`
105+
- `rune`
106+
- `os.Handle`
107+
- `time.Time`
108+
- `datetime.DateTime`
109+
- `net.Host_Or_Endpoint`,
110+
- additional custom types, see Custom Types below
111+
- `dynamic` arrays with element types of the above
112+
- `map[string]`s or `map[cstring]`s with value types of the above
113+
114+
115+
Validation:
116+
117+
The parser will ensure `required` arguments are set, if no errors occurred
118+
during parsing. This is on by default.
119+
120+
Additionally, you may call `register_flag_checker` to set your own argument
121+
validation procedure that will be called after the default checker.
122+
123+
124+
Strict:
125+
126+
The parser will return on the first error and stop parsing. This is on by
127+
default. Otherwise, all arguments that can be parsed, will be, and only the
128+
last error is returned.
129+
130+
131+
Error Messages:
132+
133+
All error message strings are allocated using the context's `temp_allocator`,
134+
so if you need them to persist, make sure to clone the underlying `message`.
135+
136+
137+
Help:
138+
139+
By default, `-h` and `-help` are reserved flags which raise their own error
140+
type when set, allowing the program to handle the request differently from
141+
other errors.
142+
143+
144+
Custom Types:
145+
146+
You may specify your own type setter for program-specific structs and other
147+
named types. Call `register_type_setter` with an appropriate proc before
148+
calling any of the parsing procs.
149+
150+
A compliant `Custom_Type_Setter` must return three values:
151+
- an error message if one occurred,
152+
- a boolean indicating if the proc handles the type, and
153+
- an `Allocator_Error` if any occurred.
154+
155+
If the setter does not handle the type, simply return without setting any of
156+
the values.
157+
158+
159+
UNIX-style:
160+
161+
This package also supports parsing arguments in a limited flavor of UNIX.
162+
Odin and UNIX style are mutually exclusive, and which one to be used is chosen
163+
at parse time.
164+
165+
```
166+
--flag
167+
--flag=argument
168+
--flag argument
169+
--flag argument repeating-argument
170+
```
171+
172+
`-flag` may also be substituted for `--flag`.
173+
174+
Do note that map flags are not currently supported in this parsing style.
175+
176+
177+
Example:
178+
179+
A complete example is given in the `example` subdirectory.
180+
*/
181+
package flags

core/flags/errors.odin

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package flags
2+
3+
import "base:runtime"
4+
import "core:net"
5+
import "core:os"
6+
7+
Parse_Error_Reason :: enum {
8+
None,
9+
// An extra positional argument was given, and there is no `varg` field.
10+
Extra_Positional,
11+
// The underlying type does not support the string value it is being set to.
12+
Bad_Value,
13+
// No flag was given by the user.
14+
No_Flag,
15+
// No value was given by the user.
16+
No_Value,
17+
// The flag on the struct is missing.
18+
Missing_Flag,
19+
// The type itself isn't supported.
20+
Unsupported_Type,
21+
}
22+
23+
Unified_Parse_Error_Reason :: union #shared_nil {
24+
Parse_Error_Reason,
25+
runtime.Allocator_Error,
26+
net.Parse_Endpoint_Error,
27+
}
28+
29+
// Raised during parsing, naturally.
30+
Parse_Error :: struct {
31+
reason: Unified_Parse_Error_Reason,
32+
message: string,
33+
}
34+
35+
// Raised during parsing.
36+
// Provides more granular information than what just a string could hold.
37+
Open_File_Error :: struct {
38+
filename: string,
39+
errno: os.Errno,
40+
mode: int,
41+
perms: int,
42+
}
43+
44+
// Raised during parsing.
45+
Help_Request :: distinct bool
46+
47+
48+
// Raised after parsing, during validation.
49+
Validation_Error :: struct {
50+
message: string,
51+
}
52+
53+
Error :: union {
54+
Parse_Error,
55+
Open_File_Error,
56+
Help_Request,
57+
Validation_Error,
58+
}

0 commit comments

Comments
 (0)