Skip to content

Transition away from Foundation.URL #6706

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

Merged
merged 1 commit into from
Jul 18, 2023
Merged
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
1 change: 1 addition & 0 deletions Sources/Basics/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ add_library(Basics
FileSystem/TemporaryFile.swift
FileSystem/TSCAdapters.swift
FileSystem/VFSOverlay.swift
SourceControlURL.swift
HTTPClient/HTTPClient.swift
HTTPClient/HTTPClientConfiguration.swift
HTTPClient/HTTPClientError.swift
Expand Down
53 changes: 53 additions & 0 deletions Sources/Basics/SourceControlURL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Foundation

public struct SourceControlURL: Codable, Equatable, Hashable, Sendable {
private let urlString: String

public init(stringLiteral: String) {
self.urlString = stringLiteral
}

public init(_ urlString: String) {
self.urlString = urlString
}

public init(_ url: URL) {
self.urlString = url.absoluteString
}

public var absoluteString: String {
return self.urlString
}

public var lastPathComponent: String {
return (self.urlString as NSString).lastPathComponent
}

public var url: URL? {
return URL(string: self.urlString)
}
}

extension SourceControlURL: CustomStringConvertible {
public var description: String {
return self.urlString
}
}

extension SourceControlURL: ExpressibleByStringInterpolation {
}

extension SourceControlURL: ExpressibleByStringLiteral {
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ extension PackageCollectionModel.V1 {

// TODO: validate package url?
private func validate(package: Collection.Package, messages: inout [ValidationMessage]) {
let packageID = "\(PackageIdentity(url: package.url).description) (\(package.url.absoluteString))"
let packageID = "\(PackageIdentity(url: SourceControlURL(package.url)).description) (\(package.url.absoluteString))"

guard !package.versions.isEmpty else {
messages.append(.error("Package \(packageID) does not have any versions.", property: "package.versions"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ struct GitHubPackageMetadataProvider: PackageMetadataProvider, Closable {
callback: @escaping (Result<Model.PackageBasicMetadata, Error>, PackageMetadataProviderContext?) -> Void
) {
guard let baseURL = Self.apiURL(location) else {
return self.errorCallback(GitHubPackageMetadataProviderError.invalidGitURL(location), apiHost: nil, callback: callback)
return self.errorCallback(GitHubPackageMetadataProviderError.invalidSourceControlURL(location), apiHost: nil, callback: callback)
}

if let cached = try? self.cache?.get(key: identity.description) {
Expand Down Expand Up @@ -334,7 +334,7 @@ struct GitHubPackageMetadataProvider: PackageMetadataProvider, Closable {
}

enum GitHubPackageMetadataProviderError: Error, Equatable {
case invalidGitURL(String)
case invalidSourceControlURL(String)
case invalidResponse(URL, String)
case permissionDenied(URL)
case invalidAuthToken(URL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ struct JSONPackageCollectionProvider: PackageCollectionProvider {

// If package identity is set, use that. Otherwise create one from URL.
return .init(
identity: package.identity.map { PackageIdentity.plain($0) } ?? PackageIdentity(url: package.url),
identity: package.identity.map { PackageIdentity.plain($0) } ?? PackageIdentity(url: SourceControlURL(package.url)),
location: package.url.absoluteString,
summary: package.summary,
keywords: package.keywords,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,15 +265,14 @@ private enum StorageModel {

let fingerprintsByContentType = try Dictionary(
throwingUniqueKeysWithValues: fingerprintsForKind.map { _, storedFingerprint in
guard let originURL = URL(string: storedFingerprint.origin) else {
throw SerializationError.invalidURL(storedFingerprint.origin)
}

let origin: Fingerprint.Origin
switch kind {
case .sourceControl:
origin = .sourceControl(originURL)
origin = .sourceControl(SourceControlURL(storedFingerprint.origin))
case .registry:
guard let originURL = URL(string: storedFingerprint.origin) else {
throw SerializationError.invalidURL(storedFingerprint.origin)
}
origin = .registry(originURL)
}

Expand Down
7 changes: 4 additions & 3 deletions Sources/PackageFingerprint/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import struct Foundation.URL

import Basics
import PackageModel
import struct TSCUtility.Version

Expand All @@ -34,7 +35,7 @@ extension Fingerprint {
}

public enum Origin: Equatable, CustomStringConvertible {
case sourceControl(URL)
case sourceControl(SourceControlURL)
case registry(URL)

public var kind: Fingerprint.Kind {
Expand All @@ -46,12 +47,12 @@ extension Fingerprint {
}
}

public var url: URL? {
public var url: SourceControlURL? {
switch self {
case .sourceControl(let url):
return url
case .registry(let url):
return url
return SourceControlURL(url.absoluteString)
}
}

Expand Down
4 changes: 1 addition & 3 deletions Sources/PackageGraph/DependencyMirrors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,8 @@ public final class DependencyMirrors: Equatable {
return PackageIdentity.plain(location)
} else if let path = try? AbsolutePath(validating: location) {
return PackageIdentity(path: path)
} else if let url = URL(string: location) {
return PackageIdentity(url: url)
} else {
throw StringError("invalid location \(location), cannot extract identity")
return PackageIdentity(url: SourceControlURL(location))
}
}
}
Expand Down
9 changes: 2 additions & 7 deletions Sources/PackageGraph/PinsStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -428,10 +428,8 @@ extension PinsStore.Pin {
var packageRef: PackageReference
if let path = try? AbsolutePath(validating: location) {
packageRef = .localSourceControl(identity: identity, path: path)
} else if let url = URL(string: location) {
packageRef = .remoteSourceControl(identity: identity, url: url)
} else {
throw StringError("invalid package location \(location)")
packageRef = .remoteSourceControl(identity: identity, url: SourceControlURL(location))
}
if let newName = pin.package {
packageRef = packageRef.withName(newName)
Expand Down Expand Up @@ -466,10 +464,7 @@ extension PinsStore.Pin {
case .localSourceControl:
packageRef = try .localSourceControl(identity: identity, path: AbsolutePath(validating: location))
case .remoteSourceControl:
guard let url = URL(string: location) else {
throw StringError("invalid url location: \(location)")
}
packageRef = .remoteSourceControl(identity: identity, url: url)
packageRef = .remoteSourceControl(identity: identity, url: SourceControlURL(location))
case .registry:
packageRef = .registry(identity: identity)
}
Expand Down
11 changes: 6 additions & 5 deletions Sources/PackageLoading/ManifestJSONParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import PackageModel

import struct Basics.AbsolutePath
import protocol Basics.FileSystem
import struct Basics.SourceControlURL
import struct Basics.InternalError
import struct Basics.RelativePath

Expand Down Expand Up @@ -237,7 +238,8 @@ enum ManifestJSONParser {
requirement: requirement,
productFilter: .everything
)
} else if let url = URL(string: location){
} else {
let url = SourceControlURL(location)
// in the future this will check with the registries for the identity of the URL
let identity = try identityResolver.resolveIdentity(for: url)
return .remoteSourceControl(
Expand All @@ -247,8 +249,6 @@ enum ManifestJSONParser {
requirement: requirement,
productFilter: .everything
)
} else {
throw StringError("invalid location: \(location)")
}
}

Expand All @@ -268,8 +268,9 @@ enum ManifestJSONParser {
productFilter: .everything
)
} else if let url = URL(string: location){
let SourceControlURL = SourceControlURL(url)
// in the future this will check with the registries for the identity of the URL
let identity = try identityResolver.resolveIdentity(for: url)
let identity = try identityResolver.resolveIdentity(for: SourceControlURL)
let sourceControlRequirement: PackageDependency.SourceControl.Requirement
switch requirement {
case .exact(let value):
Expand All @@ -280,7 +281,7 @@ enum ManifestJSONParser {
return .remoteSourceControl(
identity: identity,
nameForTargetDependencyResolutionOnly: identity.description,
url: url,
url: SourceControlURL,
requirement: sourceControlRequirement,
productFilter: .everything
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private struct CodableRegistryReleaseMetadata: Codable {
public let description: String?
public let licenseURL: URL?
public let readmeURL: URL?
public let scmRepositoryURLs: [URL]?
public let scmRepositoryURLs: [SourceControlURL]?

init(_ seed: RegistryReleaseMetadata) {
switch seed.source {
Expand Down
30 changes: 19 additions & 11 deletions Sources/PackageMetadata/PackageMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public struct Package {
public enum Source {
case indexAndCollections(collections: [PackageCollectionsModel.CollectionIdentifier], indexes: [URL])
case registry(url: URL)
case sourceControl(url: URL)
case sourceControl(url: SourceControlURL)
}

public struct Resource: Sendable {
Expand Down Expand Up @@ -67,7 +67,7 @@ public struct Package {
// Per version metadata based on the latest version that we include here for convenience.
public let licenseURL: URL?
public let readmeURL: URL?
public let repositoryURLs: [URL]?
public let repositoryURLs: [SourceControlURL]?
public let resources: [Resource]
public let author: Author?
public let description: String?
Expand All @@ -82,7 +82,7 @@ public struct Package {
versions: [Version],
licenseURL: URL? = nil,
readmeURL: URL? = nil,
repositoryURLs: [URL]?,
repositoryURLs: [SourceControlURL]?,
resources: [Resource],
author: Author?,
description: String?,
Expand Down Expand Up @@ -133,11 +133,21 @@ public struct PackageSearchClient {
}

// FIXME: This matches the current implementation, but we may want be smarter about it?
private func guessReadMeURL(baseURL: SourceControlURL, defaultBranch: String) -> URL? {
if let baseURL = baseURL.url {
return guessReadMeURL(baseURL: baseURL, defaultBranch: defaultBranch)
} else {
return nil
}
}

private func guessReadMeURL(baseURL: URL, defaultBranch: String) -> URL {
baseURL.appendingPathComponent("raw").appendingPathComponent(defaultBranch).appendingPathComponent("README.md")
}

private func guessReadMeURL(alternateLocations: [URL]?) -> URL? {


private func guessReadMeURL(alternateLocations: [SourceControlURL]?) -> URL? {
if let alternateURL = alternateLocations?.first {
// FIXME: This is pretty crude, we should let the registry metadata provide the value instead.
return guessReadMeURL(baseURL: alternateURL, defaultBranch: "main")
Expand All @@ -148,7 +158,7 @@ public struct PackageSearchClient {
private struct Metadata {
public let licenseURL: URL?
public let readmeURL: URL?
public let repositoryURLs: [URL]?
public let repositoryURLs: [SourceControlURL]?
public let resources: [Package.Resource]
public let author: Package.Author?
public let description: String?
Expand Down Expand Up @@ -231,9 +241,7 @@ public struct PackageSearchClient {
// as a URL or there are any errors during the process, we fall back to searching the configured
// index or package collections.
let fetchStandalonePackageByURL = { (error: Error?) in
guard let url = URL(string: query) else {
return search(error)
}
let url = SourceControlURL(query)

do {
try withTemporaryDirectory(removeTreeOnDeinit: true) { (tempDir: AbsolutePath) in
Expand Down Expand Up @@ -303,7 +311,7 @@ public struct PackageSearchClient {
self.getVersionMetadata(package: identity, version: version) { result in
let licenseURL: URL?
let readmeURL: URL?
let repositoryURLs: [URL]?
let repositoryURLs: [SourceControlURL]?
let resources: [Package.Resource]
let author: Package.Author?
let description: String?
Expand Down Expand Up @@ -372,7 +380,7 @@ public struct PackageSearchClient {
}

public func lookupIdentities(
scmURL: URL,
scmURL: SourceControlURL,
timeout: DispatchTimeInterval? = .none,
observabilityScope: ObservabilityScope,
callbackQueue: DispatchQueue,
Expand All @@ -392,7 +400,7 @@ public struct PackageSearchClient {
timeout: DispatchTimeInterval? = .none,
observabilityScope: ObservabilityScope,
callbackQueue: DispatchQueue,
completion: @escaping (Result<Set<URL>, Error>) -> Void
completion: @escaping (Result<Set<SourceControlURL>, Error>) -> Void
) {
registryClient.getPackageMetadata(
package: package,
Expand Down
12 changes: 4 additions & 8 deletions Sources/PackageModel/IdentityResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Foundation
// TODO: refactor this when adding registry support
public protocol IdentityResolver {
func resolveIdentity(for packageKind: PackageReference.Kind) throws -> PackageIdentity
func resolveIdentity(for url: URL) throws -> PackageIdentity
func resolveIdentity(for url: SourceControlURL) throws -> PackageIdentity
func resolveIdentity(for path: AbsolutePath) throws -> PackageIdentity
func mappedLocation(for location: String) -> String
func mappedIdentity(for identity: PackageIdentity) throws -> PackageIdentity
Expand Down Expand Up @@ -49,25 +49,21 @@ public struct DefaultIdentityResolver: IdentityResolver {
}
}

public func resolveIdentity(for url: URL) throws -> PackageIdentity {
public func resolveIdentity(for url: SourceControlURL) throws -> PackageIdentity {
let location = self.mappedLocation(for: url.absoluteString)
if let path = try? AbsolutePath(validating: location) {
return PackageIdentity(path: path)
} else if let url = URL(string: location) {
return PackageIdentity(url: url)
} else {
throw StringError("invalid mapped location: \(location) for \(url)")
return PackageIdentity(url: SourceControlURL(location))
}
}

public func resolveIdentity(for path: AbsolutePath) throws -> PackageIdentity {
let location = self.mappedLocation(for: path.pathString)
if let path = try? AbsolutePath(validating: location) {
return PackageIdentity(path: path)
} else if let url = URL(string: location) {
return PackageIdentity(url: url)
} else {
throw StringError("invalid mapped location: \(location) for \(path)")
return PackageIdentity(url: SourceControlURL(location))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public enum PackageDependency: Equatable, Hashable, Sendable {

public enum Location: Equatable, Hashable, Sendable {
case local(AbsolutePath)
case remote(URL)
case remote(SourceControlURL)
}
}

Expand Down Expand Up @@ -173,7 +173,7 @@ public enum PackageDependency: Equatable, Hashable, Sendable {

public static func remoteSourceControl(identity: PackageIdentity,
nameForTargetDependencyResolutionOnly: String?,
url: URL,
url: SourceControlURL,
requirement: SourceControl.Requirement,
productFilter: ProductFilter
) -> Self {
Expand Down
Loading