From 781b82bfaad36d4a188efaa7a9cbdf9d503eaf8e Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Fri, 1 Mar 2024 16:41:19 -0800 Subject: [PATCH 1/7] First draft: include supported values in option synopsis when reasonable --- Sources/ArgumentParser/Usage/UsageGenerator.swift | 6 +++++- Tests/ArgumentParserExampleTests/MathExampleTests.swift | 4 ++-- Tests/ArgumentParserUnitTests/HelpGenerationTests.swift | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/ArgumentParser/Usage/UsageGenerator.swift b/Sources/ArgumentParser/Usage/UsageGenerator.swift index e34b94ee6..6111d1a7a 100644 --- a/Sources/ArgumentParser/Usage/UsageGenerator.swift +++ b/Sources/ArgumentParser/Usage/UsageGenerator.swift @@ -104,7 +104,11 @@ extension ArgumentDefinition { switch update { case .unary: - return "\(name.synopsisString) <\(valueName)>" + if help.allValueStrings.count > 1 && help.allValueStrings.count < 6 { + return "\(name.synopsisString) <\(help.allValueStrings.joined(separator: "|"))>" + } else { + return "\(name.synopsisString) <\(valueName)>" + } case .nullary: return name.synopsisString } diff --git a/Tests/ArgumentParserExampleTests/MathExampleTests.swift b/Tests/ArgumentParserExampleTests/MathExampleTests.swift index 7197135f1..f3d1e7f08 100644 --- a/Tests/ArgumentParserExampleTests/MathExampleTests.swift +++ b/Tests/ArgumentParserExampleTests/MathExampleTests.swift @@ -77,7 +77,7 @@ final class MathExampleTests: XCTestCase { let helpText = """ OVERVIEW: Print the average of the values. - USAGE: math stats average [--kind ] [ ...] + USAGE: math stats average [--kind ] [ ...] ARGUMENTS: A group of floating-point values to operate on. @@ -128,7 +128,7 @@ final class MathExampleTests: XCTestCase { command: "math stats average --kind mode", expected: """ Error: Please provide at least one value to calculate the mode. - Usage: math stats average [--kind ] [ ...] + Usage: math stats average [--kind ] [ ...] See 'math stats average --help' for more information. """, exitCode: .validationFailure) diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index fbc1fb333..0aa23dfdf 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -197,7 +197,7 @@ extension HelpGenerationTests { func testHelpWithDefaultValues() { AssertHelp(.default, for: D.self, equals: """ - USAGE: d [] [--name ] [--age ] [--logging ] [--lucky ...] [--optional] [--required] [--degree ] [--directory ] [--manual ] [--unspecial ] [--special ] + USAGE: d [] [--name ] [--age ] [--logging ] [--lucky ...] [--optional] [--required] [--degree ] [--directory ] [--manual ] [--unspecial <0|1>] [--special ] ARGUMENTS: Your occupation. (default: --) From 0ba70c4ec34899068926f3b2d739211baa9d3b3d Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Fri, 1 Mar 2024 22:28:13 -0800 Subject: [PATCH 2/7] Expanded synopsis support for subcommands --- Sources/ArgumentParser/Usage/HelpGenerator.swift | 10 ++++++++-- .../SubcommandEndToEndTests.swift | 2 +- .../ArgumentParserExampleTests/MathExampleTests.swift | 2 +- .../ArgumentParserPackageManagerTests/HelpTests.swift | 6 +++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Sources/ArgumentParser/Usage/HelpGenerator.swift b/Sources/ArgumentParser/Usage/HelpGenerator.swift index f6b204ed0..1eb35ff05 100644 --- a/Sources/ArgumentParser/Usage/HelpGenerator.swift +++ b/Sources/ArgumentParser/Usage/HelpGenerator.swift @@ -111,9 +111,15 @@ internal struct HelpGenerator { } else { var usage = UsageGenerator(toolName: toolName, definition: [currentArgSet]) .synopsis - if !currentCommand.configuration.subcommands.isEmpty { + let subcommands = currentCommand.configuration.subcommands.filter { $0.configuration.shouldDisplay } + if !subcommands.isEmpty { if usage.last != " " { usage += " " } - usage += "" + let joinedSubcommands = subcommands.map { $0._commandName }.joined(separator: "|") + if subcommands.count > 1 && joinedSubcommands.count <= 40 { + usage += "<\(joinedSubcommands)>" + } else { + usage += "" + } } self.usage = usage } diff --git a/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift index 856ec6e93..b49534135 100644 --- a/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift @@ -71,7 +71,7 @@ extension SubcommandEndToEndTests { let helpB = Foo.message(for: CleanExit.helpRequest(CommandB.self)) AssertEqualStrings(actual: helpFoo, expected: """ - USAGE: foo --name + USAGE: foo --name OPTIONS: --name diff --git a/Tests/ArgumentParserExampleTests/MathExampleTests.swift b/Tests/ArgumentParserExampleTests/MathExampleTests.swift index f3d1e7f08..1e1570b5a 100644 --- a/Tests/ArgumentParserExampleTests/MathExampleTests.swift +++ b/Tests/ArgumentParserExampleTests/MathExampleTests.swift @@ -29,7 +29,7 @@ final class MathExampleTests: XCTestCase { let helpText = """ OVERVIEW: A utility for performing maths. - USAGE: math + USAGE: math OPTIONS: --version Show the version. diff --git a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift index cb67aadb7..ab3eb343b 100644 --- a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift +++ b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift @@ -50,7 +50,7 @@ extension HelpTests { XCTAssertEqual( getErrorText(Package.self, ["help"]).trimmingLines(), """ - USAGE: package + USAGE: package OPTIONS: -h, --help Show help information. @@ -69,7 +69,7 @@ extension HelpTests { XCTAssertEqual( Package.message(for: CleanExit.helpRequest()).trimmingLines(), """ - USAGE: package + USAGE: package OPTIONS: -h, --help Show help information. @@ -97,7 +97,7 @@ extension HelpTests { XCTAssertEqual( getErrorText(Package.self, ["help", "config"], screenWidth: 80).trimmingLines(), """ - USAGE: package config + USAGE: package config OPTIONS: -h, --help Show help information. From 76d5baef29add2eee31f00e2cdcfe7fe47f327e6 Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Fri, 1 Mar 2024 22:29:00 -0800 Subject: [PATCH 3/7] String length limit for unary and positional expanded synopses --- Sources/ArgumentParser/Usage/UsageGenerator.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/ArgumentParser/Usage/UsageGenerator.swift b/Sources/ArgumentParser/Usage/UsageGenerator.swift index 6111d1a7a..cfea72b8d 100644 --- a/Sources/ArgumentParser/Usage/UsageGenerator.swift +++ b/Sources/ArgumentParser/Usage/UsageGenerator.swift @@ -104,8 +104,9 @@ extension ArgumentDefinition { switch update { case .unary: - if help.allValueStrings.count > 1 && help.allValueStrings.count < 6 { - return "\(name.synopsisString) <\(help.allValueStrings.joined(separator: "|"))>" + let joinedValues = help.allValueStrings.joined(separator: "|") + if help.allValueStrings.count > 1 && joinedValues.count <= 40 { + return "\(name.synopsisString) <\(joinedValues)>" } else { return "\(name.synopsisString) <\(valueName)>" } @@ -113,7 +114,12 @@ extension ArgumentDefinition { return name.synopsisString } case .positional: - return "<\(valueName)>" + let joinedValues = help.allValueStrings.joined(separator: "|") + if help.allValueStrings.count > 1 && joinedValues.count <= 40 { + return "<\(joinedValues)>" + } else { + return "<\(valueName)>" + } case .default: return "" } From 129a63af1afcdf10e85f3a108d8adf609d31c131 Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Fri, 1 Mar 2024 22:33:25 -0800 Subject: [PATCH 4/7] Add new test for unary and positional expanded synopsis length limits --- ...HelpGenerationTests+ExpandedSynopses.swift | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift new file mode 100644 index 000000000..e97569209 --- /dev/null +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift @@ -0,0 +1,69 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift Argument Parser open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +import XCTest +import ArgumentParserTestHelpers +@testable import ArgumentParser + +extension HelpGenerationTests { + enum Fruit: String, ExpressibleByArgument, CaseIterable { + case apple, banana, coconut, dragonFruit = "dragon-fruit", elderberry, fig, grape, honeydew + } + + enum Action: String, ExpressibleByArgument, CaseIterable { + case purchase, sample, refund = "return" + } + + enum Count: Int, ExpressibleByArgument, CaseIterable { + case zero, one, two, three, four, five, six, seven, eight, nine, ten, eleven, twelve, thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen, twenty + } + + enum Ripeness: String, ExpressibleByArgument, CaseIterable { + case under, perfect, over + } + + struct FruitStore: ParsableArguments { + @Argument(help: "The transaction type") + var action: Action = .purchase + + @Argument(help: "The fruit to purchase") + var fruit: Fruit + + @Option(help: "The number of fruit to purchase") + var quantity: Count = .one + + @Option(help: "The desired ripeness of fruit") + var ripeness: Ripeness = .perfect + } + + // TODO: walk docs for all the places that print usage with "(values: " and assess for turning into a test/updating docs + func testFruitStoreHelp() { + AssertHelp(.default, for: FruitStore.self, equals: """ + USAGE: fruit_store [] [--quantity ] [--ripeness ] + + ARGUMENTS: + The transaction type (values: purchase, sample, + return; default: purchase) + The fruit to purchase (values: apple, banana, + coconut, dragon-fruit, elderberry, fig, grape, + honeydew) + + OPTIONS: + --quantity The number of fruit to purchase (values: 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20; default: 1) + --ripeness The desired ripeness of fruit (values: under, + perfect, over; default: perfect) + -h, --help Show help information. + + """) + } +} From 74b01521284cbd690d65d952e6ade6b27f677d18 Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Fri, 1 Mar 2024 22:49:15 -0800 Subject: [PATCH 5/7] Update documentation to reflect expanded synopsis behaviour --- .../Documentation.docc/Articles/CommandsAndSubcommands.md | 4 ++-- .../Documentation.docc/Articles/CustomizingHelp.md | 4 ++-- .../HelpGenerationTests+ExpandedSynopses.swift | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Sources/ArgumentParser/Documentation.docc/Articles/CommandsAndSubcommands.md b/Sources/ArgumentParser/Documentation.docc/Articles/CommandsAndSubcommands.md index b08c457fc..6ca004a50 100644 --- a/Sources/ArgumentParser/Documentation.docc/Articles/CommandsAndSubcommands.md +++ b/Sources/ArgumentParser/Documentation.docc/Articles/CommandsAndSubcommands.md @@ -22,7 +22,7 @@ You can build a program with commands and subcommands by defining multiple comma % math stats OVERVIEW: Calculate descriptive statistics. -USAGE: math stats +USAGE: math stats OPTIONS: -h, --help Show help information. @@ -118,7 +118,7 @@ extension Math.Statistics { static let configuration = CommandConfiguration( abstract: "Print the average of the values.") - enum Kind: String, ExpressibleByArgument { + enum Kind: String, ExpressibleByArgument, CaseIterable { case mean, median, mode } diff --git a/Sources/ArgumentParser/Documentation.docc/Articles/CustomizingHelp.md b/Sources/ArgumentParser/Documentation.docc/Articles/CustomizingHelp.md index 1c5c99c58..0223793d0 100644 --- a/Sources/ArgumentParser/Documentation.docc/Articles/CustomizingHelp.md +++ b/Sources/ArgumentParser/Documentation.docc/Articles/CustomizingHelp.md @@ -102,7 +102,7 @@ struct FruitStore: ParsableCommand { The help screen includes the list of values in the description of the `` argument: ``` -USAGE: fruit-store [--quantity ] +USAGE: fruit-store [--quantity ] ARGUMENTS: The fruit to purchase (values: apple, banana, @@ -137,7 +137,7 @@ struct FruitStore: ParsableCommand { The help screen still contains all the possible values. ``` -USAGE: fruit-store [--quantity ] +USAGE: fruit-store [--quantity ] ARGUMENTS: The fruit to purchase (values: apple, banana, diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift index e97569209..339192fd1 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift @@ -44,7 +44,6 @@ extension HelpGenerationTests { var ripeness: Ripeness = .perfect } - // TODO: walk docs for all the places that print usage with "(values: " and assess for turning into a test/updating docs func testFruitStoreHelp() { AssertHelp(.default, for: FruitStore.self, equals: """ USAGE: fruit_store [] [--quantity ] [--ripeness ] From dcc35d2f0a9777441c8ae7003cf7859d76e528b0 Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Fri, 1 Mar 2024 22:49:56 -0800 Subject: [PATCH 6/7] Add a test to replicate help output quoted in documentation --- .../MathExampleTests.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Tests/ArgumentParserExampleTests/MathExampleTests.swift b/Tests/ArgumentParserExampleTests/MathExampleTests.swift index 1e1570b5a..159b7587e 100644 --- a/Tests/ArgumentParserExampleTests/MathExampleTests.swift +++ b/Tests/ArgumentParserExampleTests/MathExampleTests.swift @@ -73,6 +73,28 @@ final class MathExampleTests: XCTestCase { try AssertExecuteCommand(command: "math help add --help", expected: helpText) } + func testMath_StatsHelp() throws { + let helpText = """ + OVERVIEW: Calculate descriptive statistics. + + USAGE: math stats + + OPTIONS: + --version Show the version. + -h, --help Show help information. + + SUBCOMMANDS: + average Print the average of the values. + stdev Print the standard deviation of the values. + quantiles Print the quantiles of the values (TBD). + + See 'math help stats ' for detailed help. + """ + try AssertExecuteCommand(command: "math stats -h", expected: helpText) + try AssertExecuteCommand(command: "math stats --help", expected: helpText) + try AssertExecuteCommand(command: "math help stats", expected: helpText) + } + func testMath_StatsMeanHelp() throws { let helpText = """ OVERVIEW: Print the average of the values. From 286c60630eeef0c5ac0d60d96472a55c624b26ea Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Wed, 24 Jul 2024 20:51:34 -0700 Subject: [PATCH 7/7] Scale back ambitions per feedback from @natecook1000 --- .../Articles/CommandsAndSubcommands.md | 2 +- .../Documentation.docc/Articles/CustomizingHelp.md | 4 ++-- Sources/ArgumentParser/Usage/HelpGenerator.swift | 10 ++-------- Sources/ArgumentParser/Usage/UsageGenerator.swift | 7 +------ .../SubcommandEndToEndTests.swift | 2 +- .../ArgumentParserExampleTests/MathExampleTests.swift | 6 +++--- .../ArgumentParserPackageManagerTests/HelpTests.swift | 6 +++--- .../HelpGenerationTests+ExpandedSynopses.swift | 2 +- 8 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Sources/ArgumentParser/Documentation.docc/Articles/CommandsAndSubcommands.md b/Sources/ArgumentParser/Documentation.docc/Articles/CommandsAndSubcommands.md index 8b71195e4..0c16cf403 100644 --- a/Sources/ArgumentParser/Documentation.docc/Articles/CommandsAndSubcommands.md +++ b/Sources/ArgumentParser/Documentation.docc/Articles/CommandsAndSubcommands.md @@ -22,7 +22,7 @@ You can build a program with commands and subcommands by defining multiple comma % math stats OVERVIEW: Calculate descriptive statistics. -USAGE: math stats +USAGE: math stats OPTIONS: -h, --help Show help information. diff --git a/Sources/ArgumentParser/Documentation.docc/Articles/CustomizingHelp.md b/Sources/ArgumentParser/Documentation.docc/Articles/CustomizingHelp.md index 0223793d0..1c5c99c58 100644 --- a/Sources/ArgumentParser/Documentation.docc/Articles/CustomizingHelp.md +++ b/Sources/ArgumentParser/Documentation.docc/Articles/CustomizingHelp.md @@ -102,7 +102,7 @@ struct FruitStore: ParsableCommand { The help screen includes the list of values in the description of the `` argument: ``` -USAGE: fruit-store [--quantity ] +USAGE: fruit-store [--quantity ] ARGUMENTS: The fruit to purchase (values: apple, banana, @@ -137,7 +137,7 @@ struct FruitStore: ParsableCommand { The help screen still contains all the possible values. ``` -USAGE: fruit-store [--quantity ] +USAGE: fruit-store [--quantity ] ARGUMENTS: The fruit to purchase (values: apple, banana, diff --git a/Sources/ArgumentParser/Usage/HelpGenerator.swift b/Sources/ArgumentParser/Usage/HelpGenerator.swift index fc7d42399..060ca4b96 100644 --- a/Sources/ArgumentParser/Usage/HelpGenerator.swift +++ b/Sources/ArgumentParser/Usage/HelpGenerator.swift @@ -114,15 +114,9 @@ internal struct HelpGenerator { } else { var usage = UsageGenerator(toolName: toolName, definition: [currentArgSet]) .synopsis - let subcommands = currentCommand.configuration.subcommands.filter { $0.configuration.shouldDisplay } - if !subcommands.isEmpty { + if !currentCommand.configuration.subcommands.isEmpty { if usage.last != " " { usage += " " } - let joinedSubcommands = subcommands.map { $0._commandName }.joined(separator: "|") - if subcommands.count > 1 && joinedSubcommands.count <= 40 { - usage += "<\(joinedSubcommands)>" - } else { - usage += "" - } + usage += "" } self.usage = usage } diff --git a/Sources/ArgumentParser/Usage/UsageGenerator.swift b/Sources/ArgumentParser/Usage/UsageGenerator.swift index cfea72b8d..19076dba3 100644 --- a/Sources/ArgumentParser/Usage/UsageGenerator.swift +++ b/Sources/ArgumentParser/Usage/UsageGenerator.swift @@ -114,12 +114,7 @@ extension ArgumentDefinition { return name.synopsisString } case .positional: - let joinedValues = help.allValueStrings.joined(separator: "|") - if help.allValueStrings.count > 1 && joinedValues.count <= 40 { - return "<\(joinedValues)>" - } else { - return "<\(valueName)>" - } + return "<\(valueName)>" case .default: return "" } diff --git a/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift index 0c62b2168..2dafd6e4b 100644 --- a/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/SubcommandEndToEndTests.swift @@ -71,7 +71,7 @@ extension SubcommandEndToEndTests { let helpB = Foo.message(for: CleanExit.helpRequest(CommandB.self)) AssertEqualStrings(actual: helpFoo, expected: """ - USAGE: foo --name + USAGE: foo --name OPTIONS: --name diff --git a/Tests/ArgumentParserExampleTests/MathExampleTests.swift b/Tests/ArgumentParserExampleTests/MathExampleTests.swift index c332d2f7e..c0bc147fe 100644 --- a/Tests/ArgumentParserExampleTests/MathExampleTests.swift +++ b/Tests/ArgumentParserExampleTests/MathExampleTests.swift @@ -29,7 +29,7 @@ final class MathExampleTests: XCTestCase { let helpText = """ OVERVIEW: A utility for performing maths. - USAGE: math + USAGE: math OPTIONS: --version Show the version. @@ -77,14 +77,14 @@ final class MathExampleTests: XCTestCase { let helpText = """ OVERVIEW: Calculate descriptive statistics. - USAGE: math stats + USAGE: math stats OPTIONS: --version Show the version. -h, --help Show help information. SUBCOMMANDS: - average Print the average of the values. + average, avg Print the average of the values. stdev Print the standard deviation of the values. quantiles Print the quantiles of the values (TBD). diff --git a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift index e6d41b9ed..6ba46a3b3 100644 --- a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift +++ b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift @@ -50,7 +50,7 @@ extension HelpTests { XCTAssertEqual( getErrorText(Package.self, ["help"]).trimmingLines(), """ - USAGE: package + USAGE: package OPTIONS: -h, --help Show help information. @@ -69,7 +69,7 @@ extension HelpTests { XCTAssertEqual( Package.message(for: CleanExit.helpRequest()).trimmingLines(), """ - USAGE: package + USAGE: package OPTIONS: -h, --help Show help information. @@ -97,7 +97,7 @@ extension HelpTests { XCTAssertEqual( getErrorText(Package.self, ["help", "config"], screenWidth: 80).trimmingLines(), """ - USAGE: package config + USAGE: package config OPTIONS: -h, --help Show help information. diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift index 339192fd1..1d11ed97a 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests+ExpandedSynopses.swift @@ -46,7 +46,7 @@ extension HelpGenerationTests { func testFruitStoreHelp() { AssertHelp(.default, for: FruitStore.self, equals: """ - USAGE: fruit_store [] [--quantity ] [--ripeness ] + USAGE: fruit_store [] [--quantity ] [--ripeness ] ARGUMENTS: The transaction type (values: purchase, sample,