Tusker/Packages/Pachyderm/Sources/Pachyderm/Model/Instance.swift

180 lines
6.3 KiB
Swift

//
// Instance.swift
// Pachyderm
//
// Created by Shadowfacts on 9/9/18.
// Copyright © 2018 Shadowfacts. All rights reserved.
//
import Foundation
public struct Instance: Decodable, Sendable {
public let uri: String
public let title: String
public let description: String
public let shortDescription: String?
public let email: String?
public let version: String
public let urls: [String: URL]
public let thumbnail: URL?
public let languages: [String]?
public let stats: Stats?
public let configuration: Configuration?
public let rules: [Rule]?
// pleroma doesn't currently implement these
public let contactAccount: Account?
// superseded by mastodon's configuration.statuses.max_characters, still used by older instances & pleroma
let maxTootCharacters: Int?
let pollLimits: PollsConfiguration?
public var maxStatusCharacters: Int? {
configuration?.statuses.maxCharacters ?? maxTootCharacters
}
public var pollsConfiguration: PollsConfiguration? {
configuration?.polls ?? pollLimits
}
// we need a custom decoder, because all API-compatible implementations don't return some data in the same format
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.uri = try container.decode(String.self, forKey: .uri)
self.title = try container.decode(String.self, forKey: .title)
self.description = try container.decode(String.self, forKey: .description)
self.shortDescription = try container.decodeIfPresent(String.self, forKey: .shortDescription)
self.email = try container.decodeIfPresent(String.self, forKey: .email)
self.version = try container.decode(String.self, forKey: .version)
if let urls = try? container.decodeIfPresent([String: URL].self, forKey: .urls) {
self.urls = urls
} else {
self.urls = [:]
}
self.languages = try? container.decodeIfPresent([String].self, forKey: .languages)
self.contactAccount = try? container.decodeIfPresent(Account.self, forKey: .contactAccount)
self.stats = try? container.decodeIfPresent(Stats.self, forKey: .stats)
self.thumbnail = try? container.decodeIfPresent(URL.self, forKey: .thumbnail)
self.configuration = try? container.decodeIfPresent(Configuration.self, forKey: .configuration)
self.rules = try? container.decodeIfPresent([Rule].self, forKey: .rules)
if let maxTootCharacters = try? container.decodeIfPresent(Int.self, forKey: .maxTootCharacters) {
self.maxTootCharacters = maxTootCharacters
} else if let str = try? container.decodeIfPresent(String.self, forKey: .maxTootCharacters),
let maxTootCharacters = Int(str, radix: 10) {
self.maxTootCharacters = maxTootCharacters
} else {
self.maxTootCharacters = nil
}
self.pollLimits = try? container.decodeIfPresent(PollsConfiguration.self, forKey: .pollLimits)
}
private enum CodingKeys: String, CodingKey {
case uri
case title
case description
case shortDescription = "short_description"
case email
case version
case urls
case thumbnail
case languages
case stats
case configuration
case contactAccount = "contact_account"
case rules
case maxTootCharacters = "max_toot_chars"
case pollLimits = "poll_limits"
}
}
extension Instance {
public struct Stats: Decodable, Sendable {
public let domainCount: Int?
public let statusCount: Int?
public let userCount: Int?
private enum CodingKeys: String, CodingKey {
case domainCount = "domain_count"
case statusCount = "status_count"
case userCount = "user_count"
}
}
}
extension Instance {
public struct Configuration: Codable, Sendable {
public let statuses: StatusesConfiguration
public let mediaAttachments: MediaAttachmentsConfiguration
/// Use Instance.pollsConfiguration to support older instance that don't have this nested
let polls: PollsConfiguration
private enum CodingKeys: String, CodingKey {
case statuses
case mediaAttachments = "media_attachments"
case polls
}
}
}
extension Instance {
public struct StatusesConfiguration: Codable, Sendable {
public let maxCharacters: Int
public let maxMediaAttachments: Int
public let charactersReservedPerURL: Int
private enum CodingKeys: String, CodingKey {
case maxCharacters = "max_characters"
case maxMediaAttachments = "max_media_attachments"
case charactersReservedPerURL = "characters_reserved_per_url"
}
}
}
extension Instance {
public struct MediaAttachmentsConfiguration: Codable, Sendable {
public let supportedMIMETypes: [String]
public let imageSizeLimit: Int
public let imageMatrixLimit: Int
public let videoSizeLimit: Int
public let videoFrameRateLimit: Int
public let videoMatrixLimit: Int
private enum CodingKeys: String, CodingKey {
case supportedMIMETypes = "supported_mime_types"
case imageSizeLimit = "image_size_limit"
case imageMatrixLimit = "image_matrix_limit"
case videoSizeLimit = "video_size_limit"
case videoFrameRateLimit = "video_frame_rate_limit"
case videoMatrixLimit = "video_matrix_limit"
}
}
}
extension Instance {
public struct PollsConfiguration: Codable, Sendable {
public let maxOptions: Int
public let maxCharactersPerOption: Int
public let minExpiration: TimeInterval
public let maxExpiration: TimeInterval
private enum CodingKeys: String, CodingKey {
case maxOptions = "max_options"
case maxCharactersPerOption = "max_characters_per_option"
case minExpiration = "min_expiration"
case maxExpiration = "max_expiration"
}
}
}
extension Instance {
public struct Rule: Decodable, Identifiable, Sendable {
public let id: String
public let text: String
}
}