一次只能加一張牌的相簿,是我自己跟自己過不去
Wildcard 的牌組是這樣來的:你出門拍石頭、樹枝、樹葉,然後把照片加進牌組。聽起來很合理啦——直到你發現我當初寫的 picker 一次只能挑一張。你手機相簿裡躺著二十張石頭照,想湊滿牌組?那就點開、選一張、回來、再點開、再選一張。第四次點開 picker 的時候,我開始懷疑這是不是在懲罰我自己。然後默默把這件事記進了 TODO。
這次把 picker 改成可以一次多選。邏輯本身不難吧,難的是「多選之後會發生什麼」。一次丟十張進來,但牌組只剩三個空位,那另外七張怎麼辦?我的處理是:超過容量的部分一律算 skipped,不報錯、不擋下、也不靜悄悄吃掉,就老實告訴你「這幾張因為滿了所以沒加」。
真正麻煩的是 per-file 的失敗。每張照片進來都要過 validator——重複的 hash 要擋(同一塊石頭拍兩次不能算兩張牌),辨識不出來的圖也要擋。以前單張模式很單純,錯就錯一張。但批次處理時,如果我只回傳最後一個錯誤,使用者會一臉問號:「我丟了十張,你就跟我說『辨識失敗』一句?哪一張啊?」所以這次把所有 per-file 的失敗聚合起來一起列,duplicate 的歸 duplicate、認不出的歸認不出,一次說清楚。
寫的時候踩到一個自己挖的坑:我一開始把「容量檢查」跟「validator」混在同一個迴圈裡,結果 skipped 跟 failed 的界線就糊掉了——明明是滿了沒加的,被我歸成「失敗」。
後來才想通,這其實是兩件截然不同的事:skipped 是系統的狀態問題,failed 是這張圖本身的問題。系統說「我滿了」,跟圖說「我有毛病」,不能混為一談啊。這個分法可以 generalize 出去:任何批次操作,凡是「因為整體環境限制而跳過」的,和「因為單一項目本身有問題而失敗」的,都該是兩條獨立的 channel,別讓它們摻在一起。拆開之後,回傳結構乾淨多了。
說到底,這個功能的本質就是「把我自己的懶變成 code」。能一次選十張,是因為我真的受不了選第四張了吧。
這段 code 寫於 2026 年 5 月 23 日,文章整理於 2026 年 5 月。