Skip to content

Only use FoundationEssentials if available #749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Sources/ArgumentParser/Completions/CompletionsGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,29 @@ extension Sequence where Element == ParsableCommand.Type {
}

extension String {
#if canImport(FoundationEssentials)
func shellEscapeForSingleQuotedString(iterationCount: UInt64 = 1) -> Self {
iterationCount == 0
? self
: replacing("'", with: "'\\''")
.shellEscapeForSingleQuotedString(iterationCount: iterationCount - 1)
}
#else
Comment on lines +206 to +213
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this canImport check necessary? replacing(_:with:) comes from the Swift standard library (via _StringProcessing), not FoundationEssentials. We may be able to do away with the other branch on these altogether.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for all the branches where the result is the same, we should remove the conditional code and use the stdlib version theres no need to use the variants from Foundation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is the stdlib version. On macOS this is only available on newer macOS versions. But since on macOS you anyhow have foundation I chose this route.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd personally prefer manually backdeploying these stdlib functions then.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rauhul Should I try to copy an implementation from _StringProcessing? Alternatively, is there something like #if canImport(SwiftStdlib, 6.0) that we could use here?

func shellEscapeForSingleQuotedString(iterationCount: UInt64 = 1) -> Self {
iterationCount == 0
? self
: replacingOccurrences(of: "'", with: "'\\''")
.shellEscapeForSingleQuotedString(iterationCount: iterationCount - 1)
}

#endif

#if canImport(FoundationEssentials)
func shellEscapeForVariableName() -> Self {
replacing("-", with: "_")
}
#else
func shellEscapeForVariableName() -> Self {
replacingOccurrences(of: "-", with: "_")
}
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,15 @@ extension Name {
}

extension String {
#if canImport(FoundationEssentials)
fileprivate func fishEscape() -> String {
replacing("'", with: #"\'"#)
}
#else
fileprivate func fishEscape() -> String {
replacingOccurrences(of: "'", with: #"\'"#)
}
#endif
}

extension FishCompletionsGenerator {
Expand Down
10 changes: 8 additions & 2 deletions Sources/ArgumentParser/Completions/ZshCompletionsGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,20 @@ extension [ParsableCommand.Type] {
}

extension String {
#if canImport(FoundationEssentials)
fileprivate func zshEscapeForSingleQuotedExplanation() -> String {
replacing(#/[\\\[\]]/#, with: { "\\\($0.output)" })
.shellEscapeForSingleQuotedString()
}
#else
fileprivate func zshEscapeForSingleQuotedExplanation() -> String {
replacingOccurrences(
of: #"[\\\[\]]"#,
with: #"\\$0"#,
options: .regularExpression
)
options: .regularExpression)
.shellEscapeForSingleQuotedString()
}
#endif
}

extension ArgumentDefinition {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct _WrappedParsableCommand<P: ParsableArguments>: ParsableCommand {

// If the type is named something like "TransformOptions", we only want
// to use "transform" as the command name.
if let optionsRange = name.range(of: "_options"),
if let optionsRange = name._range(of: "_options"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try firstRange(of:) here for the non-Foundation version, instead of copying in an implementation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'firstRange(of:)' is only available in macOS 13.0 or newer

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@natecook1000 what is your preferred course of action? Keep the copied implementation here?

optionsRange.upperBound == name.endIndex
{
return String(name[..<optionsRange.lowerBound])
Expand Down
9 changes: 9 additions & 0 deletions Sources/ArgumentParser/Parsing/CommandParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,19 @@
//===----------------------------------------------------------------------===//

#if swift(>=6.0)
#if canImport(FoundationEssentials)
internal import class FoundationEssentials.ProcessInfo
#else
internal import class Foundation.ProcessInfo
#endif
#else
#if canImport(FoundationEssentials)
import class FoundationEssentials.ProcessInfo
#else
import class Foundation.ProcessInfo
#endif
#endif


struct CommandError: Error {
var commandStack: [ParsableCommand.Type]
Expand Down
8 changes: 8 additions & 0 deletions Sources/ArgumentParser/Usage/DumpHelpGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@

#if swift(>=6.0)
internal import ArgumentParserToolInfo
#if canImport(FoundationEssentials)
internal import class FoundationEssentials.JSONEncoder
#else
internal import class Foundation.JSONEncoder
#endif
#else
import ArgumentParserToolInfo
#if canImport(FoundationEssentials)
import class FoundationEssentials.JSONEncoder
#else
import class Foundation.JSONEncoder
#endif
#endif

internal struct DumpHelpGenerator {
private var toolInfo: ToolInfoV0
Expand Down
12 changes: 12 additions & 0 deletions Sources/ArgumentParser/Usage/MessageInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@
//===----------------------------------------------------------------------===//

#if swift(>=6.0)
#if canImport(FoundationEssentials)
internal import protocol FoundationEssentials.LocalizedError
#else
internal import protocol Foundation.LocalizedError
internal import class Foundation.NSError
#endif
#else
#if canImport(FoundationEssentials)
import protocol FoundationEssentials.LocalizedError
#else
import protocol Foundation.LocalizedError
import class Foundation.NSError
#endif
#endif

enum MessageInfo {
case help(text: String)
Expand Down Expand Up @@ -135,11 +143,15 @@ enum MessageInfo {
// No way to unwrap bind description in pattern
self = .other(message: error.errorDescription!, exitCode: .failure)
default:
#if canImport(FoundationEssentials)
self = .other(message: String(describing: error), exitCode: .failure)
#else
if Swift.type(of: error) is NSError.Type {
self = .other(message: error.localizedDescription, exitCode: .failure)
} else {
self = .other(message: String(describing: error), exitCode: .failure)
}
#endif
}
} else if let parserError = parserError {
let usage: String = {
Expand Down
8 changes: 8 additions & 0 deletions Sources/ArgumentParser/Usage/UsageGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@
//===----------------------------------------------------------------------===//

#if swift(>=6.0)
#if canImport(FoundationEssentials)
internal import protocol FoundationEssentials.LocalizedError
#else
internal import protocol Foundation.LocalizedError
#endif
#else
#if canImport(FoundationEssentials)
import protocol FoundationEssentials.LocalizedError
#else
import protocol Foundation.LocalizedError
#endif
#endif

struct UsageGenerator {
var toolName: String
Expand Down
82 changes: 82 additions & 0 deletions Sources/ArgumentParser/Utilities/BidirectionalCollection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//


extension BidirectionalCollection {
// Equal to calling `index(&idx, offsetBy: -other.count)` with just one loop
func _index<S: BidirectionalCollection>(_ index: Index, backwardsOffsetByCountOf other: S) -> Index? {
var idx = index
var otherIdx = other.endIndex
while otherIdx > other.startIndex {
guard idx > startIndex else {
// other.count > self.count: bail
return nil
}
other.formIndex(before: &otherIdx)
formIndex(before: &idx)
}
return idx
}

func _range<S: BidirectionalCollection>(of other: S, anchored: Bool = false, backwards: Bool = false) -> Range<Index>? where S.Element == Element, Element : Equatable {
var result: Range<Index>? = nil
var fromLoc: Index
var toLoc: Index
if backwards {
guard let idx = _index(endIndex, backwardsOffsetByCountOf: other) else {
// other.count > string.count: bail
return nil
}
fromLoc = idx

toLoc = anchored ? fromLoc : startIndex
} else {
fromLoc = startIndex
if anchored {
toLoc = fromLoc
} else {
guard let idx = _index(endIndex, backwardsOffsetByCountOf: other) else {
return nil
}
toLoc = idx
}
}

let delta = fromLoc <= toLoc ? 1 : -1

while true {
var str1Index = fromLoc
var str2Index = other.startIndex

while str2Index < other.endIndex && str1Index < endIndex {
if self[str1Index] != other[str2Index] {
break
}
formIndex(after: &str1Index)
other.formIndex(after: &str2Index)
}

if str2Index == other.endIndex {
result = fromLoc..<str1Index
break
}

if fromLoc == toLoc {
break
}

formIndex(&fromLoc, offsetBy: delta)
}

return result
}
}
Loading