How to exclude members from parsing

So ParsableArguments requires that all members use one of their property wrappers.

I'm trying to add defaulted properties that won't participate in the parsing.
In so far, I've used a wrapper that ignores decoding

@propertyWrapper
struct Undecoded<Value>: Decodable {
    var storage: Value?

    init() { }
    init(from decoder: Decoder) throws { }

    var wrappedValue: Value {
        get { storage! }
        set { storage = newValue }
    }
}

struct Command: ParsableCommand {
  @Argument() var foo: Int
  @Undecoded let unparsed: Int

  mutating func validate() throws {
    unparsed = 4
  }
}

Is there any caveat in doing this? Any idea on improving this?
Ideally I'd want to do:

struct Command: ParsableCommand {
  @Argument() var foo: Int
  let unparsed = 4
}

I could also separate the Parsing from the Running logic, but it doesn't seem to be what ArgumentParser is promoting.

Huh. I need to learn more about how Decodable synthesis works. Your ideal code actually works as is:

struct Guess: ParsableCommand {
  @Argument() var guess: Int
  let magic = 4
  
  func run() {
    if guess > magic { print("too high") }
    else if guess < magic { print("too low") }
    else { print("you got it!") }
  }
}
% guess 5
too high
% guess 3
too low

...but if I change the let to var, it tries to decode a value for the magic property, and fails:

% guess 5
Error: Missing expected argument
Usage: guess <guess>
1 Like

That us both.

Turns out the excluding members also need to be Decodable, despite not being used.

It’s probably because you have a let property with an initial value so it cannot be overwritten during decoding, so it gets skipped.

4 Likes

Anyway to achieve this with var variables?

What happens if you use a var?

Pretty much this:


As said, I've been using @Unparsed, but it's less than ideal.

So you want to use a var but don’t want it to be decoded?

Yes, it has a default value. Ideally I'd have

var a = 3

I also try to get to something like this

@Undecoded(initialValue: 3) var a: Int

but it doesn't seem to avoid the decoding phase used by arg-parser.

Maybe you can exclude it by specifying the CodingKeys explicitly?

struct DecodableThing: Decodable {
	var foo: Int = 2
	let bar: String

	private enum CodingKeys: String, CodingKey {
		case bar
	}
}

let jsonString = """
{ "foo": 1, "bar": "hello" }
"""

let decodedThing = try! JSONDecoder().decode(DecodableThing.self, from: Data(jsonString.utf8))
print(decodedThing.foo) // 2
2 Likes

That‘s what I mentioned on Slack as being very annoying. I‘m not sure this was intended.

Terms of Service

Privacy Policy

Cookie Policy