126 lines
4 KiB
Swift
126 lines
4 KiB
Swift
|
|
import Foundation
|
||
|
|
|
||
|
|
public enum SaplingDomainError: Error, Equatable, Sendable {
|
||
|
|
case workspaceCannotBeInsideProject
|
||
|
|
case projectMustBeGitRepository
|
||
|
|
case subprojectPathCannotBeEmpty
|
||
|
|
}
|
||
|
|
|
||
|
|
public enum SaplingRules {
|
||
|
|
public static func validateWorkspace(_ workspace: Workspace) throws {
|
||
|
|
for item in workspace.items {
|
||
|
|
try validateWorkspaceItem(item)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static func validateProject(_ project: Project) throws {
|
||
|
|
guard project.gitRepository.rootURL == project.repositoryURL else {
|
||
|
|
throw SaplingDomainError.projectMustBeGitRepository
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static func validateSubproject(_ subproject: Subproject) throws {
|
||
|
|
guard !subproject.path.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
||
|
|
throw SaplingDomainError.subprojectPathCannotBeEmpty
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static func validateWorkspaceItem(_ item: WorkspaceItem) throws {
|
||
|
|
switch item {
|
||
|
|
case .folder(let folder):
|
||
|
|
for child in folder.children {
|
||
|
|
try validateWorkspaceItem(child)
|
||
|
|
}
|
||
|
|
case .project(let project):
|
||
|
|
try validateProject(project)
|
||
|
|
case .subproject(let subproject):
|
||
|
|
try validateSubproject(subproject)
|
||
|
|
case .file:
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public enum SaplingSampleData {
|
||
|
|
public static let rootURL = URL(fileURLWithPath: "/tmp/SaplingSample")
|
||
|
|
|
||
|
|
public static var document: MarkdownDocument {
|
||
|
|
MarkdownDocument(
|
||
|
|
url: rootURL.appendingPathComponent("Research/README.md"),
|
||
|
|
title: "README",
|
||
|
|
content: """
|
||
|
|
# Sapling
|
||
|
|
|
||
|
|
Sapling is a **Git-native** Markdown workspace.
|
||
|
|
|
||
|
|
- [x] Model local workspaces
|
||
|
|
- [ ] Render inactive Markdown lines
|
||
|
|
|
||
|
|
```swift
|
||
|
|
let workspace = Workspace(name: "Research", rootURL: url)
|
||
|
|
```
|
||
|
|
|
||
|
|

|
||
|
|
"""
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
public static var workspace: Workspace {
|
||
|
|
let repositoryURL = rootURL.appendingPathComponent("Research")
|
||
|
|
let branch = GitBranch(name: "main", isCurrent: true, upstreamName: "origin/main")
|
||
|
|
let remote = GitRemote(
|
||
|
|
name: "origin",
|
||
|
|
url: URL(string: "https://example.com/sapling/research.git")!
|
||
|
|
)
|
||
|
|
let repository = GitRepository(
|
||
|
|
name: "Research",
|
||
|
|
rootURL: repositoryURL,
|
||
|
|
currentBranch: branch,
|
||
|
|
remotes: [remote],
|
||
|
|
statusSummary: .dirty
|
||
|
|
)
|
||
|
|
let subproject = Subproject(
|
||
|
|
name: "Shared Assets",
|
||
|
|
path: "Assets/Shared",
|
||
|
|
repositoryURL: repositoryURL.appendingPathComponent("Assets/Shared"),
|
||
|
|
remoteURL: URL(string: "https://example.com/sapling/shared-assets.git")
|
||
|
|
)
|
||
|
|
let project = Project(
|
||
|
|
name: "Research",
|
||
|
|
repositoryURL: repositoryURL,
|
||
|
|
gitRepository: repository,
|
||
|
|
remotes: [remote],
|
||
|
|
branches: [
|
||
|
|
branch,
|
||
|
|
GitBranch(name: "drafts/editor-prototype")
|
||
|
|
],
|
||
|
|
subprojects: [subproject],
|
||
|
|
usesGitLFS: true
|
||
|
|
)
|
||
|
|
|
||
|
|
return Workspace(
|
||
|
|
name: "Sapling Sample",
|
||
|
|
rootURL: rootURL,
|
||
|
|
items: [
|
||
|
|
.folder(
|
||
|
|
WorkspaceFolder(
|
||
|
|
name: "Inbox",
|
||
|
|
url: rootURL.appendingPathComponent("Inbox"),
|
||
|
|
children: [
|
||
|
|
.file(
|
||
|
|
WorkspaceFile(
|
||
|
|
name: "Scratch.md",
|
||
|
|
url: rootURL.appendingPathComponent("Inbox/Scratch.md"),
|
||
|
|
kind: .markdown
|
||
|
|
)
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
),
|
||
|
|
.project(project),
|
||
|
|
.subproject(subproject)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|