@@ -37,68 +37,91 @@ public struct SwiftlyCoreContext: Sendable {
37
37
/// The output handler to use, if any.
38
38
public var outputHandler : ( any OutputHandler ) ?
39
39
40
- /// The input probider to use, if any
40
+ /// The output handler for error streams
41
+ public var errorOutputHandler : ( any OutputHandler ) ?
42
+
43
+ /// The input provider to use, if any
41
44
public var inputProvider : ( any InputProvider ) ?
42
45
43
- public init ( ) {
46
+ /// The terminal info provider
47
+ public var terminal : any Terminal
48
+
49
+ /// The format
50
+ public var format : OutputFormat = . text
51
+
52
+ public init ( format: SwiftlyCore . OutputFormat = . text) {
44
53
self . httpClient = SwiftlyHTTPClient ( httpRequestExecutor: HTTPRequestExecutorImpl ( ) )
45
54
self . currentDirectory = fs. cwd
55
+ self . format = format
56
+ self . terminal = SystemTerminal ( )
46
57
}
47
58
48
59
public init ( httpClient: SwiftlyHTTPClient ) {
49
60
self . httpClient = httpClient
50
61
self . currentDirectory = fs. cwd
62
+ self . terminal = SystemTerminal ( )
51
63
}
52
64
53
65
/// Pass the provided string to the set output handler if any.
54
66
/// If no output handler has been set, just print to stdout.
55
- public func print( _ string: String = " " , terminator : String ? = nil ) async {
67
+ public func print( _ string: String = " " ) async {
56
68
guard let handler = self . outputHandler else {
57
- if let terminator {
58
- Swift . print ( string, terminator: terminator)
59
- } else {
60
- Swift . print ( string)
61
- }
69
+ Swift . print ( string)
62
70
return
63
71
}
64
- await handler. handleOutputLine ( string + ( terminator ?? " " ) )
72
+ await handler. handleOutputLine ( string)
65
73
}
66
74
67
75
public func message( _ string: String = " " , terminator: String ? = nil ) async {
68
- // Get terminal size or use default width
69
- let terminalWidth = self . getTerminalWidth ( )
70
- let wrappedString = string. isEmpty ? string : string. wrapText ( to: terminalWidth)
71
- await self . print ( wrappedString, terminator: terminator)
76
+ let wrappedString = self . wrappedMessage ( string) + ( terminator ?? " " )
77
+
78
+ if self . format == . json {
79
+ await self . printError ( wrappedString)
80
+ return
81
+ } else {
82
+ await self . print ( wrappedString)
83
+ }
72
84
}
73
85
74
- /// Detects the terminal width in columns
75
- private func getTerminalWidth( ) -> Int {
76
- #if os(macOS) || os(Linux)
77
- var size = winsize ( )
78
- #if os(OpenBSD)
79
- // TIOCGWINSZ is a complex macro, so we need the flattened value.
80
- let tiocgwinsz = UInt ( 0x4008_7468 )
81
- let result = ioctl ( STDOUT_FILENO, tiocgwinsz, & size)
82
- #else
83
- let result = ioctl ( STDOUT_FILENO, UInt ( TIOCGWINSZ) , & size)
84
- #endif
86
+ private func wrappedMessage( _ string: String ) -> String {
87
+ let terminalWidth = self . terminal. width ( )
88
+ return string. isEmpty ? string : string. wrapText ( to: terminalWidth)
89
+ }
85
90
86
- if result == 0 && Int ( size. ws_col) > 0 {
87
- return Int ( size. ws_col)
91
+ public func printError( _ string: String = " " ) async {
92
+ if let handler = self . errorOutputHandler {
93
+ await handler. handleOutputLine ( string)
94
+ } else {
95
+ if let data = ( string + " \n " ) . data ( using: . utf8) {
96
+ try ? FileHandle . standardError. write ( contentsOf: data)
97
+ }
88
98
}
89
- #endif
90
- return 80 // Default width if terminal size detection fails
99
+ }
100
+
101
+ public func output( _ data: OutputData ) async {
102
+ let formattedOutput : String
103
+ switch self . format {
104
+ case . text:
105
+ formattedOutput = TextOutputFormatter ( ) . format ( data)
106
+ case . json:
107
+ formattedOutput = JSONOutputFormatter ( ) . format ( data)
108
+ }
109
+ await self . print ( formattedOutput)
91
110
}
92
111
93
112
public func readLine( prompt: String ) async -> String ? {
94
- await self . print ( prompt, terminator: " : \n " )
113
+ await self . message ( prompt, terminator: " : \n " )
95
114
guard let provider = self . inputProvider else {
96
115
return Swift . readLine ( strippingNewline: true )
97
116
}
98
117
return await provider. readLine ( )
99
118
}
100
119
101
120
public func promptForConfirmation( defaultBehavior: Bool ) async -> Bool {
121
+ if self . format == . json {
122
+ await self . message ( " Assuming \( defaultBehavior ? " yes " : " no " ) due to JSON format " )
123
+ return defaultBehavior
124
+ }
102
125
let options : String
103
126
if defaultBehavior {
104
127
options = " (Y/n) "
@@ -112,7 +135,7 @@ public struct SwiftlyCoreContext: Sendable {
112
135
?? ( defaultBehavior ? " y " : " n " ) ) . lowercased ( )
113
136
114
137
guard [ " y " , " n " , " " ] . contains ( answer) else {
115
- await self . print (
138
+ await self . message (
116
139
" Please input either \" y \" or \" n \" , or press ENTER to use the default. " )
117
140
continue
118
141
}
0 commit comments