Skip to content

Commit

Permalink
Merge pull request #69 from outfoxx/feature/pretty-yaml
Browse files Browse the repository at this point in the history
Support pretty printing and preferred flow/block style for YAML
  • Loading branch information
kdubb committed May 16, 2024
2 parents fe101f9 + c141600 commit 5468800
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 27 deletions.
26 changes: 24 additions & 2 deletions Sources/PotentYAML/YAMLEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ public class YAMLEncoder: ValueEncoder<YAML, YAMLEncoderTransform>, EncodesToStr
self.rawValue = rawValue
}

/// Attempt to output good looking YAML
///
/// Aside from selecting the style (flow or block) for mappings
/// and sequences, it limits the output width to 80 characters
/// by using folded strings when necessary.
public static let pretty = OutputFormatting(rawValue: 1 << 0)

/// Produce YAML with dictionary keys sorted in lexicographic order.
public static let sortedKeys = OutputFormatting(rawValue: 1 << 1)
}
Expand Down Expand Up @@ -80,6 +87,9 @@ public class YAMLEncoder: ValueEncoder<YAML, YAMLEncoderTransform>, EncodesToStr
/// The output format to produce. Defaults to `[]`.
public var outputFormatting: OutputFormatting = []

/// The preferred output style for sequences and mappings. Defaults to `.any`.
public var preferredCollectionStyle: YAML.CollectionStyle = .any

/// The strategy to use in encoding dates. Defaults to `.deferredToDate`.
public var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate

Expand All @@ -93,6 +103,7 @@ public class YAMLEncoder: ValueEncoder<YAML, YAMLEncoderTransform>, EncodesToStr
dataEncodingStrategy: dataEncodingStrategy,
outputFormatting: outputFormatting,
keyEncodingStrategy: keyEncodingStrategy,
preferredCollectionStyle: preferredCollectionStyle,
userInfo: userInfo
)
}
Expand All @@ -119,6 +130,7 @@ public struct YAMLEncoderTransform: InternalEncoderTransform, InternalValueSeria
public let dataEncodingStrategy: YAMLEncoder.DataEncodingStrategy
public let outputFormatting: YAMLEncoder.OutputFormatting
public let keyEncodingStrategy: KeyEncodingStrategy
public let preferredCollectionStyle: YAML.CollectionStyle
public let userInfo: [CodingUserInfoKey: Any]
}

Expand Down Expand Up @@ -358,15 +370,25 @@ public struct YAMLEncoderTransform: InternalEncoderTransform, InternalValueSeria
if options.outputFormatting.contains(.sortedKeys) {
writingOptions.insert(.sortedKeys)
}
return try YAMLSerialization.data(from: value, options: writingOptions)
if options.outputFormatting.contains(.pretty) {
writingOptions.insert(.pretty)
}
return try YAMLSerialization.data(from: value,
preferredCollectionStyle: options.preferredCollectionStyle,
options: writingOptions)
}

public static func string(from value: YAML, options: Options) throws -> String {
var writingOptions: YAMLSerialization.WritingOptions = []
if options.outputFormatting.contains(.sortedKeys) {
writingOptions.insert(.sortedKeys)
}
return try YAMLSerialization.string(from: value, options: writingOptions)
if options.outputFormatting.contains(.pretty) {
writingOptions.insert(.pretty)
}
return try YAMLSerialization.string(from: value,
preferredCollectionStyle: options.preferredCollectionStyle,
options: writingOptions)
}

}
Expand Down
21 changes: 17 additions & 4 deletions Sources/PotentYAML/YAMLSerialization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,17 @@ public enum YAMLSerialization {
}

public static let sortedKeys = WritingOptions(rawValue: 1 << 0)
public static let pretty = WritingOptions(rawValue: 1 << 1)
}

