import Hummingbird /// A controller type that provides information about the *DocC* archives containers. struct ArchiveController { // MARK: Properties private let archivesPath: String private let fileService: any FileServicing // MARK: Initialisers /// Initialises this controller. /// - Parameters: /// - archivesPath: A path in the local file system where the *DocC* archive contained are located. /// - fileService: A service that interfaces with the local file system. init( _ archivesPath: String, fileService: any FileServicing = FileService() ) { self.archivesPath = archivesPath self.fileService = fileService } // MARK: Functions /// Registers the controller to a given router. /// - Parameter router: A router to register this controller to. func register(to router: Router) { router.get(.archives, use: listArchives) } } // MARK: - Helpers private extension ArchiveController { // MARK: Functions @Sendable func listArchives( _ request: Request, context: Context ) async throws (HTTPError) -> ArchiveList { do { let nameArchives = try await fileService .listItems(in: archivesPath) .filter { $0.hasSuffix(.suffixArchive) } .map { $0.dropLast(String.suffixArchive.count) } .map(String.init) .sorted { $0 < $1 } return .init(nameArchives) } catch .folderNotFound { throw .init(.notFound) } catch .folderPathEmpty, .folderNotDirectory { throw .init(.unprocessableContent) } catch { throw .init(.badRequest) } } } // MARK: - RouterPath+Constants private extension RouterPath { static let archives: RouterPath = .init("archives") } // MARK: - String+Constants private extension String { static let suffixArchive: String = ".doccarchive" }