Send irc features to the client

This commit is contained in:
Ken-Håvard Lieng 2019-01-27 08:53:07 +01:00
parent 9267c661dc
commit 613d9fca6e
20 changed files with 690 additions and 304 deletions

File diff suppressed because one or more lines are too long

View File

@ -79,6 +79,7 @@ export default createCommandMiddleware(COMMAND, {
topic({ dispatch, getState, server, channel }, ...newTopic) {
if (newTopic.length > 0) {
dispatch(setTopic(newTopic.join(' '), channel, server));
return;
} else if (channel) {
const { topic } = getState().channels[server][channel];
if (topic) {

View File

@ -1,5 +1,6 @@
import React, { PureComponent } from 'react';
import classnames from 'classnames';
import get from 'lodash/get';
import Button from 'components/ui/Button';
import TabListItem from 'containers/TabListItem';
@ -39,13 +40,22 @@ export default class TabList extends PureComponent {
/>
);
let chanLabel;
const chanLimit =
get(srv.features, ['CHANLIMIT', '#'], 0) || srv.features.MAXCHANNELS;
if (chanLimit > 0) {
chanLabel = `CHANNELS (${server.channels.length}/${chanLimit})`;
} else {
chanLabel = `CHANNELS (${server.channels.length})`;
}
tabs.push(
<div
key={`${address}-chans}`}
className="tab-label"
onClick={() => openModal('channel', { server: address })}
>
<span>CHANNELS ({server.channels.length})</span>
<span>{chanLabel}</span>
<Button>+</Button>
</div>
);

View File

@ -16,7 +16,8 @@ describe('server reducer', () => {
status: {
connected: false,
error: null
}
},
features: {}
}
});
@ -30,7 +31,8 @@ describe('server reducer', () => {
status: {
connected: false,
error: null
}
},
features: {}
}
});
@ -47,7 +49,8 @@ describe('server reducer', () => {
status: {
connected: false,
error: null
}
},
features: {}
},
'127.0.0.2': {
name: 'srv',
@ -56,7 +59,8 @@ describe('server reducer', () => {
status: {
connected: false,
error: null
}
},
features: {}
}
});
});
@ -216,7 +220,8 @@ describe('server reducer', () => {
editedNick: null,
status: {
connected: true
}
},
features: {}
},
'127.0.0.2': {
name: 'stuffz',
@ -224,7 +229,8 @@ describe('server reducer', () => {
editedNick: null,
status: {
connected: false
}
},
features: {}
}
});
});
@ -247,7 +253,8 @@ describe('server reducer', () => {
editedNick: null,
status: {
connected: true
}
},
features: {}
}
});
@ -266,7 +273,8 @@ describe('server reducer', () => {
status: {
connected: false,
error: 'Bad stuff happened'
}
},
features: {}
}
});
});

View File

@ -70,6 +70,7 @@ export const socket = createSocketActions([
'channel_search',
'connection_update',
'error',
'features',
'join',
'message',
'mode',

View File

@ -45,7 +45,8 @@ export default createReducer(
status: {
connected: false,
error: null
}
},
features: {}
};
}
},
@ -79,8 +80,8 @@ export default createReducer(
[actions.socket.SERVERS](state, { data }) {
if (data) {
data.forEach(({ host, name, nick, status }) => {
state[host] = { name, nick, status, editedNick: null };
data.forEach(({ host, name = host, nick, status, features = {} }) => {
state[host] = { name, nick, status, features, editedNick: null };
});
}
},
@ -90,6 +91,17 @@ export default createReducer(
state[server].status.connected = connected;
state[server].status.error = error;
}
},
[actions.socket.FEATURES](state, { server, features }) {
const srv = state[server];
if (srv) {
srv.features = features;
if (features.NETWORK && srv.name === server) {
srv.name = features.NETWORK;
}
}
}
}
);

View File

