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) ``` ![Architecture](attachments/architecture.png) """ ) } 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) ] ) } }