summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DestinationDescriber.swift
blob: b2674c2c1bfba2767e84654a78a102d2c6210acd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//
//  DestinationDescriber.swift
//  MullvadVPN
//
//  Created by Andrew Bulhak on 2025-01-20.
//  Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
//  A source of truth for converting an exit relay destination (i.e., a relay or list) into a name

import MullvadREST
import MullvadSettings
import MullvadTypes

protocol DestinationDescribing {
    func describe(_ destination: UserSelectedRelays) -> String?
}

struct DestinationDescriber: DestinationDescribing {
    let relayCache: RelayCacheProtocol
    let customListRepository: CustomListRepositoryProtocol

    public init(
        relayCache: RelayCacheProtocol,
        customListRepository: CustomListRepositoryProtocol
    ) {
        self.relayCache = relayCache
        self.customListRepository = customListRepository
    }

    private func customListDescription(_ destination: UserSelectedRelays) -> String? {
        // We only return a description for the list if the user has selected the
        // entire list. If they have only selected relays/locations from it,
        // we show those as if they selected them from elsewhere.
        guard
            let customListSelection = destination.customListSelection,
            customListSelection.isList,
            let customList = customListRepository.fetch(by: customListSelection.listId)
        else { return nil }
        return customList.name
    }

    private func describeRelayLocation(
        _ locationSpec: RelayLocation,
        usingRelayWithLocation serverLocation: Location
    ) -> String {
        switch locationSpec {
        case .country: serverLocation.country
        case .city: serverLocation.city
        case let .hostname(_, _, hostname):
            "\(serverLocation.city) (\(hostname))"
        }
    }

    private func relayDescription(_ destination: UserSelectedRelays) -> String? {
        guard
            let location = destination.locations.first,
            let cachedRelays = try? relayCache.read().relays
        else { return nil }
        let locatedRelays = RelayWithLocation.locateRelays(
            relays: cachedRelays.wireguard.relays,
            locations: cachedRelays.locations
        )

        guard
            let matchingRelay =
                (locatedRelays.first {
                    $0.matches(location: location)
                })
        else { return nil }

        return describeRelayLocation(location, usingRelayWithLocation: matchingRelay.serverLocation)
    }

    func describe(_ destination: UserSelectedRelays) -> String? {
        customListDescription(destination) ?? relayDescription(destination)
    }
}