@@ -37,69 +37,91 @@ public struct SwiftlyCoreContext: Sendable {
3737 /// The output handler to use, if any.
3838 public var outputHandler : ( any OutputHandler ) ?
3939
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
4144 public var inputProvider : ( any InputProvider ) ?
4245
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) {
4453 self . httpClient = SwiftlyHTTPClient ( httpRequestExecutor: HTTPRequestExecutorImpl ( ) )
4554 self . currentDirectory = fs. cwd
55+ self . format = format
56+ self . terminal = SystemTerminal ( )
4657 }
4758
4859 public init ( httpClient: SwiftlyHTTPClient ) {
4960 self . httpClient = httpClient
5061 self . currentDirectory = fs. cwd
62+ self . terminal = SystemTerminal ( )
5163 }
5264
5365 /// Pass the provided string to the set output handler if any.
5466 /// If no output handler has been set, just print to stdout.
55- public func print( _ string: String = " " , terminator: String ? = nil ) async {
56-
67+ public func print( _ string: String = " " ) async {
5768 guard let handler = self . outputHandler else {
58- if let terminator {
59- Swift . print ( string, terminator: terminator)
60- } else {
61- Swift . print ( string)
62- }
69+ Swift . print ( string)
6370 return
6471 }
65- await handler. handleOutputLine ( string + ( terminator ?? " " ) )
72+ await handler. handleOutputLine ( string)
6673 }
6774
6875 public func message( _ string: String = " " , terminator: String ? = nil ) async {
69- // Get terminal size or use default width
70- let terminalWidth = self . getTerminalWidth ( )
71- let wrappedString = string. isEmpty ? string : string. wrapText ( to: terminalWidth)
72- 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+ }
7384 }
7485
75- /// Detects the terminal width in columns
76- private func getTerminalWidth( ) -> Int {
77- #if os(macOS) || os(Linux)
78- var size = winsize ( )
79- #if os(OpenBSD)
80- // TIOCGWINSZ is a complex macro, so we need the flattened value.
81- let tiocgwinsz = UInt ( 0x4008_7468 )
82- let result = ioctl ( STDOUT_FILENO, tiocgwinsz, & size)
83- #else
84- let result = ioctl ( STDOUT_FILENO, UInt ( TIOCGWINSZ) , & size)
85- #endif
86+ private func wrappedMessage( _ string: String ) -> String {
87+ let terminalWidth = self . terminal. width ( )
88+ return string. isEmpty ? string : string. wrapText ( to: terminalWidth)
89+ }
90+
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+ }
98+ }
99+ }
86100
87- if result == 0 && Int ( size. ws_col) > 0 {
88- return Int ( size. ws_col)
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)
89108 }
90- #endif
91- return 80 // Default width if terminal size detection fails
109+ await self . print ( formattedOutput)
92110 }
93111
94112 public func readLine( prompt: String ) async -> String ? {
95- await self . print ( prompt, terminator: " : \n " )
113+ await self . message ( prompt, terminator: " : \n " )
96114 guard let provider = self . inputProvider else {
97115 return Swift . readLine ( strippingNewline: true )
98116 }
99117 return await provider. readLine ( )
100118 }
101119
102120 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+ }
103125 let options : String
104126 if defaultBehavior {
105127 options = " (Y/n) "
@@ -113,7 +135,7 @@ public struct SwiftlyCoreContext: Sendable {
113135 ?? ( defaultBehavior ? " y " : " n " ) ) . lowercased ( )
114136
115137 guard [ " y " , " n " , " " ] . contains ( answer) else {
116- await self . print (
138+ await self . message (
117139 " Please input either \" y \" or \" n \" , or press ENTER to use the default. " )
118140 continue
119141 }
0 commit comments