public static func data(from yaml: YAML, options: WritingOptions = []) throws -> Data {
guard let data = try string(from: yaml, options: options).data(using: .utf8) else {
public static func data(from yaml: YAML,
preferredCollectionStyle: YAML.CollectionStyle = .any,
options: WritingOptions = []) throws -> Data {
guard
let data = try string(from: yaml,
preferredCollectionStyle: preferredCollectionStyle,
options: options).data(using: .utf8)
else {
throw DecodingError
.dataCorrupted(
DecodingError
Expand All @@ -85,11 +92,17 @@ public enum YAMLSerialization {
return data
}

public static func string(from yaml: YAML, options: WritingOptions = []) throws -> String {
public static func string(from yaml: YAML,
preferredCollectionStyle: YAML.CollectionStyle = .any,
options: WritingOptions = []) throws -> String {

var output = String()

try YAMLWriter.write([yaml], sortedKeys: options.contains(.sortedKeys)) {
try YAMLWriter.write([yaml],
preferredCollectionStyle: preferredCollectionStyle,
pretty: options.contains(.pretty),
width: options.contains(.pretty) ? .normal : .infinite,
sortedKeys: options.contains(.sortedKeys)) {
guard let str = $0 else { return }
output.append(str)
}
Expand Down
79 changes: 58 additions & 21 deletions Sources/PotentYAML/YAMLWriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,18 @@ internal struct YAMLWriter {

typealias Writer = (String?) -> Void

static func write(_ documents: YAML.Sequence, sortedKeys: Bool = false, writer: @escaping Writer) throws {
enum Width {
case normal
case wide
case infinite
}

static func write(_ documents: YAML.Sequence,
preferredCollectionStyle: YAML.CollectionStyle = .block,
pretty: Bool = true,
width: Width = .normal,
sortedKeys: Bool = false,
writer: @escaping Writer) throws {

func output(
emitter: OpaquePointer?,
Expand All @@ -40,9 +51,15 @@ internal struct YAMLWriter {

try withUnsafePointer(to: writer) { writerPtr in

var flags: UInt32 = 0
if sortedKeys {
flags |= FYECF_SORT_KEYS.rawValue
var flags: UInt32 = pretty ? FYECF_MODE_PRETTY.rawValue : FYECF_DEFAULT.rawValue

switch width {
case .normal:
flags |= FYECF_WIDTH_80.rawValue
case .wide:
flags |= FYECF_WIDTH_132.rawValue
case .infinite:
flags |= FYECF_WIDTH_INF.rawValue
}

var emitterCfg = fy_emitter_cfg(
Expand All @@ -62,7 +79,10 @@ internal struct YAMLWriter {
try documents.forEach {
emit(emitter: emitter, type: FYET_DOCUMENT_START, args: 0, 0, 0)

try emit(emitter: emitter, value: $0, sortedKeys: sortedKeys)
try emit(emitter: emitter,
value: $0,
preferredCollectionStyle: preferredCollectionStyle,
sortedKeys: sortedKeys)

emit(emitter: emitter, type: FYET_DOCUMENT_END)
}
Expand All @@ -72,7 +92,10 @@ internal struct YAMLWriter {

}

private static func emit(emitter: OpaquePointer, value: YAML, sortedKeys: Bool) throws {
private static func emit(emitter: OpaquePointer,
value: YAML,
preferredCollectionStyle: YAML.CollectionStyle,
sortedKeys: Bool) throws {

switch value {
case .null(anchor: let anchor):
Expand All @@ -92,10 +115,22 @@ internal struct YAMLWriter {
emit(emitter: emitter, scalar: bool ? "true" : "false", style: FYSS_PLAIN, anchor: anchor, tag: nil)

case .sequence(let sequence, style: let style, tag: let tag, anchor: let anchor):
try emit(emitter: emitter, sequence: sequence, sortedKeys: sortedKeys, style: style, anchor: anchor, tag: tag)
try emit(emitter: emitter,
sequence: sequence,
style: style,
preferredStyle: preferredCollectionStyle,
sortedKeys: sortedKeys,
anchor: anchor,
tag: tag)

case .mapping(let mapping, style: let style, tag: let tag, anchor: let anchor):
try emit(emitter: emitter, mapping: mapping, sortedKeys: sortedKeys, style: style, anchor: anchor, tag: tag)
try emit(emitter: emitter,
mapping: mapping,
style: style,
preferredStyle: preferredCollectionStyle,
sortedKeys: sortedKeys,
anchor: anchor,
tag: tag)

case .alias(let alias):
emit(emitter: emitter, alias: alias)
Expand All @@ -106,48 +141,50 @@ internal struct YAMLWriter {
private static func emit(
emitter: OpaquePointer,
mapping: YAML.Mapping,
sortedKeys: Bool,
style: YAML.CollectionStyle,
preferredStyle: YAML.CollectionStyle,
sortedKeys: Bool,
anchor: String?,
tag: YAML.Tag?
) throws {
var mapping = mapping
emit(
emitter: emitter,
type: FYET_MAPPING_START,
args: style.nodeStyle.rawValue,
args: style.nodeStyle(preferred: preferredStyle).rawValue,
anchor.varArg,
(tag?.rawValue).varArg
)
var mapping = mapping
if sortedKeys {
mapping = mapping.sorted {
$0.key.description < $1.key.description
}
}
try mapping.forEach { entry in
try emit(emitter: emitter, value: entry.key, sortedKeys: sortedKeys)
try emit(emitter: emitter, value: entry.value, sortedKeys: sortedKeys)
try emit(emitter: emitter, value: entry.key, preferredCollectionStyle: preferredStyle, sortedKeys: sortedKeys)
try emit(emitter: emitter, value: entry.value, preferredCollectionStyle: preferredStyle, sortedKeys: sortedKeys)
}
emit(emitter: emitter, type: FYET_MAPPING_END)
}

private static func emit(
emitter: OpaquePointer,
sequence: [YAML],
sortedKeys: Bool,
style: YAML.CollectionStyle,
preferredStyle: YAML.CollectionStyle,
sortedKeys: Bool,
anchor: String?,
tag: YAML.Tag?
) throws {
emit(
emitter: emitter,
type: FYET_SEQUENCE_START,
args: style.nodeStyle.rawValue,
args: style.nodeStyle(preferred: preferredStyle).rawValue,
anchor.varArg,
(tag?.rawValue).varArg
)
try sequence.forEach { element in
try emit(emitter: emitter, value: element, sortedKeys: sortedKeys)
try emit(emitter: emitter, value: element, preferredCollectionStyle: preferredStyle, sortedKeys: sortedKeys)
}
emit(emitter: emitter, type: FYET_SEQUENCE_END)
}
Expand Down Expand Up @@ -208,11 +245,11 @@ extension Optional where Wrapped == String {

extension YAML.CollectionStyle {

var nodeStyle: fy_node_style {
switch self {
case .any: return FYNS_ANY
case .flow: return FYNS_FLOW
case .block: return FYNS_BLOCK
func nodeStyle(preferred: Self) -> fy_node_style {
switch (self, preferred) {
case (.any, .any): return FYNS_ANY
case (.any, .flow), (.flow, _): return FYNS_FLOW
case (.any, .block), (.block, _): return FYNS_BLOCK
}
}

Expand Down

0 comments on commit 5468800

Please sign in to comment.