2017-11-11 04:06:16 +00:00
package common
2017-11-11 23:34:27 +00:00
import (
"database/sql"
"sync/atomic"
2018-10-27 03:21:02 +00:00
"github.com/Azareal/Gosora/query_gen"
2017-11-11 23:34:27 +00:00
)
2017-11-11 04:06:16 +00:00
2018-08-04 11:46:36 +00:00
// TODO: Move some features into methods on this?
2017-11-11 04:06:16 +00:00
type WordFilter struct {
ID int
Find string
Replacement string
}
2018-08-04 11:46:36 +00:00
var WordFilters WordFilterStore
2017-11-11 04:06:16 +00:00
2018-08-04 11:46:36 +00:00
type WordFilterStore interface {
ReloadAll ( ) error
GetAll ( ) ( filters map [ int ] * WordFilter , err error )
Create ( find string , replacement string ) error
Delete ( id int ) error
Update ( id int , find string , replacement string ) error
Length ( ) int
EstCount ( ) int
2019-06-01 12:31:48 +00:00
Count ( ) ( count int )
2017-11-11 23:34:27 +00:00
}
2018-08-04 11:46:36 +00:00
type DefaultWordFilterStore struct {
box atomic . Value // An atomic value holding a WordFilterMap
2017-11-11 23:34:27 +00:00
2018-08-04 11:46:36 +00:00
getAll * sql . Stmt
create * sql . Stmt
delete * sql . Stmt
update * sql . Stmt
count * sql . Stmt
2017-11-11 04:06:16 +00:00
}
2018-08-04 11:46:36 +00:00
func NewDefaultWordFilterStore ( acc * qgen . Accumulator ) ( * DefaultWordFilterStore , error ) {
2019-10-28 07:46:14 +00:00
wf := "word_filters"
2018-08-04 11:46:36 +00:00
store := & DefaultWordFilterStore {
2019-10-28 07:46:14 +00:00
getAll : acc . Select ( wf ) . Columns ( "wfid,find,replacement" ) . Prepare ( ) ,
create : acc . Insert ( wf ) . Columns ( "find,replacement" ) . Fields ( "?,?" ) . Prepare ( ) ,
delete : acc . Delete ( wf ) . Where ( "wfid = ?" ) . Prepare ( ) ,
update : acc . Update ( wf ) . Set ( "find = ?, replacement = ?" ) . Where ( "wfid = ?" ) . Prepare ( ) ,
count : acc . Count ( wf ) . Prepare ( ) ,
2018-08-04 11:46:36 +00:00
}
// TODO: Should we initialise this elsewhere?
if acc . FirstError ( ) == nil {
acc . RecordError ( store . ReloadAll ( ) )
}
return store , acc . FirstError ( )
}
// ReloadAll drops all the items in the memory cache and replaces them with fresh copies from the database
2019-06-01 12:31:48 +00:00
func ( s * DefaultWordFilterStore ) ReloadAll ( ) error {
2019-10-06 05:32:08 +00:00
wordFilters := make ( map [ int ] * WordFilter )
2019-06-01 12:31:48 +00:00
filters , err := s . bypassGetAll ( )
2017-11-11 04:06:16 +00:00
if err != nil {
return err
}
2017-11-23 05:37:08 +00:00
for _ , filter := range filters {
wordFilters [ filter . ID ] = filter
}
2019-06-01 12:31:48 +00:00
s . box . Store ( wordFilters )
2017-11-23 05:37:08 +00:00
return nil
}
2018-08-04 11:46:36 +00:00
// ? - Return pointers to word filters intead to save memory? -- A map is a pointer.
2019-06-01 12:31:48 +00:00
func ( s * DefaultWordFilterStore ) bypassGetAll ( ) ( filters [ ] * WordFilter , err error ) {
rows , err := s . getAll . Query ( )
2017-11-23 05:37:08 +00:00
if err != nil {
return nil , err
}
defer rows . Close ( )
2017-11-11 04:06:16 +00:00
for rows . Next ( ) {
2019-10-06 05:32:08 +00:00
f := & WordFilter { ID : 0 }
err := rows . Scan ( & f . ID , & f . Find , & f . Replacement )
2017-11-11 04:06:16 +00:00
if err != nil {
2017-11-23 05:37:08 +00:00
return filters , err
2017-11-11 04:06:16 +00:00
}
2019-10-06 05:32:08 +00:00
filters = append ( filters , f )
2017-11-11 04:06:16 +00:00
}
2017-11-23 05:37:08 +00:00
return filters , rows . Err ( )
2017-11-11 04:06:16 +00:00
}
2018-08-04 11:46:36 +00:00
// GetAll returns all of the word filters in a map. Do note mutate this map (or maps returned from any store not explicitly noted as copies) as multiple threads may be accessing it at once
2019-06-01 12:31:48 +00:00
func ( s * DefaultWordFilterStore ) GetAll ( ) ( filters map [ int ] * WordFilter , err error ) {
return s . box . Load ( ) . ( map [ int ] * WordFilter ) , nil
2018-08-04 11:46:36 +00:00
}
// Create adds a new word filter to the database and refreshes the memory cache
2019-10-06 05:32:08 +00:00
func ( s * DefaultWordFilterStore ) Create ( find string , replace string ) error {
_ , err := s . create . Exec ( find , replace )
2018-08-04 11:46:36 +00:00
if err != nil {
return err
}
2019-06-01 12:31:48 +00:00
return s . ReloadAll ( )
2018-08-04 11:46:36 +00:00
}
// Delete removes a word filter from the database and refreshes the memory cache
2019-06-01 12:31:48 +00:00
func ( s * DefaultWordFilterStore ) Delete ( id int ) error {
_ , err := s . delete . Exec ( id )
2018-08-04 11:46:36 +00:00
if err != nil {
return err
}
2019-06-01 12:31:48 +00:00
return s . ReloadAll ( )
2018-08-04 11:46:36 +00:00
}
2019-10-06 05:32:08 +00:00
func ( s * DefaultWordFilterStore ) Update ( id int , find string , replace string ) error {
_ , err := s . update . Exec ( find , replace , id )
2018-08-04 11:46:36 +00:00
if err != nil {
return err
}
2019-06-01 12:31:48 +00:00
return s . ReloadAll ( )
2018-08-04 11:46:36 +00:00
}
// Length gets the number of word filters currently in memory, for the DefaultWordFilterStore, this should be all of them
2019-06-01 12:31:48 +00:00
func ( s * DefaultWordFilterStore ) Length ( ) int {
return len ( s . box . Load ( ) . ( map [ int ] * WordFilter ) )
2018-08-04 11:46:36 +00:00
}
// EstCount provides the same result as Length(), intended for alternate implementations of WordFilterStore, so that Length is the number of items in cache, if only a subset is held there and EstCount is the total count
2019-06-01 12:31:48 +00:00
func ( s * DefaultWordFilterStore ) EstCount ( ) int {
return len ( s . box . Load ( ) . ( map [ int ] * WordFilter ) )
2018-08-04 11:46:36 +00:00
}
2019-06-01 12:31:48 +00:00
// Count gets the total number of word filters directly from the database
func ( s * DefaultWordFilterStore ) Count ( ) ( count int ) {
err := s . count . QueryRow ( ) . Scan ( & count )
2018-08-04 11:46:36 +00:00
if err != nil {
LogError ( err )
}
return count
2017-11-11 04:06:16 +00:00
}