import CliDoc extension CliDoc.Discussion where Content == AnyTextNode { static func playbook( examples: [Example] ) -> Self { .init { VStack { Note.mostOptionsNotRequired ExampleSection.default(examples: examples) SeeAlso(label: "Ansible playbook options.", command: "ansible-playbook --help") ImportantNote.passingExtraArgs } .separator(.newLine(count: 2)) .eraseToAnyTextNode() } } } extension Array where Element == Example { func addingPassingOptions(command: String) -> Self { var output = self output.append(( label: "Passing extra arguments / options", example: "\(command) -- --vaultId=myId@$SCRIPTS/vault-gopass-client" )) return output } func addingPipeToOtherCommand(command: String) -> Self { var output = self output.append(( label: "Piping output to other command.", example: "\(command) --quiet | xargs open" )) return output } } extension ExampleSection where Header == String, Label == String { static func `default`( examples: [Example], usesPassingExtraOptions: Bool = true, usesPipeToOtherCommand: Bool = true ) -> some TextNode { var examples = examples if let first = examples.first { if usesPassingExtraOptions { examples = examples.addingPassingOptions(command: first.example) } if usesPipeToOtherCommand { examples = examples.addingPipeToOtherCommand(command: first.example) } } return Self(examples: examples) { "EXAMPLES:" } label: { "Some common usage examples." } .exampleStyle(HPAExampleStyle()) } } struct HPAExampleStyle: ExampleStyle { func render(content: ExampleConfiguration) -> some TextNode { VStack { content.examples.map { example in VStack { example.label.color(.green).bold() ShellCommand.hpaCommand(example.example) } } } .separator(.newLine(count: 2)) } } extension ShellCommand where Content == String, Symbol == String { static func hpaCommand(_ command: String) -> Self { .init { "\(Constants.appName) \(command)" } symbol: { "$" } } } struct SeeAlso: TextNode { let title = "SEE ALSO:" let label: Label let content: Content init( @TextBuilder content: () -> Content, @TextBuilder label: () -> Label ) { self.content = content() self.label = label() } var body: some TextNode { VStack { LabeledContent { label.italic() } label: { title.color(.yellow).bold().underline() } ShellCommand { content } symbol: { "$" } } } } extension SeeAlso where Content == String, Label == Empty { init(command: String) { self.init { "\(Constants.appName) \(command)" } label: { Empty() } } } extension SeeAlso where Content == String, Label == String { init(label: String, command: String) { self.init { "\(Constants.appName) \(command)" } label: { label } } } struct Note: TextNode { let label: String = "NOTE:" let content: Content init( @TextBuilder _ content: () -> Content ) { self.content = content() } var body: some TextNode { HStack { label.color(.yellow).bold() content } } } extension Note where Content == String { static var mostOptionsNotRequired: Self { .init { "Most options are not required if you have a configuration file setup." } } } struct ImportantNote: TextNode { let label: String = "IMPORTANT NOTE:" let content: Content init( @TextBuilder _ content: () -> Content ) { self.content = content() } var body: some TextNode { HStack { label.color(.red).bold().underline() content } } } extension ImportantNote where Content == String { static var passingExtraArgs: Self { .init { """ Any extra options passed to the underlying command need to come after `--` otherwise an "Unknown option" error will occur. See above example of passing extra options. """ } } }