@ -14,7 +14,7 @@ const (
)
func (c *Client) Casefold(s string) string {
mapping := c.Support.Get("CASEMAPPING")
mapping := c.Features.String("CASEMAPPING")
if mapping == "" {
mapping = RFC1459
}
@ -22,7 +22,7 @@ func (c *Client) Casefold(s string) string {
}
func (c *Client) EqualFold(s1, s2 string) bool {
mapping := c.Support.Get("CASEMAPPING")
mapping := c.Features.String("CASEMAPPING")
if mapping == "" {
mapping = RFC1459
}

View File

@ -11,20 +11,20 @@ import (
)
type Client struct {
Server string
Host string
TLS bool
TLSConfig *tls.Config
Password string
Username string
Realname string
Server string
Host string
TLS bool
TLSConfig *tls.Config
Password string
Username string
Realname string
HandleNickInUse func(string) string
Messages chan *Message
ConnectionChanged chan ConnectionState
HandleNickInUse func(string) string
nick string
channels []string
Support *iSupport
Features *Features
nick string
channels []string
conn net.Conn
connected bool
@ -44,7 +44,7 @@ type Client struct {
func NewClient(nick, username string) *Client {
return &Client{
nick: nick,
Support: newISupport(),
Features: NewFeatures(),
Username: username,
Realname: nick,
Messages: make(chan *Message, 32),

View File

@ -232,7 +232,7 @@ func (c *Client) recv() {
go c.send()
case ReplyISupport:
c.Support.parse(msg.Params)
c.Features.Parse(msg.Params)
case ErrNicknameInUse:
if c.HandleNickInUse != nil {

134
pkg/irc/feature.go Normal file
View File

@ -0,0 +1,134 @@
package irc
import (
"strconv"
"strings"
"sync"
)
type Features struct {
m map[string]interface{}
lock sync.Mutex
}
func NewFeatures() *Features {
return &Features{
m: map[string]interface{}{},
}
}
func (f *Features) Map() map[string]interface{} {
m := map[string]interface{}{}
f.lock.Lock()
for k, v := range f.m {
m[k] = v
}
f.lock.Unlock()
return m
}
func (f *Features) Parse(params []string) {
f.lock.Lock()
for _, param := range params[1 : len(params)-1] {
key, val := splitParam(param)
if key == "" {
continue
}
if key[0] == '-' {
delete(f.m, key[1:])
} else {
if t, ok := featureTransforms[key]; ok {
f.m[key] = t(val)
} else {
f.m[key] = val
}
}
}
f.lock.Unlock()
}
func (f *Features) Has(key string) bool {
f.lock.Lock()
_, has := f.m[key]
f.lock.Unlock()
return has
}
func (f *Features) Get(key string) interface{} {
f.lock.Lock()
v := f.m[key]
f.lock.Unlock()
return v
}
func (f *Features) String(key string) string {
if v, ok := f.Get(key).(string); ok {
return v
}
return ""
}
func (f *Features) Int(key string) int {
if v, ok := f.Get(key).(int); ok {
return v
}
return 0
}
type featureTransform func(interface{}) interface{}
func toInt(v interface{}) interface{} {
s := v.(string)
if s == "" {
return 0
}
i, _ := strconv.Atoi(s)
return i
}
func toCharList(v interface{}) interface{} {
s := v.(string)
list := make([]string, len(s))
for i := range s {
list[i] = s[i : i+1]
}
return list
}
func parseChanlimit(v interface{}) interface{} {
limits := map[string]int{}
pairs := strings.Split(v.(string), ",")
for _, p := range pairs {
pair := strings.Split(p, ":")
if len(pair) == 2 {
prefixes := pair[0]
limit, _ := strconv.Atoi(pair[1])
for i := range prefixes {
limits[prefixes[i:i+1]] = limit
}
}
}
return limits
}
var featureTransforms = map[string]featureTransform{
"AWAYLEN": toInt,
"CHANLIMIT": parseChanlimit,
"CHANNELLEN": toInt,
"CHANTYPES": toCharList,
"HOSTLEN": toInt,
"KICKLEN": toInt,
"MAXCHANNELS": toInt,
"MAXTARGETS": toInt,
"MODES": toInt,
"NICKLEN": toInt,
"TOPICLEN": toInt,
"USERLEN": toInt,
}

49
pkg/irc/feature_test.go Normal file
View File

@ -0,0 +1,49 @@
package irc
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseFeatures(t *testing.T) {
s := NewFeatures()
featureTransforms["CAKE"] = toInt
s.Parse([]string{"bob", "CAKE=31", "PIE", ":durr"})
assert.Equal(t, 31, s.Int("CAKE"))
assert.Equal(t, "", s.String("CAKE"))
assert.True(t, s.Has("CAKE"))
assert.True(t, s.Has("PIE"))
assert.False(t, s.Has("APPLES"))
assert.Equal(t, "", s.String("APPLES"))
assert.Equal(t, 0, s.Int("APPLES"))
s.Parse([]string{"bob", "-PIE", ":hurr"})
assert.False(t, s.Has("PIE"))
s.Parse([]string{"bob", "CAKE=1337", ":durr"})
assert.Equal(t, 1337, s.Int("CAKE"))
s.Parse([]string{"bob", "CAKE=", ":durr"})
assert.Equal(t, "", s.String("CAKE"))
assert.True(t, s.Has("CAKE"))
delete(featureTransforms, "CAKE")
s.Parse([]string{"bob", "CAKE===", ":durr"})
assert.Equal(t, "==", s.String("CAKE"))
s.Parse([]string{"bob", "-CAKE=31", ":durr"})
assert.False(t, s.Has("CAKE"))
s.Parse([]string{"bob", "CHANLIMIT=#&:50", ":durr"})
assert.Equal(t, map[string]int{"#": 50, "&": 50}, s.Get("CHANLIMIT"))
s.Parse([]string{"bob", "CHANLIMIT=#:50,&:25", ":durr"})
assert.Equal(t, map[string]int{"#": 50, "&": 25}, s.Get("CHANLIMIT"))
s.Parse([]string{"bob", "CHANLIMIT=&:50,#:", ":durr"})
assert.Equal(t, map[string]int{"#": 0, "&": 50}, s.Get("CHANLIMIT"))
s.Parse([]string{"bob", "CHANTYPES=#&", ":durr"})
assert.Equal(t, []string{"#", "&"}, s.Get("CHANTYPES"))
}

View File

@ -2,9 +2,6 @@ package irc
import (
"strings"
"sync"
"github.com/spf13/cast"
)
type Message struct {
@ -96,55 +93,6 @@ func ParseMessage(line string) *Message {
return &msg
}
type iSupport struct {
support map[string]string
lock sync.Mutex
}
func newISupport() *iSupport {
return &iSupport{
support: map[string]string{},
}
}
func (i *iSupport) parse(params []string) {
i.lock.Lock()
for _, param := range params[1 : len(params)-1] {
key, val := splitParam(param)
if key == "" {
continue
}
if key[0] == '-' {
delete(i.support, key[1:])
} else {
i.support[key] = val
}
}
i.lock.Unlock()
}
func (i *iSupport) Has(key string) bool {
i.lock.Lock()
_, has := i.support[key]
i.lock.Unlock()
return has
}
func (i *iSupport) Get(key string) string {
i.lock.Lock()
v := i.support[key]
i.lock.Unlock()
return v
}
func (i *iSupport) GetInt(key string) int {
i.lock.Lock()
v := i.support[key]
i.lock.Unlock()
return cast.ToInt(v)
}
func splitParam(param string) (string, string) {
parts := strings.SplitN(param, "=", 2)
if len(parts) == 2 {

View File

@ -187,31 +187,3 @@ func TestBadMessage(t *testing.T) {
assert.Nil(t, ParseMessage(":"))
assert.Nil(t, ParseMessage(""))
}
func TestParseISupport(t *testing.T) {
s := newISupport()
s.parse([]string{"bob", "CAKE=31", "PIE", ":durr"})
assert.Equal(t, 31, s.GetInt("CAKE"))
assert.Equal(t, "31", s.Get("CAKE"))
assert.True(t, s.Has("CAKE"))
assert.True(t, s.Has("PIE"))
assert.False(t, s.Has("APPLES"))
assert.Equal(t, "", s.Get("APPLES"))
assert.Equal(t, 0, s.GetInt("APPLES"))
s.parse([]string{"bob", "-PIE", ":hurr"})
assert.False(t, s.Has("PIE"))
s.parse([]string{"bob", "CAKE=1337", ":durr"})
assert.Equal(t, 1337, s.GetInt("CAKE"))
s.parse([]string{"bob", "CAKE=", ":durr"})
assert.Equal(t, "", s.Get("CAKE"))
assert.True(t, s.Has("CAKE"))
s.parse([]string{"bob", "CAKE===", ":durr"})
assert.Equal(t, "==", s.Get("CAKE"))
s.parse([]string{"bob", "-CAKE=31", ":durr"})
assert.False(t, s.Has("CAKE"))
}

View File

@ -77,10 +77,16 @@ func (d *Dispatch) getIndexData(r *http.Request, path string, state *State) *ind
server.Username = ""
server.Realname = ""
data.Servers = append(data.Servers, Server{
s := Server{
Server: server,
Status: newConnectionUpdate(server.Host, connections[server.Host]),
})
}
if i, ok := state.irc[server.Host]; ok {
s.Features = i.Features.Map()
}
data.Servers = append(data.Servers, s)
}
channels, err := state.user.GetChannels()

View File

@ -216,6 +216,22 @@ func (i *ircHandler) info(msg *irc.Message) {
})
}
func (i *ircHandler) features(msg *irc.Message) {
i.state.sendJSON("features", Features{
Server: i.client.Host,
Features: i.client.Features.Map(),
})
if name := i.client.Features.String("NETWORK"); name != "" {
go func() {
server, err := i.state.user.GetServer(i.client.Host)
if err == nil && server.Name == "" {
i.state.user.SetServerName(name, server.Host)
}
}()
}
}
func (i *ircHandler) whoisUser(msg *irc.Message) {
i.whois.Nick = msg.Params[1]
i.whois.Username = msg.Params[2]
@ -369,6 +385,7 @@ func (i *ircHandler) initHandlers() {
irc.ReplyWelcome: i.info,
irc.ReplyYourHost: i.info,
irc.ReplyCreated: i.info,
irc.ReplyISupport: i.features,
irc.ReplyLUserClient: i.info,
irc.ReplyLUserOp: i.info,
irc.ReplyLUserUnknown: i.info,

View File

@ -21,7 +21,13 @@ type WSResponse struct {
type Server struct {
*storage.Server
Status ConnectionUpdate
Status ConnectionUpdate
Features map[string]interface{}
}
type Features struct {
Server string
Features map[string]interface{}
}
type ServerName struct {

View File

@ -781,6 +781,32 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer7(in *jlexer.Lexer, out
if data := in.Raw(); in.Ok() {
in.AddError((out.Status).UnmarshalJSON(data))
}
case "features":
if in.IsNull() {
in.Skip()
} else {
in.Delim('{')
if !in.IsDelim('}') {
out.Features = make(map[string]interface{})
} else {
out.Features = nil
}
for !in.IsDelim('}') {
key := string(in.String())
in.WantColon()
var v7 interface{}
if m, ok := v7.(easyjson.Unmarshaler); ok {
m.UnmarshalEasyJSON(in)
} else if m, ok := v7.(json.Unmarshaler); ok {
_ = m.UnmarshalJSON(in.Raw())
} else {
v7 = in.Interface()
}
(out.Features)[key] = v7
in.WantComma()
}
in.Delim('}')
}
case "name":
out.Name = string(in.String())
case "host":
@ -821,6 +847,36 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer7(out *jwriter.Writer,
}
out.Raw((in.Status).MarshalJSON())
}
if len(in.Features) != 0 {
const prefix string = ",\"features\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
{
out.RawByte('{')
v8First := true
for v8Name, v8Value := range in.Features {
if v8First {
v8First = false
} else {
out.RawByte(',')
}
out.String(string(v8Name))
out.RawByte(':')
if m, ok := v8Value.(easyjson.Marshaler); ok {
m.MarshalEasyJSON(out)
} else if m, ok := v8Value.(json.Marshaler); ok {
out.Raw(m.MarshalJSON())
} else {
out.Raw(json.Marshal(v8Value))
}
}
out.RawByte('}')
}
}
if in.Name != "" {
const prefix string = ",\"name\":"
if first {
@ -966,9 +1022,9 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer8(in *jlexer.Lexer, out
out.Results = (out.Results)[:0]
}
for !in.IsDelim(']') {
var v7 storage.Message
easyjson42239ddeDecodeGithubComKhliengDispatchStorage(in, &v7)
out.Results = append(out.Results, v7)
var v9 storage.Message
easyjson42239ddeDecodeGithubComKhliengDispatchStorage(in, &v9)
out.Results = append(out.Results, v9)
in.WantComma()
}
in.Delim(']')
@ -1017,11 +1073,11 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer8(out *jwriter.Writer,
}
{
out.RawByte('[')
for v8, v9 := range in.Results {
if v8 > 0 {
for v10, v11 := range in.Results {
if v10 > 0 {
out.RawByte(',')
}
easyjson42239ddeEncodeGithubComKhliengDispatchStorage(out, v9)
easyjson42239ddeEncodeGithubComKhliengDispatchStorage(out, v11)
}
out.RawByte(']')
}
@ -1520,9 +1576,9 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer13(in *jlexer.Lexer, ou
out.Channels = (out.Channels)[:0]
}
for !in.IsDelim(']') {
var v10 string
v10 = string(in.String())
out.Channels = append(out.Channels, v10)
var v12 string
v12 = string(in.String())
out.Channels = append(out.Channels, v12)
in.WantComma()
}
in.Delim(']')
@ -1583,11 +1639,11 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer13(out *jwriter.Writer,
}
{
out.RawByte('[')
for v11, v12 := range in.Channels {
if v11 > 0 {
for v13, v14 := range in.Channels {
if v13 > 0 {
out.RawByte(',')
}
out.String(string(v12))
out.String(string(v14))
}
out.RawByte(']')
}
@ -1952,9 +2008,9 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer17(in *jlexer.Lexer, ou
out.Messages = (out.Messages)[:0]
}
for !in.IsDelim(']') {
var v13 storage.Message
easyjson42239ddeDecodeGithubComKhliengDispatchStorage(in, &v13)
out.Messages = append(out.Messages, v13)
var v15 storage.Message
easyjson42239ddeDecodeGithubComKhliengDispatchStorage(in, &v15)
out.Messages = append(out.Messages, v15)
in.WantComma()
}
in.Delim(']')
@ -2007,11 +2063,11 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer17(out *jwriter.Writer,
}
{
out.RawByte('[')
for v14, v15 := range in.Messages {
if v14 > 0 {
for v16, v17 := range in.Messages {
if v16 > 0 {
out.RawByte(',')
}
easyjson42239ddeEncodeGithubComKhliengDispatchStorage(out, v15)
easyjson42239ddeEncodeGithubComKhliengDispatchStorage(out, v17)
}
out.RawByte(']')
}
@ -2232,9 +2288,9 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer19(in *jlexer.Lexer, ou
out.Content = (out.Content)[:0]
}
for !in.IsDelim(']') {
var v16 string
v16 = string(in.String())
out.Content = append(out.Content, v16)
var v18 string
v18 = string(in.String())
out.Content = append(out.Content, v18)
in.WantComma()
}
in.Delim(']')
@ -2283,11 +2339,11 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer19(out *jwriter.Writer,
}
{
out.RawByte('[')
for v17, v18 := range in.Content {
if v17 > 0 {
for v19, v20 := range in.Content {
if v19 > 0 {
out.RawByte(',')
}
out.String(string(v18))
out.String(string(v20))
}
out.RawByte(']')
}
@ -2452,9 +2508,9 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer21(in *jlexer.Lexer, ou
out.Channels = (out.Channels)[:0]
}
for !in.IsDelim(']') {
var v19 string
v19 = string(in.String())
out.Channels = append(out.Channels, v19)
var v21 string
v21 = string(in.String())
out.Channels = append(out.Channels, v21)
in.WantComma()
}
in.Delim(']')
@ -2503,11 +2559,11 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer21(out *jwriter.Writer,
}
{
out.RawByte('[')
for v20, v21 := range in.Channels {
if v20 > 0 {
for v22, v23 := range in.Channels {
if v22 > 0 {
out.RawByte(',')
}
out.String(string(v21))
out.String(string(v23))
}
out.RawByte(']')
}
@ -2823,7 +2879,134 @@ func (v *FetchMessages) UnmarshalJSON(data []byte) error {
func (v *FetchMessages) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer24(l, v)
}
func easyjson42239ddeDecodeGithubComKhliengDispatchServer25(in *jlexer.Lexer, out *Error) {
func easyjson42239ddeDecodeGithubComKhliengDispatchServer25(in *jlexer.Lexer, out *Features) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "server":
out.Server = string(in.String())
case "features":
if in.IsNull() {
in.Skip()
} else {
in.Delim('{')
if !in.IsDelim('}') {
out.Features = make(map[string]interface{})
} else {
out.Features = nil
}
for !in.IsDelim('}') {
key := string(in.String())
in.WantColon()
var v24 interface{}
if m, ok := v24.(easyjson.Unmarshaler); ok {
m.UnmarshalEasyJSON(in)
} else if m, ok := v24.(json.Unmarshaler); ok {
_ = m.UnmarshalJSON(in.Raw())
} else {
v24 = in.Interface()
}
(out.Features)[key] = v24
in.WantComma()
}
in.Delim('}')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson42239ddeEncodeGithubComKhliengDispatchServer25(out *jwriter.Writer, in Features) {
out.RawByte('{')
first := true
_ = first
if in.Server != "" {
const prefix string = ",\"server\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
out.String(string(in.Server))
}
if len(in.Features) != 0 {
const prefix string = ",\"features\":"
if first {
first = false
out.RawString(prefix[1:])
} else {
out.RawString(prefix)
}
{
out.RawByte('{')
v25First := true
for v25Name, v25Value := range in.Features {
if v25First {
v25First = false
} else {
out.RawByte(',')
}
out.String(string(v25Name))
out.RawByte(':')
if m, ok := v25Value.(easyjson.Marshaler); ok {
m.MarshalEasyJSON(out)
} else if m, ok := v25Value.(json.Marshaler); ok {
out.Raw(m.MarshalJSON())
} else {
out.Raw(json.Marshal(v25Value))
}
}
out.RawByte('}')
}
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v Features) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson42239ddeEncodeGithubComKhliengDispatchServer25(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Features) MarshalEasyJSON(w *jwriter.Writer) {
easyjson42239ddeEncodeGithubComKhliengDispatchServer25(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *Features) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson42239ddeDecodeGithubComKhliengDispatchServer25(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Features) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer25(l, v)
}
func easyjson42239ddeDecodeGithubComKhliengDispatchServer26(in *jlexer.Lexer, out *Error) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@ -2856,7 +3039,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer25(in *jlexer.Lexer, ou
in.Consumed()
}
}
func easyjson42239ddeEncodeGithubComKhliengDispatchServer25(out *jwriter.Writer, in Error) {
func easyjson42239ddeEncodeGithubComKhliengDispatchServer26(out *jwriter.Writer, in Error) {
out.RawByte('{')
first := true
_ = first
@ -2886,27 +3069,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer25(out *jwriter.Writer,
// MarshalJSON supports json.Marshaler interface
func (v Error) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson42239ddeEncodeGithubComKhliengDispatchServer25(&w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer26(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Error) MarshalEasyJSON(w *jwriter.Writer) {
easyjson42239ddeEncodeGithubComKhliengDispatchServer25(w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer26(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *Error) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson42239ddeDecodeGithubComKhliengDispatchServer25(&r, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer26(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Error) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer25(l, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer26(l, v)
}
func easyjson42239ddeDecodeGithubComKhliengDispatchServer26(in *jlexer.Lexer, out *ConnectionUpdate) {
func easyjson42239ddeDecodeGithubComKhliengDispatchServer27(in *jlexer.Lexer, out *ConnectionUpdate) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@ -2943,7 +3126,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer26(in *jlexer.Lexer, ou
in.Consumed()
}
}
func easyjson42239ddeEncodeGithubComKhliengDispatchServer26(out *jwriter.Writer, in ConnectionUpdate) {
func easyjson42239ddeEncodeGithubComKhliengDispatchServer27(out *jwriter.Writer, in ConnectionUpdate) {
out.RawByte('{')
first := true
_ = first
@ -2993,27 +3176,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer26(out *jwriter.Writer,
// MarshalJSON supports json.Marshaler interface
func (v ConnectionUpdate) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson42239ddeEncodeGithubComKhliengDispatchServer26(&w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer27(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v ConnectionUpdate) MarshalEasyJSON(w *jwriter.Writer) {
easyjson42239ddeEncodeGithubComKhliengDispatchServer26(w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer27(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *ConnectionUpdate) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson42239ddeDecodeGithubComKhliengDispatchServer26(&r, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer27(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *ConnectionUpdate) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer26(l, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer27(l, v)
}
func easyjson42239ddeDecodeGithubComKhliengDispatchServer27(in *jlexer.Lexer, out *ClientCert) {
func easyjson42239ddeDecodeGithubComKhliengDispatchServer28(in *jlexer.Lexer, out *ClientCert) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@ -3046,7 +3229,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer27(in *jlexer.Lexer, ou
in.Consumed()
}
}
func easyjson42239ddeEncodeGithubComKhliengDispatchServer27(out *jwriter.Writer, in ClientCert) {
func easyjson42239ddeEncodeGithubComKhliengDispatchServer28(out *jwriter.Writer, in ClientCert) {
out.RawByte('{')
first := true
_ = first
@ -3076,27 +3259,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer27(out *jwriter.Writer,
// MarshalJSON supports json.Marshaler interface
func (v ClientCert) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson42239ddeEncodeGithubComKhliengDispatchServer27(&w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer28(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v ClientCert) MarshalEasyJSON(w *jwriter.Writer) {
easyjson42239ddeEncodeGithubComKhliengDispatchServer27(w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer28(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *ClientCert) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson42239ddeDecodeGithubComKhliengDispatchServer27(&r, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer28(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *ClientCert) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer27(l, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer28(l, v)
}
func easyjson42239ddeDecodeGithubComKhliengDispatchServer28(in *jlexer.Lexer, out *ChannelSearchResult) {
func easyjson42239ddeDecodeGithubComKhliengDispatchServer29(in *jlexer.Lexer, out *ChannelSearchResult) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@ -3131,17 +3314,17 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer28(in *jlexer.Lexer, ou
out.Results = (out.Results)[:0]
}
for !in.IsDelim(']') {
var v22 *storage.ChannelListItem
var v26 *storage.ChannelListItem
if in.IsNull() {
in.Skip()
v22 = nil
v26 = nil
} else {
if v22 == nil {
v22 = new(storage.ChannelListItem)
if v26 == nil {
v26 = new(storage.ChannelListItem)
}
easyjson42239ddeDecodeGithubComKhliengDispatchStorage1(in, &*v22)
easyjson42239ddeDecodeGithubComKhliengDispatchStorage1(in, &*v26)
}
out.Results = append(out.Results, v22)
out.Results = append(out.Results, v26)
in.WantComma()
}
in.Delim(']')
@ -3158,7 +3341,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer28(in *jlexer.Lexer, ou
in.Consumed()
}
}
func easyjson42239ddeEncodeGithubComKhliengDispatchServer28(out *jwriter.Writer, in ChannelSearchResult) {
func easyjson42239ddeEncodeGithubComKhliengDispatchServer29(out *jwriter.Writer, in ChannelSearchResult) {
out.RawByte('{')
first := true
_ = first
@ -3172,14 +3355,14 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer28(out *jwriter.Writer,
}
{
out.RawByte('[')
for v23, v24 := range in.Results {
if v23 > 0 {
for v27, v28 := range in.Results {
if v27 > 0 {
out.RawByte(',')
}
if v24 == nil {
if v28 == nil {
out.RawString("null")
} else {
easyjson42239ddeEncodeGithubComKhliengDispatchStorage1(out, *v24)
easyjson42239ddeEncodeGithubComKhliengDispatchStorage1(out, *v28)
}
}
out.RawByte(']')
@ -3201,25 +3384,25 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer28(out *jwriter.Writer,
// MarshalJSON supports json.Marshaler interface
func (v ChannelSearchResult) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson42239ddeEncodeGithubComKhliengDispatchServer28(&w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer29(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v ChannelSearchResult) MarshalEasyJSON(w *jwriter.Writer) {
easyjson42239ddeEncodeGithubComKhliengDispatchServer28(w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer29(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *ChannelSearchResult) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson42239ddeDecodeGithubComKhliengDispatchServer28(&r, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer29(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *ChannelSearchResult) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer28(l, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer29(l, v)
}
func easyjson42239ddeDecodeGithubComKhliengDispatchStorage1(in *jlexer.Lexer, out *storage.ChannelListItem) {
isTopLevel := in.IsStart()
@ -3292,7 +3475,7 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchStorage1(out *jwriter.Writer,
}
out.RawByte('}')
}
func easyjson42239ddeDecodeGithubComKhliengDispatchServer29(in *jlexer.Lexer, out *ChannelSearch) {
func easyjson42239ddeDecodeGithubComKhliengDispatchServer30(in *jlexer.Lexer, out *ChannelSearch) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@ -3327,7 +3510,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer29(in *jlexer.Lexer, ou
in.Consumed()
}
}
func easyjson42239ddeEncodeGithubComKhliengDispatchServer29(out *jwriter.Writer, in ChannelSearch) {
func easyjson42239ddeEncodeGithubComKhliengDispatchServer30(out *jwriter.Writer, in ChannelSearch) {
out.RawByte('{')
first := true
_ = first
@ -3367,27 +3550,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer29(out *jwriter.Writer,
// MarshalJSON supports json.Marshaler interface
func (v ChannelSearch) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson42239ddeEncodeGithubComKhliengDispatchServer29(&w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer30(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v ChannelSearch) MarshalEasyJSON(w *jwriter.Writer) {
easyjson42239ddeEncodeGithubComKhliengDispatchServer29(w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer30(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *ChannelSearch) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson42239ddeDecodeGithubComKhliengDispatchServer29(&r, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer30(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *ChannelSearch) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer29(l, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer30(l, v)
}
func easyjson42239ddeDecodeGithubComKhliengDispatchServer30(in *jlexer.Lexer, out *ChannelForward) {
func easyjson42239ddeDecodeGithubComKhliengDispatchServer31(in *jlexer.Lexer, out *ChannelForward) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@ -3422,7 +3605,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer30(in *jlexer.Lexer, ou
in.Consumed()
}
}
func easyjson42239ddeEncodeGithubComKhliengDispatchServer30(out *jwriter.Writer, in ChannelForward) {
func easyjson42239ddeEncodeGithubComKhliengDispatchServer31(out *jwriter.Writer, in ChannelForward) {
out.RawByte('{')
first := true
_ = first
@ -3462,27 +3645,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer30(out *jwriter.Writer,
// MarshalJSON supports json.Marshaler interface
func (v ChannelForward) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson42239ddeEncodeGithubComKhliengDispatchServer30(&w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer31(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v ChannelForward) MarshalEasyJSON(w *jwriter.Writer) {
easyjson42239ddeEncodeGithubComKhliengDispatchServer30(w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer31(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *ChannelForward) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson42239ddeDecodeGithubComKhliengDispatchServer30(&r, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer31(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *ChannelForward) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer30(l, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer31(l, v)
}
func easyjson42239ddeDecodeGithubComKhliengDispatchServer31(in *jlexer.Lexer, out *Away) {
func easyjson42239ddeDecodeGithubComKhliengDispatchServer32(in *jlexer.Lexer, out *Away) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@ -3515,7 +3698,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer31(in *jlexer.Lexer, ou
in.Consumed()
}
}
func easyjson42239ddeEncodeGithubComKhliengDispatchServer31(out *jwriter.Writer, in Away) {
func easyjson42239ddeEncodeGithubComKhliengDispatchServer32(out *jwriter.Writer, in Away) {
out.RawByte('{')
first := true
_ = first
@ -3545,23 +3728,23 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer31(out *jwriter.Writer,
// MarshalJSON supports json.Marshaler interface
func (v Away) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson42239ddeEncodeGithubComKhliengDispatchServer31(&w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer32(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Away) MarshalEasyJSON(w *jwriter.Writer) {
easyjson42239ddeEncodeGithubComKhliengDispatchServer31(w, v)
easyjson42239ddeEncodeGithubComKhliengDispatchServer32(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *Away) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson42239ddeDecodeGithubComKhliengDispatchServer31(&r, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer32(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Away) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson42239ddeDecodeGithubComKhliengDispatchServer31(l, v)
easyjson42239ddeDecodeGithubComKhliengDispatchServer32(l, v)
}

View File

@ -109,6 +109,26 @@ func (s *BoltStore) DeleteUser(user *storage.User) error {
})
}
func (s *BoltStore) GetServer(user *storage.User, address string) (*storage.Server, error) {
var server *storage.Server
err := s.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(bucketServers)
id := serverID(user, address)
v := b.Get(id)
if v == nil {
return storage.ErrNotFound
} else {
server = &storage.Server{}
server.Unmarshal(v)
return nil
}
})
return server, err
}
func (s *BoltStore) GetServers(user *storage.User) ([]*storage.Server, error) {
var servers []*storage.Server
@ -127,7 +147,7 @@ func (s *BoltStore) GetServers(user *storage.User) ([]*storage.Server, error) {
return servers, nil
}
func (s *BoltStore) AddServer(user *storage.User, server *storage.Server) error {
func (s *BoltStore) SaveServer(user *storage.User, server *storage.Server) error {
return s.db.Batch(func(tx *bolt.Tx) error {
b := tx.Bucket(bucketServers)
data, _ := server.Marshal(nil)

View File

@ -1,6 +1,7 @@
package storage
import (
"errors"
"os"
"github.com/khlieng/dispatch/pkg/session"
@ -13,16 +14,19 @@ func Initialize(dir string) {
os.MkdirAll(Path.Root(), 0700)
}
var (
ErrNotFound = errors.New("no item found")
)
type Store interface {
GetUsers() ([]*User, error)
SaveUser(user *User) error
DeleteUser(user *User) error
GetServer(user *User, host string) (*Server, error)
GetServers(user *User) ([]*Server, error)
AddServer(user *User, server *Server) error
SaveServer(user *User, server *Server) error
RemoveServer(user *User, host string) error
SetNick(user *User, nick, host string) error
SetServerName(user *User, name, host string) error
GetChannels(user *User) ([]*Channel, error)
AddChannel(user *User, channel *Channel) error

View File

@ -137,12 +137,16 @@ type Server struct {
Realname string
}
func (u *User) GetServer(address string) (*Server, error) {
return u.store.GetServer(u, address)
}
func (u *User) GetServers() ([]*Server, error) {
return u.store.GetServers(u)
}
func (u *User) AddServer(server *Server) error {
return u.store.AddServer(u, server)
return u.store.SaveServer(u, server)
}
func (u *User) RemoveServer(address string) error {
@ -150,13 +154,24 @@ func (u *User) RemoveServer(address string) error {
}
func (u *User) SetNick(nick, address string) error {
return u.store.SetNick(u, nick, address)
server, err := u.GetServer(address)
if err != nil {
return err
}
server.Nick = nick
return u.AddServer(server)
}
func (u *User) SetServerName(name, address string) error {
return u.store.SetServerName(u, name, address)
server, err := u.GetServer(address)
if err != nil {
return err
}
server.Name = name
return u.AddServer(server)
}
// TODO: Remove topic from disk schema
type Channel struct {
Server string
Name string