@@ -13,10 +13,19 @@ import UniformTypeIdentifiers
1313import AppKit // for NSWorkspace
1414
1515struct FileCommands : ParsableCommand {
16+
17+ static var subCommands : [ ParsableCommand . Type ] {
18+ if #available( macOS 12 . 0 , * ) {
19+ return [ GetUTI . self, App . self, ListApps . self, Set . self ]
20+ } else {
21+ return [ GetUTI . self, App . self ]
22+ }
23+ }
24+
1625 static let configuration = CommandConfiguration (
1726 commandName: " file " ,
1827 abstract: " commands to manage specific files " ,
19- subcommands: [ GetUTI . self ]
28+ subcommands: subCommands
2029 )
2130
2231 struct GetUTI : ParsableCommand {
@@ -32,5 +41,86 @@ struct FileCommands: ParsableCommand {
3241 print ( typeIdentifier ?? " <unknown> " )
3342 }
3443 }
44+
45+ struct App : ParsableCommand {
46+ static let configuration
47+ = CommandConfiguration ( abstract: " get the app that will open this file " )
48+
49+ @Argument ( help: ArgumentHelp ( " file path " , valueName: " path " ) )
50+ var path : String
51+
52+ @Flag ( help: ArgumentHelp (
53+ " list bundle identifiers instead of paths " ,
54+ valueName: " bundleID " ) )
55+ var bundleID = false
56+
57+ func run( ) {
58+ let url = URL ( fileURLWithPath: path)
59+ if let app = NSWorkspace . shared. urlForApplication ( toOpen: url) {
60+ if bundleID {
61+ guard let appBundle = Bundle ( url: app) else {
62+ Self . exit ( withError: ExitCode ( 6 ) )
63+ }
64+ print ( appBundle. bundleIdentifier ?? " <no identifier> " )
65+ } else {
66+ print ( app. path)
67+ }
68+ } else {
69+ print ( " no app found " )
70+ Self . exit ( withError: ExitCode ( 9 ) )
71+ }
72+ }
73+ }
74+
75+ @available ( macOS 12 , * )
76+ struct ListApps : ParsableCommand {
77+ static let configuration
78+ = CommandConfiguration ( abstract: " get all app that can open this file " )
79+
80+ @Argument ( help: ArgumentHelp ( " file path " , valueName: " path " ) )
81+ var path : String
82+
83+ @Flag ( help: ArgumentHelp (
84+ " list bundle identifiers instead of paths " ,
85+ valueName: " bundleID " ) )
86+ var bundleID = false
87+
88+ func run( ) {
89+ let url = URL ( fileURLWithPath: path)
90+ let apps = NSWorkspace . shared. urlsForApplications ( toOpen: url)
91+ for app in apps {
92+ if bundleID {
93+ guard let appBundle = Bundle ( url: app) else {
94+ Self . exit ( withError: ExitCode ( 6 ) )
95+ }
96+ print ( appBundle. bundleIdentifier ?? " <no identifier> " )
97+ } else {
98+ print ( app. path)
99+ }
100+ }
101+ }
102+ }
103+
104+ @available ( macOS 12 , * )
105+ struct Set : AsyncParsableCommand {
106+ static let configuration
107+ = CommandConfiguration ( abstract: " Set the default app for this specific file " )
108+
109+ @Argument ( help: ArgumentHelp ( " file path " , valueName: " path " ) )
110+ var path : String
111+
112+ @Argument ( help: " the bundle identifier for the new default app " )
113+ var identifier : String
114+
115+ func run( ) async throws {
116+ let url = URL ( fileURLWithPath: path)
117+ guard let appURL = NSWorkspace . shared. urlForApplication ( withBundleIdentifier: identifier)
118+ else {
119+ Self . exit ( withError: ExitCode ( 11 ) )
120+ }
121+ try await NSWorkspace . shared. setDefaultApplication ( at: appURL, toOpenFileAt: url)
122+ print ( " set \( identifier) for \( path) " )
123+ }
124+ }
35125}
36126
0 commit comments