freda
1
I am trying to create a utility to create a DMG from a given number of files.
That DMG will contain a number of files and for each file I have a mandatory
name plus an optional position within the DMG window plus an optional type.
So the command line looks like the following:
createDMG [standard options] target [file specifications]
createDMG --verbose example.dmg \
bin/binary --position=20,20 \
README.pdf --position 20,80 \
/Applications --position 60,20 --type=link
In this example --verbose is a "standard option".
As far as I know it is not possibility for the standard argument parser to parse the "file specifications". Am I correct or am I missing something here?
So I ended up by parsing the file specifications manually using custom code:
@Argument(
parsing: .unconditionalRemaining,
help: "Specify content in the form path [--position x y] [--type file|link]")
var args: [String]
And then in the run() method I will parse args manually.
One of the unwanted side-effects is that standard options like --verbose must now precede the arguments.
Is there a better way?
MPLewis
(Mike Lewis)
2
The way I’ve seen many other programs (not even necessarily Swift Argument Parser ones) do this is to combine all the information into a custom format in a single argument. For instance : is not generally valid in filenames so you could something like
createDMG --verbose example.dmg \
bin/binary:20,20 \
README.pdf:20,80 \
/Applications:60,20:link
I admit you lose some user experience in having to teach the custom format (especially if someone could specify just the link type and not the position, for instance), but it’s a trade-off between that and being able to specify the other arguments anywhere in the rest of the list.
Assuming any character other than NUL will never exist in a file name can lead to trouble.
The reason you don't normally see colon (:) in macOS file names is because the Hierarchical File System (HFS), the format used by Macs prior HFS+ and prior to APFS, used it as a path separator. For example, what you'd spell as /Users/alex/hello.swift in a Unix-y way would have been represented by HFS as something like Macintosh HD:Users:alex:hello.swift.
You can create a file with a colon in its name in Terminal. I just tested touch a:b now. It's worth noting that Finder shows that file's name as a/b — from a user's perspective, slashes are allowed but not colons, and Finder silently maps between them to actually use a colon in the name stored on disk.
3 Likes
MPLewis
(Mike Lewis)
4
Note that this is why I said generally 
Regardless, even if there are some corner-cases, both macOS and Linux use : as a path separator for things like your PATH, so in general it should be decently okay to use. Obviously not perfect, but it’s a common solution to this problem that other tools I’ve used have implemented successfully. Again, it’s a trade-off between being able to specify your arguments at any point in the command and having to deal with the user experience concerns introduced by the solution.
nnnnnnnn
(Nate Cook)
5
ArgumentParser doesn’t really support that kind of relationship between positional arguments and options/flags, so I don’t think there’s a much better way to handle what you’re trying to do. One thing I might do is to remove the .unconditionalRemaining parsing strategy, and instead call your command with the file specifications after a double-dash (--):
createDMG [standard options] target -- [file specifications]
That way you’ll be able to be more flexible with the order of the options / target, and still get all your specifications at the end.
1 Like
freda
6
Thanks All.
Just happy I didn't overlook anything obvious 
I don't think that the : approach is something I will go for. The -- approach is something I may consider.