|
101 | 101 | incoming |
102 | 102 | existing)) |
103 | 103 |
|
| 104 | +(defn- pane-key? |
| 105 | + "Returns true when key is the canonical pane key for session." |
| 106 | + [key session] |
| 107 | + (and (seq (:pane-id session)) |
| 108 | + (= key (:pane-id session)))) |
| 109 | + |
| 110 | +(defn- pick-session-id-duplicate |
| 111 | + "Picks one entry among entries with the same :session-id. |
| 112 | + A pane-id keyed entry is preferred because pane-id is the |
| 113 | + canonical live-session key. Otherwise the newest entry wins." |
| 114 | + [[existing-key existing :as existing-entry] |
| 115 | + [incoming-key incoming :as incoming-entry]] |
| 116 | + (let [existing-pane? (pane-key? existing-key existing) |
| 117 | + incoming-pane? (pane-key? incoming-key incoming)] |
| 118 | + (cond |
| 119 | + (and incoming-pane? (not existing-pane?)) |
| 120 | + incoming-entry |
| 121 | + (and existing-pane? (not incoming-pane?)) |
| 122 | + existing-entry |
| 123 | + :else |
| 124 | + (if (= incoming (pick-newer existing incoming)) |
| 125 | + incoming-entry |
| 126 | + existing-entry)))) |
| 127 | + |
| 128 | +(defn- canonicalize-by-pane [sessions] |
| 129 | + (reduce-kv |
| 130 | + (fn [acc key session] |
| 131 | + (let [pane-id (:pane-id session) |
| 132 | + canonical (if (seq pane-id) pane-id key)] |
| 133 | + (if-let [existing (get acc canonical)] |
| 134 | + (assoc acc canonical |
| 135 | + (pick-newer existing session)) |
| 136 | + (assoc acc canonical session)))) |
| 137 | + {} |
| 138 | + sessions)) |
| 139 | + |
| 140 | +(defn- session-id-winners [sessions] |
| 141 | + (reduce-kv |
| 142 | + (fn [acc key session] |
| 143 | + (if-let [session-id (:session-id session)] |
| 144 | + (if-let [existing-entry (get acc session-id)] |
| 145 | + (assoc acc session-id |
| 146 | + (pick-session-id-duplicate |
| 147 | + existing-entry [key session])) |
| 148 | + (assoc acc session-id [key session])) |
| 149 | + acc)) |
| 150 | + {} |
| 151 | + sessions)) |
| 152 | + |
| 153 | +(defn- session-id-keys [sessions winners] |
| 154 | + (into #{} |
| 155 | + (map first) |
| 156 | + (filter (fn [[_key session]] |
| 157 | + (contains? winners |
| 158 | + (:session-id session))) |
| 159 | + sessions))) |
| 160 | + |
104 | 161 | (defn normalize-sessions |
105 | 162 | "Re-keys sessions by :pane-id (canonical key). |
106 | 163 | Entries with non-empty :pane-id are keyed by pane-id; |
107 | 164 | entries without :pane-id keep their original key. |
108 | 165 | When multiple entries share the same pane-id, the one |
109 | | - with the latest :last-updated wins. |
| 166 | + with the latest :last-updated wins. When multiple entries |
| 167 | + share the same :session-id, they are deduplicated and |
| 168 | + pane-id keyed entries are preferred. |
110 | 169 | Returns empty map when sessions is nil." |
111 | 170 | [sessions] |
112 | 171 | (if (nil? sessions) |
113 | 172 | {} |
114 | | - (reduce-kv |
115 | | - (fn [acc key session] |
116 | | - (let [pane-id (:pane-id session) |
117 | | - canonical (if (seq pane-id) pane-id key)] |
118 | | - (if-let [existing (get acc canonical)] |
119 | | - (assoc acc canonical (pick-newer existing session)) |
120 | | - (assoc acc canonical session)))) |
121 | | - {} |
122 | | - sessions))) |
| 173 | + (let [by-pane |
| 174 | + (canonicalize-by-pane sessions) |
| 175 | + by-session (session-id-winners by-pane) |
| 176 | + duplicate-keys (session-id-keys by-pane by-session)] |
| 177 | + (merge |
| 178 | + (apply dissoc by-pane duplicate-keys) |
| 179 | + (into {} |
| 180 | + (map (fn [[_session-id [key session]]] |
| 181 | + [key session])) |
| 182 | + by-session))))) |
123 | 183 |
|
124 | 184 | (defn- read-state-file |
125 | 185 | "Reads and parses the sessions.edn file. |
|
0 commit comments