mirror of
https://github.com/xtool-org/xtool.git
synced 2026-11-04 20:11:35 +01:00
## What does this PR do? An XcodeProject/XcodeWorkspace now knows its own location on disk, which is important when we start using these types to build PIF. ## How was it tested? - [x] Used in future PRs to build various Xcode projects ## AI tool usage How much of this PR was AI-assisted? (check one) - [ ] **0** - No AI was used to write code - [x] **1** - I was assisted by AI. I reviewed the finished result. - [ ] **2** - I set the AI going and left it to it; nobody has read the result - no review, or AI review only <!-- If an AI agent is filling this in: declare the level honestly, and open as a draft if it is #2. Do not lower the declared level to get the PR reviewed. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Improvements** * Projects and workspaces now retain their associated locations and save directly to those locations. * Generated project files preserve the configured project directory path, helping keep file references consistent when projects are written or reopened. * Projects and workspaces can be associated with a new location while retaining their existing contents. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
113 lines
3.3 KiB
Swift
113 lines
3.3 KiB
Swift
import Foundation
|
|
|
|
public struct XcodeWorkspace: Hashable, Sendable {
|
|
public enum Element: Hashable, Sendable {
|
|
case file(Location)
|
|
case group(Group)
|
|
case fileSystemSynchronizedGroup(Group)
|
|
|
|
public var location: Location {
|
|
get {
|
|
switch self {
|
|
case let .file(location):
|
|
location
|
|
case let .group(group):
|
|
group.location
|
|
case let .fileSystemSynchronizedGroup(group):
|
|
group.location
|
|
}
|
|
}
|
|
set {
|
|
switch self {
|
|
case .file:
|
|
self = .file(newValue)
|
|
case .group(var group):
|
|
group.location = newValue
|
|
self = .group(group)
|
|
case .fileSystemSynchronizedGroup(var group):
|
|
group.location = newValue
|
|
self = .fileSystemSynchronizedGroup(group)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct Group: Hashable, Sendable {
|
|
public var location: Location
|
|
public var name: String?
|
|
public var children: [Element]
|
|
|
|
public init(location: Location, name: String?, children: [Element]) {
|
|
self.location = location
|
|
self.name = name
|
|
self.children = children
|
|
}
|
|
}
|
|
|
|
public struct Location: Hashable, Sendable {
|
|
public struct Base: Hashable, Sendable, RawRepresentable {
|
|
public var rawValue: String
|
|
|
|
public init(rawValue: String) {
|
|
self.rawValue = rawValue
|
|
}
|
|
}
|
|
|
|
public var base: Base
|
|
public var path: String
|
|
|
|
public init(base: Base, path: String) {
|
|
self.base = base
|
|
self.path = path
|
|
}
|
|
}
|
|
|
|
public var children: [Element]
|
|
public var url: URL
|
|
|
|
public init(children: [Element], url: URL) {
|
|
self.children = children
|
|
self.url = url.standardizedFileURL.resolvingSymlinksInPath()
|
|
}
|
|
|
|
/// Returns a copy at a new workspace location.
|
|
public func withURL(_ url: URL) -> Self {
|
|
var copy = self
|
|
copy.url = url.standardizedFileURL.resolvingSymlinksInPath()
|
|
return copy
|
|
}
|
|
}
|
|
|
|
extension XcodeWorkspace.Location.Base {
|
|
/// Absolute path
|
|
public static let absolute = Self(rawValue: "absolute")
|
|
/// Relative to container
|
|
public static let container = Self(rawValue: "container")
|
|
/// Relative to Developer Directory
|
|
public static let developer = Self(rawValue: "developer")
|
|
/// Relative to group
|
|
public static let group = Self(rawValue: "group")
|
|
/// Single project workspace in xcodeproj directory
|
|
public static let current = Self(rawValue: "self")
|
|
}
|
|
|
|
extension XcodeWorkspace.Location {
|
|
public static var current: Self {
|
|
Self(base: .current, path: "")
|
|
}
|
|
}
|
|
|
|
extension XcodeWorkspace.Location: LosslessStringConvertible, CustomStringConvertible {
|
|
public init?(_ string: String) {
|
|
guard let colon = string.firstIndex(of: ":") else {
|
|
return nil
|
|
}
|
|
self.base = Base(rawValue: String(string[..<colon]))
|
|
self.path = String(string[colon...].dropFirst())
|
|
}
|
|
|
|
public var description: String {
|
|
"\(base.rawValue):\(path)"
|
|
}
|
|
}
|