ReferencedId.swift (4574B)
1 // 2 // ReferencedId.swift 3 // damus 4 // 5 // Created by William Casarin on 2023-07-22. 6 // 7 8 import Foundation 9 10 func tagref_should_be_id(_ tag: NdbTagElem) -> Bool { 11 return !(tag.matches_char("t") || tag.matches_char("d")) 12 } 13 14 15 struct References<T: TagConvertible>: Sequence, IteratorProtocol { 16 let tags: TagsSequence 17 var tags_iter: TagsIterator 18 19 init(tags: TagsSequence) { 20 self.tags = tags 21 self.tags_iter = tags.makeIterator() 22 } 23 24 mutating func next() -> T? { 25 while let tag = tags_iter.next() { 26 guard let evref = T.from_tag(tag: tag) else { continue } 27 return evref 28 } 29 return nil 30 } 31 } 32 33 extension References { 34 var first: T? { 35 self.first(where: { _ in true }) 36 } 37 38 var last: T? { 39 var last: T? = nil 40 for t in self { 41 last = t 42 } 43 return last 44 } 45 } 46 47 48 // NdbTagElem transition helpers 49 extension String { 50 func string() -> String { 51 return self 52 } 53 54 func first_char() -> AsciiCharacter? { 55 self.first.flatMap { chr in AsciiCharacter(chr) } 56 } 57 58 func matches_char(_ c: AsciiCharacter) -> Bool { 59 return self.first == c.character 60 } 61 62 func matches_str(_ str: String) -> Bool { 63 return self == str 64 } 65 } 66 67 enum FollowRef: TagKeys, Hashable, TagConvertible, Equatable { 68 69 // NOTE: When adding cases make sure to update key and from_tag 70 case pubkey(Pubkey) 71 case hashtag(String) 72 73 var key: FollowKeys { 74 switch self { 75 case .hashtag: return .t 76 case .pubkey: return .p 77 } 78 } 79 80 enum FollowKeys: AsciiCharacter, TagKey, CustomStringConvertible { 81 case p, t 82 83 var keychar: AsciiCharacter { self.rawValue } 84 var description: String { self.rawValue.description } 85 } 86 87 static func from_tag(tag: TagSequence) -> FollowRef? { 88 guard tag.count >= 2 else { return nil } 89 90 var i = tag.makeIterator() 91 92 guard let t0 = i.next(), 93 let c = t0.single_char, 94 let fkey = FollowKeys(rawValue: c), 95 let t1 = i.next() 96 else { 97 return nil 98 } 99 100 switch fkey { 101 case .p: return t1.id().map({ .pubkey(Pubkey($0)) }) 102 case .t: return .hashtag(t1.string()) 103 } 104 } 105 106 var tag: [String] { 107 [key.description, self.description] 108 } 109 110 var description: String { 111 switch self { 112 case .pubkey(let pubkey): return pubkey.description 113 case .hashtag(let string): return string 114 } 115 } 116 } 117 118 enum RefId: TagConvertible, TagKeys, Equatable, Hashable { 119 case event(NoteId) 120 case pubkey(Pubkey) 121 case quote(QuoteId) 122 case hashtag(Hashtag) 123 case param(TagElem) 124 case naddr(NAddr) 125 126 var key: RefKey { 127 switch self { 128 case .event: return .e 129 case .pubkey: return .p 130 case .quote: return .q 131 case .hashtag: return .t 132 case .param: return .d 133 case .naddr: return .a 134 } 135 } 136 137 enum RefKey: AsciiCharacter, TagKey, CustomStringConvertible { 138 case e, p, t, d, q, a 139 140 var keychar: AsciiCharacter { 141 self.rawValue 142 } 143 144 var description: String { 145 self.keychar.description 146 } 147 } 148 149 var tag: [String] { 150 [self.key.description, self.description] 151 } 152 153 var description: String { 154 switch self { 155 case .event(let noteId): return noteId.hex() 156 case .pubkey(let pubkey): return pubkey.hex() 157 case .quote(let quote): return quote.hex() 158 case .hashtag(let string): return string.hashtag 159 case .param(let string): return string.string() 160 case .naddr(let naddr): 161 return naddr.kind.description + ":" + naddr.author.hex() + ":" + naddr.identifier 162 } 163 } 164 165 static func from_tag(tag: TagSequence) -> RefId? { 166 var i = tag.makeIterator() 167 168 guard tag.count >= 2, 169 let t0 = i.next(), 170 let key = t0.single_char, 171 let rkey = RefKey(rawValue: key), 172 let t1 = i.next() 173 else { return nil } 174 175 switch rkey { 176 case .e: return t1.id().map({ .event(NoteId($0)) }) 177 case .p: return t1.id().map({ .pubkey(Pubkey($0)) }) 178 case .q: return t1.id().map({ .quote(QuoteId($0)) }) 179 case .t: return .hashtag(Hashtag(hashtag: t1.string())) 180 case .d: return .param(t1) 181 case .a: return .naddr(NAddr(identifier: "", author: Pubkey(Data()), relays: [], kind: 0)) 182 } 183 } 184 } 185