diff --git a/internal/bridge/bridge_test.go b/internal/bridge/bridge_test.go index 23c12090..c2ab0c0a 100644 --- a/internal/bridge/bridge_test.go +++ b/internal/bridge/bridge_test.go @@ -34,7 +34,6 @@ import ( "github.com/ProtonMail/proton-bridge/v2/internal/events" "github.com/ProtonMail/proton-bridge/v2/internal/focus" "github.com/ProtonMail/proton-bridge/v2/internal/locations" - "github.com/ProtonMail/proton-bridge/v2/internal/safe" "github.com/ProtonMail/proton-bridge/v2/internal/updater" "github.com/ProtonMail/proton-bridge/v2/internal/user" "github.com/ProtonMail/proton-bridge/v2/internal/useragent" @@ -168,7 +167,10 @@ func TestBridge_UserAgent(t *testing.T) { func TestBridge_Cookies(t *testing.T) { withEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, vaultKey []byte) { - sessionIDs := safe.NewValue([]string{}) + var ( + sessionIDs []string + sessionIDsLock sync.RWMutex + ) // Save any session IDs we use. s.AddCallWatcher(func(call server.Call) { @@ -177,9 +179,10 @@ func TestBridge_Cookies(t *testing.T) { return } - sessionIDs.Mod(func(sessionIDs *[]string) { - *sessionIDs = append(*sessionIDs, cookie.Value) - }) + sessionIDsLock.Lock() + defer sessionIDsLock.Unlock() + + sessionIDs = append(sessionIDs, cookie.Value) }) // Start bridge and add a user so that API assigns us a session ID via cookie. @@ -194,9 +197,10 @@ func TestBridge_Cookies(t *testing.T) { }) // We should have used just one session ID. - sessionIDs.Load(func(sessionIDs []string) { - require.Len(t, xslices.Unique(sessionIDs), 1) - }) + sessionIDsLock.Lock() + defer sessionIDsLock.Unlock() + + require.Len(t, xslices.Unique(sessionIDs), 1) }) } diff --git a/internal/safe/map.go b/internal/safe/map.go deleted file mode 100644 index 0898a695..00000000 --- a/internal/safe/map.go +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) 2022 Proton AG -// -// This file is part of Proton Mail Bridge. -// -// Proton Mail Bridge is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Proton Mail Bridge is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Proton Mail Bridge. If not, see . - -package safe - -import ( - "sync" - - "github.com/bradenaw/juniper/xslices" - "golang.org/x/exp/slices" -) - -type Map[Key comparable, Val any] struct { - data map[Key]Val - order []Key - sort func(a, b Key, data map[Key]Val) bool - lock sync.RWMutex -} - -func NewMap[Key comparable, Val any](sort func(a, b Key, data map[Key]Val) bool) *Map[Key, Val] { - return &Map[Key, Val]{ - data: make(map[Key]Val), - sort: sort, - } -} - -func NewMapFrom[Key comparable, Val any](from map[Key]Val, sort func(a, b Key, data map[Key]Val) bool) *Map[Key, Val] { - m := NewMap(sort) - - for key, val := range from { - m.Set(key, val) - } - - return m -} - -func (m *Map[Key, Val]) Index(idx int, fn func(Key, Val)) bool { - m.lock.RLock() - defer m.lock.RUnlock() - - if idx < 0 || idx >= len(m.order) { - return false - } - - fn(m.order[idx], m.data[m.order[idx]]) - - return true -} - -func (m *Map[Key, Val]) Has(key Key) bool { - return m.HasFunc(func(k Key, v Val) bool { - return k == key - }) -} - -func (m *Map[Key, Val]) HasFunc(fn func(key Key, val Val) bool) bool { - m.lock.RLock() - defer m.lock.RUnlock() - - for key, val := range m.data { - if fn(key, val) { - return true - } - } - - return false -} - -func (m *Map[Key, Val]) Get(key Key, fn func(Val)) bool { - m.lock.RLock() - defer m.lock.RUnlock() - - val, ok := m.data[key] - if !ok { - return false - } - - fn(val) - - return true -} - -func (m *Map[Key, Val]) GetErr(key Key, fn func(Val) error) (bool, error) { - var err error - - ok := m.Get(key, func(val Val) { - err = fn(val) - }) - - return ok, err -} - -func (m *Map[Key, Val]) GetDelete(key Key, fn func(Val)) bool { - m.lock.Lock() - defer m.lock.Unlock() - - val, ok := m.data[key] - if !ok { - return false - } - - fn(val) - - delete(m.data, key) - - if idx := xslices.Index(m.order, key); idx >= 0 { - m.order = append(m.order[:idx], m.order[idx+1:]...) - } else { - panic("order and data out of sync") - } - - return true -} - -func (m *Map[Key, Val]) GetDeleteErr(key Key, fn func(Val) error) (bool, error) { - var err error - - ok := m.GetDelete(key, func(val Val) { - err = fn(val) - }) - - return ok, err -} - -func (m *Map[Key, Val]) GetFunc(where func(Val) bool, fn func(Val)) bool { - m.lock.RLock() - defer m.lock.RUnlock() - - for _, key := range m.order { - if where(m.data[key]) { - fn(m.data[key]) - return true - } - } - - return false -} - -func (m *Map[Key, Val]) Delete(key Key) bool { - return m.GetDelete(key, func(val Val) {}) -} - -func (m *Map[Key, Val]) Set(key Key, val Val) bool { - m.lock.Lock() - defer m.lock.Unlock() - - var had bool - - if _, ok := m.data[key]; ok { - had = true - } - - m.data[key] = val - - if idx := xslices.Index(m.order, key); idx >= 0 { - m.order[idx] = key - } else { - m.order = append(m.order, key) - } - - if m.sort != nil { - slices.SortFunc(m.order, func(a, b Key) bool { - return m.sort(a, b, m.data) - }) - } - - return had -} - -func (m *Map[Key, Val]) SetFrom(key Key, other Key) bool { - m.lock.Lock() - defer m.lock.Unlock() - - var had bool - - if _, ok := m.data[key]; ok { - had = true - } - - m.data[key] = m.data[other] - - if idx := xslices.Index(m.order, key); idx >= 0 { - m.order[idx] = key - } else { - m.order = append(m.order, key) - } - - if m.sort != nil { - slices.SortFunc(m.order, func(a, b Key) bool { - return m.sort(a, b, m.data) - }) - } - - return had -} - -func (m *Map[Key, Val]) Iter(fn func(key Key, val Val)) { - m.lock.RLock() - defer m.lock.RUnlock() - - for _, key := range m.order { - fn(key, m.data[key]) - } -} - -func (m *Map[Key, Val]) IterKeys(fn func(Key)) { - m.Iter(func(key Key, _ Val) { - fn(key) - }) -} - -func (m *Map[Key, Val]) IterKeysErr(fn func(Key) error) error { - var err error - - m.IterKeys(func(key Key) { - if err != nil { - return - } - - err = fn(key) - }) - - return err -} - -func (m *Map[Key, Val]) IterValues(fn func(Val)) { - m.Iter(func(_ Key, val Val) { - fn(val) - }) -} - -func (m *Map[Key, Val]) IterValuesErr(fn func(Val) error) error { - var err error - - m.IterValues(func(val Val) { - if err != nil { - return - } - - err = fn(val) - }) - - return err -} - -func (m *Map[Key, Val]) Values(fn func(vals []Val)) { - m.lock.RLock() - defer m.lock.RUnlock() - - vals := make([]Val, len(m.order)) - - for i, key := range m.order { - vals[i] = m.data[key] - } - - fn(vals) -} - -func (m *Map[Key, Val]) ValuesErr(fn func(vals []Val) error) error { - var err error - - m.Values(func(vals []Val) { - err = fn(vals) - }) - - return err -} - -func (m *Map[Key, Val]) Clear() { - m.lock.Lock() - defer m.lock.Unlock() - - m.data = make(map[Key]Val) - m.order = nil -} - -func (m *Map[Key, Val]) MapErr(fn func(map[Key]Val) error) error { - m.lock.RLock() - defer m.lock.RUnlock() - - return fn(m.data) -} - -func MapGetRet[Key comparable, Val, Ret any](m *Map[Key, Val], key Key, fn func(Val) Ret) (Ret, bool) { - var ret Ret - - ok := m.Get(key, func(val Val) { - ret = fn(val) - }) - - return ret, ok -} - -func MapGetRetErr[Key comparable, Val, Ret any](m *Map[Key, Val], key Key, fn func(Val) (Ret, error)) (Ret, bool, error) { - var ret Ret - - ok, err := m.GetErr(key, func(val Val) error { - var err error - - ret, err = fn(val) - - return err - }) - - return ret, ok, err -} - -func MapValuesRet[Key comparable, Val, Ret any](m *Map[Key, Val], fn func([]Val) Ret) Ret { - var ret Ret - - m.Values(func(vals []Val) { - ret = fn(vals) - }) - - return ret -} - -func MapValuesRetErr[Key comparable, Val, Ret any](m *Map[Key, Val], fn func([]Val) (Ret, error)) (Ret, error) { - var ret Ret - - err := m.ValuesErr(func(vals []Val) error { - var err error - - ret, err = fn(vals) - - return err - }) - - return ret, err -} diff --git a/internal/safe/map_test.go b/internal/safe/map_test.go deleted file mode 100644 index 96465baf..00000000 --- a/internal/safe/map_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2022 Proton AG -// -// This file is part of Proton Mail Bridge. -// -// Proton Mail Bridge is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Proton Mail Bridge is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Proton Mail Bridge. If not, see . - -package safe - -import "testing" - -func TestSafe_Map(t *testing.T) { - m := NewMap(func(a, b string, data map[string]string) bool { - return a < b - }) - - m.Set("a", "b") - - if !m.Has("a") { - t.Fatal("expected to have key") - } - - if m.Has("b") { - t.Fatal("expected not to have key") - } - - m.Set("b", "c") - - if !m.Has("b") { - t.Fatal("expected to have key") - } - - if !m.HasFunc(func(key string, val string) bool { - return key == "b" - }) { - t.Fatal("expected to have key") - } - - if !m.Get("b", func(val string) { - if val != "c" { - t.Fatal("expected to have value") - } - }) { - t.Fatal("expected to have key") - } - - if !m.Index(0, func(key string, val string) { - if key != "a" || val != "b" { - t.Fatal("expected to have key and value") - } - }) { - t.Fatal("expected to have index") - } - - if !m.Index(1, func(key string, val string) { - if key != "b" || val != "c" { - t.Fatal("expected to have key and value") - } - }) { - t.Fatal("expected to have index") - } - - if m.Index(2, func(key string, val string) { - t.Fatal("expected not to have index") - }) { - t.Fatal("expected not to have index") - } - - if !m.GetDelete("b", func(val string) { - if val != "c" { - t.Fatal("expected to have value") - } - }) { - t.Fatal("expected to have key") - } - - if m.Has("b") { - t.Fatal("expected not to have key") - } - - if m.GetDelete("b", func(val string) { - t.Fatal("expected not to have value") - }) { - t.Fatal("expected not to have key") - } - - if !m.Index(0, func(key string, val string) { - if key != "a" || val != "b" { - t.Fatal("expected to have key and value") - } - }) { - t.Fatal("expected to have index") - } - - m.Values(func(val []string) { - if len(val) != 1 { - t.Fatal("expected to have values") - } - - if val[0] != "b" { - t.Fatal("expected to have value") - } - }) -} diff --git a/internal/safe/slice.go b/internal/safe/slice.go deleted file mode 100644 index f6deaed9..00000000 --- a/internal/safe/slice.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2022 Proton AG -// -// This file is part of Proton Mail Bridge. -// -// Proton Mail Bridge is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Proton Mail Bridge is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Proton Mail Bridge. If not, see . - -package safe - -import ( - "sync" - - "github.com/bradenaw/juniper/xslices" -) - -type Slice[Val comparable] struct { - data []Val - lock sync.RWMutex -} - -func NewSlice[Val comparable](from ...Val) *Slice[Val] { - s := &Slice[Val]{ - data: make([]Val, len(from)), - } - - copy(s.data, from) - - return s -} - -func (s *Slice[Val]) Iter(fn func(val Val)) { - s.lock.RLock() - defer s.lock.RUnlock() - - for _, val := range s.data { - fn(val) - } -} - -func (s *Slice[Val]) Append(val Val) { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = append(s.data, val) -} - -func (s *Slice[Val]) Delete(val Val) { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = xslices.Filter(s.data, func(v Val) bool { - return v != val - }) -} diff --git a/internal/safe/slice_test.go b/internal/safe/slice_test.go deleted file mode 100644 index dc6c68c2..00000000 --- a/internal/safe/slice_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2022 Proton AG -// -// This file is part of Proton Mail Bridge. -// -// Proton Mail Bridge is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Proton Mail Bridge is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Proton Mail Bridge. If not, see . - -package safe - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestSlice(t *testing.T) { - s := NewSlice(1, 2, 3, 4, 5) - - { - var have []int - - s.Iter(func(val int) { - have = append(have, val) - }) - - require.Equal(t, []int{1, 2, 3, 4, 5}, have) - } - - s.Append(6) - s.Delete(3) - - { - var have []int - - s.Iter(func(val int) { - have = append(have, val) - }) - - require.Equal(t, []int{1, 2, 4, 5, 6}, have) - } -} diff --git a/internal/safe/value.go b/internal/safe/value.go deleted file mode 100644 index b072d46c..00000000 --- a/internal/safe/value.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2022 Proton AG -// -// This file is part of Proton Mail Bridge. -// -// Proton Mail Bridge is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Proton Mail Bridge is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Proton Mail Bridge. If not, see . - -package safe - -import "sync" - -type Value[T any] struct { - data T - lock sync.RWMutex -} - -func NewValue[T any](data T) *Value[T] { - return &Value[T]{ - data: data, - } -} - -func (s *Value[T]) Load(fn func(data T)) { - s.lock.RLock() - defer s.lock.RUnlock() - - fn(s.data) -} - -func (s *Value[T]) LoadErr(fn func(data T) error) error { - var err error - - s.Load(func(data T) { - err = fn(data) - }) - - return err -} - -func (s *Value[T]) Save(data T) { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = data -} - -func (s *Value[T]) Mod(fn func(data *T)) { - s.lock.Lock() - defer s.lock.Unlock() - - fn(&s.data) -} - -func LoadRet[T, Ret any](s *Value[T], fn func(data T) Ret) Ret { - var ret Ret - - s.Load(func(data T) { - ret = fn(data) - }) - - return ret -} - -func LoadRetErr[T, Ret any](s *Value[T], fn func(data T) (Ret, error)) (Ret, error) { - var ret Ret - - err := s.LoadErr(func(data T) error { - var err error - - ret, err = fn(data) - - return err - }) - - return ret, err -} diff --git a/internal/safe/value_test.go b/internal/safe/value_test.go deleted file mode 100644 index 8a0c4b58..00000000 --- a/internal/safe/value_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2022 Proton AG -// -// This file is part of Proton Mail Bridge. -// -// Proton Mail Bridge is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Proton Mail Bridge is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Proton Mail Bridge. If not, see . - -package safe - -import "testing" - -func TestValue(t *testing.T) { - v := NewValue("foo") - - v.Load(func(data string) { - if data != "foo" { - t.Error("expected foo") - } - }) - - v.Save("bar") - - v.Load(func(data string) { - if data != "bar" { - t.Error("expected bar") - } - }) - - v.Mod(func(data *string) { - *data = "baz" - }) - - v.Load(func(data string) { - if data != "baz" { - t.Error("expected baz") - } - }) - - if LoadRet(v, func(data string) string { - return data - }) != "baz" { - t.Error("expected baz") - } -}