Files

113 lines
3.2 KiB
Swift

/// A type that prints database rows.
///
/// Types that conform to `DumpFormat` feed the printing methods such as
/// ``DatabaseReader/dumpContent(format:to:)`` and
/// ``Database/dumpSQL(_:format:to:)``.
///
/// Most built-in formats are inspired from the
/// [output formats of the SQLite command line tool](https://sqlite.org/cli.html#changing_output_formats).
///
/// ## Topics
///
/// ### Built-in Formats
///
/// - ``debug(header:separator:nullValue:)``
/// - ``json(encoder:)``
/// - ``line(nullValue:)``
/// - ``list(header:separator:nullValue:)``
/// - ``quote(header:separator:)``
///
/// ### Supporting Types
///
/// - ``DebugDumpFormat``
/// - ``JSONDumpFormat``
/// - ``LineDumpFormat``
/// - ``ListDumpFormat``
/// - ``QuoteDumpFormat``
/// - ``DumpStream``
///
/// ### Implementing a custom format
///
/// [**🔥 EXPERIMENTAL**](https://github.com/groue/GRDB.swift/blob/master/README.md#what-are-experimental-features)
///
/// - ``writeRow(_:statement:to:)``
/// - ``finalize(_:statement:to:)``
public protocol DumpFormat {
/// Writes a row from the given statement.
///
/// - Parameters:
/// - db: A connection to the database
/// - statement: The iterated statement
/// - stream: A stream for text output.
mutating func writeRow(
_ db: Database,
statement: Statement,
to stream: inout DumpStream) throws
/// All rows from the statement have been printed.
///
/// - Parameters:
/// - db: A connection to the database
/// - statement: The statement that was iterated.
/// - stream: A stream for text output.
mutating func finalize(
_ db: Database,
statement: Statement,
to stream: inout DumpStream)
}
/// A TextOutputStream that prints to standard output
struct StandardOutputStream: TextOutputStream {
func write(_ string: String) {
print(string, terminator: "")
}
}
/// A text output stream suited for printing database content.
///
/// [**🔥 EXPERIMENTAL**](https://github.com/groue/GRDB.swift/blob/master/README.md#what-are-experimental-features)
public struct DumpStream {
var base: any TextOutputStream
var needsMarginLine = false
init(_ base: (any TextOutputStream)?) {
self.base = base ?? StandardOutputStream()
}
/// Will write `"\n"` before the next non-empty string.
public mutating func margin() {
needsMarginLine = true
}
}
extension DumpStream: TextOutputStream {
public mutating func write(_ string: String) {
if needsMarginLine && !string.isEmpty {
needsMarginLine = false
if string.first != "\n" {
base.write("\n")
}
}
base.write(string)
}
}
extension TextOutputStream {
mutating func writeln(_ string: String) {
write(string)
write("\n")
}
}
extension String {
func leftPadding(toLength newLength: Int, withPad padString: String) -> String {
precondition(padString.count == 1)
if count < newLength {
return String(repeating: padString, count: newLength - count) + self
} else {
let startIndex = index(startIndex, offsetBy: count - newLength)
return String(self[startIndex...])
}
}
}