import Foundation import Moya public enum ReadeckAPI { case bookmarks(BookmarkQuery) case bookmark(id: String) case bookmarkArticle(id: String) case createBookmark(url: String, title: String?, labels: [String]) case updateBookmark(id: String, patch: BookmarkPatch) case deleteBookmark(id: String) case labels case userInfo } public struct BookmarkQuery: Equatable, Sendable { public var search: String? public var labels: [String] public var isArchived: Bool? public var isMarked: Bool? public var limit: Int public var offset: Int public init( search: String? = nil, labels: [String] = [], isArchived: Bool? = nil, isMarked: Bool? = nil, limit: Int = 50, offset: Int = 0 ) { self.search = search self.labels = labels self.isArchived = isArchived self.isMarked = isMarked self.limit = limit self.offset = offset } } public struct BookmarkPatch: Equatable, Sendable { public var isArchived: Bool? public var isMarked: Bool? public var readProgress: Int? public var labels: [String]? public init( isArchived: Bool? = nil, isMarked: Bool? = nil, readProgress: Int? = nil, labels: [String]? = nil ) { self.isArchived = isArchived self.isMarked = isMarked self.readProgress = readProgress self.labels = labels } } extension ReadeckAPI: TargetType { public var baseURL: URL { ReadeckEnvironment.current.baseURL ?? URL(string: "https://example.invalid")! } public var path: String { switch self { case .bookmarks: return "/api/bookmarks" case .bookmark(let id): return "/api/bookmarks/\(id)" case .bookmarkArticle(let id): return "/api/bookmarks/\(id)/article" case .createBookmark: return "/api/bookmarks" case .updateBookmark(let id, _): return "/api/bookmarks/\(id)" case .deleteBookmark(let id): return "/api/bookmarks/\(id)" case .labels: return "/api/bookmarks/labels" case .userInfo: return "/api/profile" } } public var method: Moya.Method { switch self { case .bookmarks, .bookmark, .bookmarkArticle, .labels, .userInfo: return .get case .createBookmark: return .post case .updateBookmark: return .patch case .deleteBookmark: return .delete } } public var task: Moya.Task { switch self { case .bookmarks(let query): var params: [String: Any] = [ "limit": query.limit, "offset": query.offset ] if let search = query.search, !search.isEmpty { params["search"] = search } if !query.labels.isEmpty { params["labels"] = query.labels.joined(separator: ",") } if let isArchived = query.isArchived { params["is_archived"] = isArchived } if let isMarked = query.isMarked { params["is_marked"] = isMarked } return .requestParameters(parameters: params, encoding: URLEncoding.queryString) case .bookmark, .bookmarkArticle, .labels, .userInfo, .deleteBookmark: return .requestPlain case .createBookmark(let url, let title, let labels): var body: [String: Any] = ["url": url] if let title { body["title"] = title } if !labels.isEmpty { body["labels"] = labels } return .requestParameters(parameters: body, encoding: JSONEncoding.default) case .updateBookmark(_, let patch): var body: [String: Any] = [:] if let isArchived = patch.isArchived { body["is_archived"] = isArchived } if let isMarked = patch.isMarked { body["is_marked"] = isMarked } if let readProgress = patch.readProgress { body["read_progress"] = readProgress } if let labels = patch.labels { body["labels"] = labels } return .requestParameters(parameters: body, encoding: JSONEncoding.default) } } public var headers: [String: String]? { var headers = ["Accept": "application/json"] switch self { case .bookmarkArticle: headers["Accept"] = "text/html" default: break } return headers } public var sampleData: Data { Data() } }