Slide 1

Slide 1 text

swift-argument-parserͰ ؆୯ CLI πʔϧ࡞Γ Zli × DMM ߹ಉLT ry-itto 2020/07/18 1

Slide 2

Slide 2 text

ࣗݾ঺հ • ҏ౻྇໵(@ry-itto) • iOS • B4 • Χϐόϥ͕޷͖ 2 ͜Μͳ͜ͱ΍ͬͯ·ͨ͠ˠ

Slide 3

Slide 3 text

ࣗݾ঺հʢଓ͖ʣ ʬ͋ͭ͠ʭChannel ΛΈΔ͜ͱΛ೔՝ʹͯ͠·͢ ௒༊͞ΕΔͷͰҰճ͸Έͯ΄͍͠ ↑͓͢͢Ίͷಈը (https://www.youtube.com/watch?v=4tieUdeYHHY)

Slide 4

Slide 4 text

swift-argument-parser ͱ͸ʁʁ • Apple ެࣜͷϥΠϒϥϦ • CLI πʔϧΛ࢖͏࣌ʹΑ͋͘ΔΦϓγϣϯͱ͔ɺίϚϯυͷαϒίϚ ϯυͱ͔Λѻ͏΋ͷ • PropertyWrapper Λ͏·͍͜ͱ࢖͍ͬͯΔ • help ͕͍͍ײ͡ʹͳΔ 4

Slide 5

Slide 5 text

ॾ஫ҙ • ࠓճ͸ swift-argument-parser ʹ͍ͭͯͷΈઆ໌͠·͢ • SPM ͰίϚϯυϥΠϯ πʔϧΛ࡞Δํ๏ͳͲ͸ݴ͍·ͤΜ 5

Slide 6

Slide 6 text

ϛχϚϜͳ ࢖͍ํ struct SampleCommand: ParsableCommand { func run() throws { print("sample") } } SampleCommand.main() 1. ParsableCommand ʹ४ڌ ͨ͠Ϋϥε/ߏ଄ମΛ༻ҙ ͢Δ 2. run() ϝιουΛ࣮૷͢Δ 3. main() ΛݺͿ 6

Slide 7

Slide 7 text

ͪΐͬͱԠ༻ import ArgumentParser struct SampleCommand: ParsableCommand { @Argument() var context: String @Flag() var upper: Bool = false @Option( name: .shortAndLong, help: "Repeating context" ) var repeatTimes: Int? mutating func run() throws { if upper { self.context = context.uppercased() } if let repeatTimes = repeatTimes { self.context = String(repeating: context, count: repeatTimes) } print(context) } } SampleCommand.main() echo ίϚϯυͷΑ͏ͳ΋ͷɻ • @Argument • @Flag • @Option ͕ग़͖ͯͨ 7

Slide 8

Slide 8 text

ͪΐͬͱԠ༻ @Argument • ίϚϯυʹର͢ΔҾ਺ • σϑΥϧτ஋Λ༩͓͑ͯ͘ͱࢦఆ ͠ͳͯ͘΋ಈ࡞͢Δ • ΠχγϟϥΠβ͸ҎԼ̎ͭͳͲ͕ ༻ҙ͞Ε͍ͯΔ • @Argument(help:transform:) • @Argument(wrappedValue:parsing:help:trans form:) • ෳ਺ͷҾ਺ʹ΋ରԠ͍ͯ͠Δ 8

Slide 9

Slide 9 text

ͪΐͬͱԠ༻ @Flag • ϑϥάɻ (ls -a ͷ -a ͱ͔ͷΠϝʔ δ) • ྫͷΑ͏ʹ Bool Ͱͷ੾Γସ͑΋ Ͱ͖Δ͕ɺenum Ͱ΋͍͚Δ • ↓ΠχγϟϥΠβͰΑ͘࢖͍ͦ͏ • @Flag(name:help:) 9

Slide 10

Slide 10 text

@Flag (͓·͚) import ArgumentParser enum OutputCase: EnumerableFlag { case upper case lower case natural } struct SampleCommand: ParsableCommand { @Argument() var context: String @Flag() var outputCase: OutputCase = .natural @Option( name: .shortAndLong, help: "Repeating context" ) var repeatTimes: Int? mutating func run() throws { switch outputCase { case .upper: self.context = context.uppercased() case .lower: self.context = context.lowercased() case .natural: break } if let repeatTimes = repeatTimes { self.context = String(repeating: context, count: repeatTimes) } print(context) } } SampleCommand.main() enum Ͱॻ͍ͨόʔδϣϯ (lower ΋ͨͯ͠Έͨ) 10

Slide 11

Slide 11 text

ͪΐͬͱԠ༻ @Option • git commit -m ͷΠϝʔδ • σϑΥϧτ஋Λ༩͑Δ·ͨ͸Φϓ γϣφϧܕʹ͠ͳ͍ͱඞਢͷΦϓ γϣϯʹͳͬͯ͠·͏ 11

Slide 12

Slide 12 text

ͪΐͬͱԠ༻ > ./.build/debug/SampleCommand --help USAGE: sample-command [--upper] [--repeat- times ] ARGUMENTS: OPTIONS: --upper -r, --repeat-times Repeating context -h, --help Show help information. —help

Slide 13

Slide 13 text

ͪΐͬͱԠ༻ ࢖ͬͯΈΔ :yosasou: 13

Slide 14

Slide 14 text

αϒίϚϯυ import ArgumentParser struct SampleSubCommands: ParsableCommand { static var configuration: CommandConfiguration = .init( subcommands: [Sum.self, Average.self], defaultSubcommand: Sum.self ) } struct Sum: ParsableCommand { @Argument() var numbers: [Int] = [] mutating func run() throws { print(numbers.reduce(0, +)) } } struct Average: ParsableCommand { @Argument() var numbers: [Int] = [] func validate() throws { if numbers.count == 0 { throw ValidationError("Please specify numbers.") } } mutating func run() throws { print(numbers.reduce(0, +) / numbers.count) } } SampleSubCommands.main()

Slide 15

Slide 15 text

αϒίϚϯυ > ./.build/debug/SampleSubCommands --help USAGE: sample-sub-commands OPTIONS: -h, --help Show help information. SUBCOMMANDS: sum (default) average See 'sample-sub-commands help ' for detailed help. --help

Slide 16

Slide 16 text

αϒίϚϯυ ࡞Γํ • ParsableCommand ʹ४ڌͨ͠Ϋ ϥε/ߏ଄ମ Λ༻ҙ • configuration Λఆٛͯ͠ɺҾ਺ ͷ subcommands ʹ࡞ͬͨαϒί ϚϯυΛೖΕΔ • defaultSubcommand Λࢦఆ͢Δ ͜ͱͰɺsubcommand ໊Λࢦఆ ͠ͳͯ͘΋ྑ͘ͳΔ

Slide 17

Slide 17 text

αϒίϚϯυ ࢖ͬͯΈΔ

Slide 18

Slide 18 text

εϥΠυ಺ʹग़͖ͯͨιʔείʔυ ↓ʹஔ͍ͯ·͢ github.com/ry-itto-playground/SampleCommand

Slide 19

Slide 19 text

࡞ͬͯΈͨ΋ͷͷ঺հ (⭐͍ͩ͘͞) AtCoderSwiftCLI (github.com/ry-itto/AtCoderSwiftCLI) • cargo-atcoder ͷ Swift ൛ΛΠϝʔδ • ఏग़ػೳ͸࡞ͬͯΈͯΔ్த • brew ͰམͱͤΔΑ͏ʹͯ͠ͳ͍ͷͰ mint ͔ɺखಈϏϧυͰ 19