Documentation ¶
Index ¶
- Constants
- Variables
- func MakePath(relPath string) string
- func MakeQual(relPath, name string) string
- func TrimRoot(fullPath string) string
- func TypeQual(typePath string) (path, typeName string)
- type AsserterMethod
- type Comment
- type ContainerMethod
- type EmbeddedInterface
- type EnumValue
- type Enumeration
- type ErrorStruct
- type GetterMethod
- type IOMethod
- type Interface
- type Method
- type NamedType
- type Package
- type Packages
- type SetterMethod
- type Stringer
- type Struct
- type StructField
- type TmplString
- type TypeAlias
Constants ¶
const RootPath = "github.com/diamondburned/cchat"
RootPath is the root Go module path. This path is prefixed in every package path.
Variables ¶
var Main = Packages{ MakePath("text"): { Comment: Comment{` Package text provides a rich text API for cchat interfaces to use. Asserting Although interfaces here contain asserter methods similarly to cchat, the backend should take care to not implement multiple interfaces that may seem conflicting. For example, if Avatarer is already implemented, then Imager shouldn't be. `}, Enums: []Enumeration{{ Comment: Comment{` Attribute is the type for basic rich text markup attributes. `}, Name: "Attribute", Values: []EnumValue{{ Comment: Comment{"Normal is a zero-value attribute."}, Name: "Normal", }, { Comment: Comment{"Bold represents bold text."}, Name: "Bold", }, { Comment: Comment{"Italics represents italicized text."}, Name: "Italics", }, { Comment: Comment{"Underline represents underlined text."}, Name: "Underline", }, { Comment: Comment{` Strikethrough represents struckthrough text. `}, Name: "Strikethrough", }, { Comment: Comment{` Spoiler represents spoiler text, which usually looks blacked out until hovered or clicked on. `}, Name: "Spoiler", }, { Comment: Comment{` Monospace represents monospaced text, typically for inline code. `}, Name: "Monospace", }, { Comment: Comment{` Dimmed represents dimmed text, typically slightly less visible than other text. `}, Name: "Dimmed", }}, Bitwise: true, }}, Structs: []Struct{{ Comment: Comment{` Rich is a normal text wrapped with optional format segments. `}, Name: "Rich", Fields: []StructField{ { NamedType: NamedType{"Content", "string"}, }, { Comment: Comment{` Segments are optional rich-text segment markers. `}, NamedType: NamedType{"Segments", "[]Segment"}, }, }, Stringer: Stringer{ Comment: Comment{` String returns the Content in plain text. `}, TmplString: TmplString{ Format: "%s", Fields: []string{"Content"}, }, }, }}, Interfaces: []Interface{{ Comment: Comment{` Segment is the minimum requirement for a format segment. Frontends will use this to determine when the format starts and ends. They will also assert this interface to any other formatting interface, including Linker, Colorer and Attributor. Note that a segment may implement multiple interfaces. For example, a Mentioner may also implement Colorer. `}, Name: "Segment", Methods: []Method{ GetterMethod{ Returns: []NamedType{ {Name: "start", Type: "int"}, {Name: "end", Type: "int"}, }, // contains filtered or unexported fields }, AsserterMethod{ChildType: "Colorer"}, AsserterMethod{ChildType: "Linker"}, AsserterMethod{ChildType: "Imager"}, AsserterMethod{ChildType: "Avatarer"}, AsserterMethod{ChildType: "Mentioner"}, AsserterMethod{ChildType: "Attributor"}, AsserterMethod{ChildType: "Codeblocker"}, AsserterMethod{ChildType: "Quoteblocker"}, AsserterMethod{ChildType: "MessageReferencer"}, }, }, { Comment: Comment{` MessageReferencer is similar to Linker, except it references a message instead of an arbitrary URL. As such, its appearance may be formatted similarly to a link, but this is up to the frontend to decide. When clicked, the frontend should scroll to the message with the ID returned by MessageID() and highlight it, though this is also for appearance, so the frontend may decide in detail how to display it. `}, Name: "MessageReferencer", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "string"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Linker is a hyperlink format that a segment could implement. This implies that the segment should be replaced with a hyperlink, similarly to the anchor tag with href being the URL and the inner text being the text string. `}, Name: "Linker", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Name: "url", Type: "string"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Imager implies the segment should be replaced with a (possibly inlined) image. The Imager segment must return a bound of length zero, that is, the start and end bounds must be the same, unless the Imager segment covers something meaningful, as images must not substitute texts and only complement them. An example of the start and end bounds being the same would be any inline image, and an Imager that belongs to a Mentioner segment should have its bounds overlap. Normally, implementations with separated Mentioner and Imager implementations don't have to bother about this, since with Mentioner, the same Bounds will be shared, and with Imager, the Bounds method can easily return the same variable for start and end. For segments that also implement mentioner, the image should be treated as a square avatar. `}, Name: "Imager", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Name: "url", Type: "string"}}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{ {Name: "w", Type: "int"}, {Name: "h", Type: "int"}, }, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Type: "string"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Avatarer implies the segment should be replaced with a rounded-corners image. This works similarly to Imager. For segments that also implement mentioner, the image should be treated as a round avatar. `}, Name: "Avatarer", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Name: "url", Type: "string"}}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Name: "size", Type: "int"}}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Type: "string"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Colorer is a text color format that a segment could implement. This is to be applied directly onto the text. The Color method must return a valid 32-bit RGBA color. That is, if the text color is solid, then the alpha value must be 0xFF. Frontends that support 32-bit colors must render alpha accordingly without any edge cases. `}, Name: "Colorer", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "uint32"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Mentioner implies that the segment can be clickable, and when clicked it should open up a dialog containing information from MentionInfo(). It is worth mentioning that frontends should assume whatever segment that Mentioner highlighted to be the display name of that user. This would allow frontends to flexibly layout the labels. `}, Name: "Mentioner", Methods: []Method{ GetterMethod{ Returns: []NamedType{{ Type: MakeQual("text", "Rich"), }}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Attributor is a rich text markup format that a segment could implement. This is to be applied directly onto the text. `}, Name: "Attributor", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "Attribute"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Codeblocker is a codeblock that supports optional syntax highlighting using the language given. Note that as this is a block, it will appear separately from the rest of the paragraph. This interface is equivalent to Markdown's codeblock syntax. `}, Name: "Codeblocker", Methods: []Method{ GetterMethod{ Returns: []NamedType{{ Name: "language", Type: "string", }}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Quoteblocker represents a quoteblock that behaves similarly to the blockquote HTML tag. The quoteblock may be represented typically by an actaul quoteblock or with green arrows prepended to each line. `}, Name: "Quoteblocker", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Name: "prefix", Type: "string"}}, // contains filtered or unexported fields }, }, }}, }, RootPath: { Comment: Comment{` Package cchat is a set of stabilized interfaces for cchat implementations, joining the backend and frontend together. Backend Almost anything in the backend comes with an ID. For example, a Server must have an ID, or a Session must have a user ID. The backend is required to guarantee that IDs are somehow unique. This should already be the case for most chat services; for example, Discord provides IDs for guilds, channels, members, and more. The only time that the backend should not guarantee ID uniqueness is across Sessions, because it doesn't make sense to do so. In this case, the frontend should guarantee uniqueness instead, either by discarding duplicated items, overriding them, or anything reasonable and explicit. Methods implemented by the backend that have frontend containers as arguments can do IO. Frontends must NOT rely on individual backend states and should always assume that they will block. Methods that do not return an error must NOT do any IO to prevent blocking the main thread. As such, ID() and Name() must never do any IO. Methods that do return an error may do IO, but they should be documented per method. Backend implementations have certain conditions that should be adhered to: - Storing MessagesContainer and ServersContainer are advised against; however, they should be done if need be. - Other containers such as LabelContainer and IconContainer should also not be stored; however, the same rule as above applies. - For the server list, icon updates and such that happen after their calls should use SetServers(). - For the nickname of the current server, the backend can store the state of the label container. It must, however, remove the container when the stop callback from JoinServer() is called. - Some methods that take in a container may take in a context as well. Although implementations don't have to use this context, it should try to. Note: IO in most cases usually refer to networking, but they should files and anything that is blocking, such as mutexes or semaphores. Note: As mentioned above, contexts are optional for both the frontend and backend. The frontend may use it for cancellation, and the backend may ignore it. Some interfaces can be extended. Interfaces that are extendable will have methods starting with "As" and returns another interface type. The implementation may or may not return the same struct as the interface, but the caller should not have to type assert it to a struct. They can also return nil, which should indicate the backend that the feature is not implemented. To avoid confusing, when said "A implements B," it is mostly assumed that A has a method named "AsB." It does not mean that A can be type-asserted to B. For future references, these "As" methods will be called asserter methods. Note: Backends must not do IO in the "As" methods. Most of the time, it should only conditionally check the local state and return value or nil. Below is an example of checking for an extended interface. if iconer := server.AsIconer(); iconer != nil { println("Server implements Iconer.") } Frontend Frontend contains all interfaces that a frontend can or must implement. The backend may call these methods any time from any goroutine. Thus, they should be thread-safe. They should also not block the call by doing so, as backends may call these methods in its own main thread. It is worth pointing out that frontend container interfaces will not have an error handling API, as frontends can do that themselves. Errors returned by backend methods will be errors from the backend itself and never the frontend errors. `}, Enums: []Enumeration{{ Comment: Comment{` Status represents a user's status. This might be used by the frontend to visually display the status. `}, Name: "Status", Values: []EnumValue{ {Comment{""}, "Unknown"}, {Comment{""}, "Online"}, {Comment{""}, "Idle"}, {Comment{""}, "Busy"}, {Comment{""}, "Away"}, {Comment{""}, "Offline"}, {Comment{"Invisible is reserved."}, "Invisible"}, }, }}, TypeAliases: []TypeAlias{{ Comment: Comment{` ID is the type alias for an ID string. This type is used for clarification and documentation purposes only. Implementations could either use this type or a string type. `}, NamedType: NamedType{"ID", "string"}, }}, Structs: []Struct{{ Comment: Comment{` AuthenticateEntry represents a single authentication entry, usually an email or password prompt. Passwords or similar entries should have Secrets set to true, which should imply to frontends that the fields be masked. `}, Name: "AuthenticateEntry", Fields: []StructField{ {NamedType: NamedType{"Name", "string"}}, {NamedType: NamedType{"Placeholder", "string"}}, {NamedType: NamedType{"Description", "string"}}, {NamedType: NamedType{"Secret", "bool"}}, {NamedType: NamedType{"Multiline", "bool"}}, }, }, { Comment: Comment{` CompletionEntry is a single completion entry returned by CompleteMessage. The icon URL field is optional. `}, Name: "CompletionEntry", Fields: []StructField{{ Comment: Comment{` Raw is the text to be replaced in the input box. `}, NamedType: NamedType{"Raw", "string"}, }, { Comment: Comment{` Text is the label to be displayed. `}, NamedType: NamedType{ Name: "Text", Type: MakeQual("text", "Rich"), }, }, { Comment: Comment{` Secondary is the label to be displayed on the second line, on the right of Text, or not displayed at all. This should be optional. This text may be dimmed out as styling. `}, NamedType: NamedType{ Name: "Secondary", Type: MakeQual("text", "Rich"), }, }, { Comment: Comment{` IconURL is the URL to the icon that will be displayed on the left of the text. This field is optional. `}, NamedType: NamedType{"IconURL", "string"}, }, { Comment: Comment{` Image returns whether or not the icon URL is actually an image, which indicates that the frontend should not do rounded corners. `}, NamedType: NamedType{"Image", "bool"}, }}, }, { Comment: Comment{` MessageAttachment represents a single file attachment. If needed, the frontend will close the reader after the message is sent, that is when the SendMessage function returns. The backend must not use the reader after that. `}, Name: "MessageAttachment", Fields: []StructField{ {NamedType: NamedType{"", "io.Reader"}}, {NamedType: NamedType{"Name", "string"}}, }, }, { Comment: Comment{` ReadIndication represents a read indication of a user/author in a messager server. It relates to a message ID within the server and is meant to imply that the user/author has read up to the given message ID. The frontend should override an existing author with the received ones. This could be treated as upsert operations. `}, Name: "ReadIndication", Fields: []StructField{ {NamedType: NamedType{"User", "User"}}, {NamedType: NamedType{"MessageID", "ID"}}, }, }}, ErrorStructs: []ErrorStruct{{ Struct: Struct{ Comment: Comment{` ErrInvalidConfigAtField is the structure for an error at a specific configuration field. Frontends can use this and highlight fields if the backends support it. `}, Name: "ErrInvalidConfigAtField", Fields: []StructField{ {NamedType: NamedType{"Key", "string"}}, {NamedType: NamedType{"Err", "error"}}, }, }, ErrorString: TmplString{ Format: "Error at %s: %s", Fields: []string{"Key", "Err.Error()"}, }, }}, Interfaces: []Interface{{ Comment: Comment{` Identifier requires ID() to return a uniquely identifiable string for whatever this is embedded into. Typically, servers and messages have IDs. It is worth mentioning that IDs should be consistent throughout the lifespan of the program or maybe even forever. `}, Name: "Identifier", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "ID"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Namer requires Name() to return the name of the object. Typically, this implies usernames for sessions or service names for services. Frontends can show the ID of the object when a name hasn't yet been set. The backend may immediately update the name afterwards, but assumptions should not be made. `}, Name: "Namer", Methods: []Method{ ContainerMethod{ ContainerType: "LabelContainer", // contains filtered or unexported fields }, }, }, { Comment: Comment{` Noncer adds nonce support. A nonce is defined in this context as a unique identifier from the frontend. This interface defines the common nonce getter. Nonces are useful for frontends to know if an incoming event is a reply from the server backend. As such, nonces should be roundtripped through the server. For example, IRC would use labeled responses. The Nonce method can return an empty string. This indicates that either the frontend or backend (or neither) supports nonces. Contrary to other interfaces that extend with an "Is" method, the Nonce method could return an empty string here. `}, Name: "Noncer", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "string"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` User is the interface for an identifiable author. The interface defines that an author always have an ID and a name. An example of where this interface is used would be in MessageCreate's User method or embedded in Typer. The returned ID may or may not be used by the frontend, but backends must guarantee that the User's ID is in fact a user ID. The frontend may use the ID to squash messages with the same author together. `}, Name: "User", Embeds: []EmbeddedInterface{ {InterfaceName: "Identifier"}, {InterfaceName: "Namer"}, }, }, { Comment: Comment{` Service is a complete service that's capable of multiple sessions. It has to implement the Authenticate() method, which returns multiple implementations of Authenticator. A service can implement SessionRestorer, which would indicate the frontend that it can restore past sessions. Sessions are saved using the SessionSaver interface that Session can implement. A service can also implement Configurator if it has additional configurations. The current API is a flat key-value map, which can be parsed by the backend itself into more meaningful data structures. All configurations must be optional, as frontends may not implement a configurator UI. `}, Name: "Service", Embeds: []EmbeddedInterface{{ Comment: Comment{` Identifier returns the unique identifier for the service. There is no enforced representation, but services are recommended to follow the Reverse Domain Name Notation for consistency. An example of that would be: com.github.diamondburned.cchat-discord com.github.username.service `}, InterfaceName: "Identifier", }, { Comment: Comment{` Namer returns the name of the service. `}, InterfaceName: "Namer", }}, Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "[]Authenticator"}}, // contains filtered or unexported fields }, AsserterMethod{ ChildType: "Configurator", }, AsserterMethod{ ChildType: "SessionRestorer", }, }, }, { Comment: Comment{` AuthenticateError is the error returned when authenticating. This error interface extends the normal error to allow backends to implement multi-stage authentication if needed in a clean way without needing any loops. This interface satisfies the error interface. `}, Name: "AuthenticateError", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "string"}}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Type: "[]Authenticator"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` The authenticator interface allows for a multistage initial authentication API that the backend could use. Multistage is done by calling Authenticate and check for AuthenticateError's NextStage method. `}, Name: "Authenticator", Methods: []Method{ GetterMethod{ Returns: []NamedType{{ Type: MakeQual("text", "Rich"), }}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{ Type: MakeQual("text", "Rich"), }}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Type: "[]AuthenticateEntry"}}, // contains filtered or unexported fields }, IOMethod{ Parameters: []NamedType{{Type: "[]string"}}, ReturnValue: NamedType{Type: "Session"}, ErrorType: "AuthenticateError", // contains filtered or unexported fields }, }, }, { Comment: Comment{` SessionRestorer extends Service and is called by the frontend to restore a saved session. The frontend may call this at any time, but it's usually on startup. To save a session, refer to SessionSaver. `}, Name: "SessionRestorer", Methods: []Method{ IOMethod{ Parameters: []NamedType{{Type: "map[string]string"}}, ReturnValue: NamedType{Type: "Session"}, ErrorType: "error", // contains filtered or unexported fields }, }, }, { Comment: Comment{` Configurator is an interface which the backend can implement for a primitive configuration API. `}, Name: "Configurator", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "map[string]string"}}, // contains filtered or unexported fields }, SetterMethod{ Parameters: []NamedType{{Type: "map[string]string"}}, ErrorType: "error", // contains filtered or unexported fields }, }, }, { Comment: Comment{` Session is returned after authentication on the service. It implements Name(), which should return the username most of the time. It also implements ID(), which might be used by frontends to check against User.ID() and other things. A session can implement SessionSaver, which would allow the frontend to save the session into its keyring at any time. Whether the keyring is completely secure or not is up to the frontend. For a GTK client, that would be using the GNOME Keyring daemon. `}, Name: "Session", Embeds: []EmbeddedInterface{{ Comment: Comment{` Identifier should typically return the user ID. `}, InterfaceName: "Identifier", }, { Comment: Comment{` Namer gives the name of the session, which is typically the username. `}, InterfaceName: "Namer", }, { InterfaceName: "Lister", }}, Methods: []Method{ IOMethod{ ErrorType: "error", Disposer: true, // contains filtered or unexported fields }, AsserterMethod{ChildType: "Commander"}, AsserterMethod{ChildType: "SessionSaver"}, }, }, { Comment: Comment{` SessionSaver extends Session and is called by the frontend to save the current session. This is typically called right after authentication, but a frontend may call this any time, including when it's closing. The frontend can ask to restore a session using SessionRestorer, which extends Service. The SaveSession method must not do IO; if there are any reasons that cause SaveSession to fail, then a nil map should be returned. `}, Name: "SessionSaver", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "map[string]string"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Commander is an optional interface that a session could implement for command support. This is different from just intercepting the SendMessage() API, as this extends globally to the entire session. A very primitive use of this API would be to provide additional features that are not in cchat through a very basic terminal interface. `}, Name: "Commander", Methods: []Method{ IOMethod{ Parameters: []NamedType{ {Name: "words", Type: "[]string"}, }, ReturnValue: NamedType{Type: "[]byte"}, ErrorType: "error", // contains filtered or unexported fields }, AsserterMethod{ChildType: "Completer"}, }, }, { Comment: Comment{` Server is a single server-like entity that could translate to a guild, a channel, a chat-room, and such. A server must implement at least ServerList or ServerMessage, else the frontend must treat it as a no-op. Note that the Server is allowed to implement both Lister and Messenger. This is useful when the messenger contains sub-servers, such as threads. `}, Name: "Server", Embeds: []EmbeddedInterface{ {InterfaceName: "Identifier"}, {InterfaceName: "Namer"}, }, Methods: []Method{ AsserterMethod{ChildType: "Lister"}, AsserterMethod{ChildType: "Messenger"}, AsserterMethod{ChildType: "Commander"}, AsserterMethod{ChildType: "Configurator"}, }, }, { Comment: Comment{` Lister is for servers that contain children servers. This is similar to guilds containing channels in Discord, or IRC servers containing channels. There isn't a similar stop callback API unlike other interfaces because all servers are expected to be listed. However, they could be hidden, such as collapsing a tree. The backend should call both the container and other icon and label containers, if any. `}, Name: "Lister", Methods: []Method{ GetterMethod{ Returns: []NamedType{{"", "bool"}}, // contains filtered or unexported fields }, ContainerMethod{ ContainerType: "ServersContainer", // contains filtered or unexported fields }, }, }, { Comment: Comment{` Messenger is for servers that contain messages. This is similar to Discord or IRC channels. `}, Name: "Messenger", Methods: []Method{ ContainerMethod{ ContainerType: "MessagesContainer", // contains filtered or unexported fields }, AsserterMethod{ChildType: "Sender"}, AsserterMethod{ChildType: "Editor"}, AsserterMethod{ChildType: "Actioner"}, AsserterMethod{ChildType: "Nicknamer"}, AsserterMethod{ChildType: "Backlogger"}, AsserterMethod{ChildType: "MemberLister"}, AsserterMethod{ChildType: "UnreadIndicator"}, AsserterMethod{ChildType: "TypingIndicator"}, }, }, { Comment: Comment{` Sender adds message sending to a messenger. Messengers that don't implement MessageSender will be considered read-only. `}, Name: "Sender", Methods: []Method{ IOMethod{ Parameters: []NamedType{ {Type: "SendableMessage"}, }, ErrorType: "error", // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Type: "bool"}}, // contains filtered or unexported fields }, AsserterMethod{ChildType: "Completer"}, }, }, { Comment: Comment{` Editor adds message editing to the messenger. Only EditMessage can do IO. `}, Name: "Editor", Methods: []Method{ GetterMethod{ Parameters: []NamedType{{Name: "id", Type: "ID"}}, Returns: []NamedType{{Type: "bool"}}, // contains filtered or unexported fields }, GetterMethod{ Parameters: []NamedType{{Name: "id", Type: "ID"}}, Returns: []NamedType{{Type: "string"}}, ErrorType: "error", // contains filtered or unexported fields }, IOMethod{ Parameters: []NamedType{ {Name: "id", Type: "ID"}, {Name: "content", Type: "string"}, }, ErrorType: "error", // contains filtered or unexported fields }, }, }, { Comment: Comment{` Actioner adds custom message actions into each message. Similarly to ServerMessageEditor, some of these methods may do IO. `}, Name: "Actioner", Methods: []Method{ GetterMethod{ Parameters: []NamedType{{Name: "id", Type: "ID"}}, Returns: []NamedType{{Type: "[]string"}}, // contains filtered or unexported fields }, IOMethod{ Parameters: []NamedType{ {Name: "action", Type: "string"}, {Name: "id", Type: "ID"}, }, ErrorType: "error", // contains filtered or unexported fields }, }, }, { Comment: Comment{` Nicknamer adds the current user's nickname. The frontend will not traverse up the server tree, meaning the backend must handle nickname inheritance. This also means that servers that don't implement ServerMessage also don't need to implement ServerNickname. By default, the session name should be used. `}, Name: "Nicknamer", Embeds: []EmbeddedInterface{{InterfaceName: "Namer"}}, }, { Comment: Comment{` Backlogger adds message history capabilities into a message container. The backend should send old messages using the MessageCreate method of the MessagesContainer, and the frontend should automatically sort messages based on the timestamp. As there is no stop callback, if the backend needs to fetch messages asynchronously, it is expected to use the context to know when to cancel. The frontend should usually call this method when the user scrolls to the top. It is expected to guarantee not to call Backlogger more than once on the same ID. This can usually be done by deactivating the UI. Note that the optional usage of contexts also apply here. The frontend should deactivate the UI when the backend is working. However, the frontend can accomodate this by not deactivating until another event is triggered, then freeze the UI until the method is cancelled. This works even when the backend does not use the context. `}, Name: "Backlogger", Methods: []Method{ IOMethod{ Parameters: []NamedType{ {"before", "ID"}, {"msgc", "MessagesContainer"}, }, ErrorType: "error", // contains filtered or unexported fields }, }, }, { Comment: Comment{` MemberLister adds a member list into a message server. `}, Name: "MemberLister", Methods: []Method{ ContainerMethod{ ContainerType: "MemberListContainer", // contains filtered or unexported fields }, }, }, { Comment: Comment{` ReadIndicator adds a read indicator API for frontends to show. An example of the read indicator is in Matrix, where each message can have a small avatar indicating that the user in the room has read the message. `}, Name: "ReadIndicator", Methods: []Method{ ContainerMethod{ ContainerType: "ReadContainer", // contains filtered or unexported fields }, }, }, { Comment: Comment{` UnreadIndicator adds an unread state API for frontends to use. The unread state describes whether a channel has been read or not by the current user. It is not to be confused with ReadIndicator, which indicates the unread state of others. `}, Name: "UnreadIndicator", Methods: []Method{ SetterMethod{ Parameters: []NamedType{{"messageID", "ID"}}, // contains filtered or unexported fields }, ContainerMethod{ ContainerType: "UnreadContainer", // contains filtered or unexported fields }, }, }, { Comment: Comment{` TypingIndicator optionally extends ServerMessage to provide bidirectional typing indicating capabilities. This is similar to typing events on Discord and typing client tags on IRCv3. The client should remove a typer when a message is received with the same user ID, when RemoveTyper() is called by the backend or when the timeout returned from TypingTimeout() has been reached. `}, Name: "TypingIndicator", Methods: []Method{ IOMethod{ ErrorType: "error", // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Type: "time.Duration"}}, // contains filtered or unexported fields }, ContainerMethod{ ContainerType: "TypingContainer", // contains filtered or unexported fields }, }, }, { Comment: Comment{` Completer adds autocompletion into the message composer. IO is not allowed, and the backend should do that only in goroutines and update its state for future calls. Frontends could utilize the split package inside utils for splitting words and index. This is the de-facto standard implementation for splitting words, thus backends can rely on their behaviors. `}, Name: "Completer", Methods: []Method{ GetterMethod{ Parameters: []NamedType{ {Name: "words", Type: "[]string"}, {Name: "current", Type: "int64"}, }, Returns: []NamedType{ {Type: "[]CompletionEntry"}, }, // contains filtered or unexported fields }, }, }, { Comment: Comment{` ServersContainer is any type of view that displays the list of servers. It should implement a SetServers([]Server) that the backend could use to call anytime the server list changes (at all). Typically, most frontends should implement this interface onto a tree node, as servers can be infinitely nested. Frontends should also reset the entire node and its children when SetServers is called again. `}, Name: "ServersContainer", Methods: []Method{ SetterMethod{ Parameters: []NamedType{{Type: "[]Server"}}, // contains filtered or unexported fields }, SetterMethod{ Parameters: []NamedType{{Type: "ServerUpdate"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` ServerUpdate represents a server update event. `}, Name: "ServerUpdate", Embeds: []EmbeddedInterface{{ Comment: Comment{` Server embeds a complete server. Unlike MessageUpdate, which only returns data on methods that are changed, ServerUpdate's methods must return the complete data even if they stay the same. As such, zero-value returns are treated as not updated, including the name. `}, InterfaceName: "Server", }}, Methods: []Method{ GetterMethod{ Returns: []NamedType{ {Name: "serverID", Type: "ID"}, {Name: "replace", Type: "bool"}, }, // contains filtered or unexported fields }, }, }, { Comment: Comment{` MessagesContainer is a view implementation that displays a list of messages live. This implements the 3 most common message events: CreateMessage, UpdateMessage and DeleteMessage. The frontend must handle all 3. Since this container interface extends a single Server, the frontend is allowed to have multiple views. This is usually done with tabs or splits, but the backend should update them all nonetheless. `}, Name: "MessagesContainer", Methods: []Method{ SetterMethod{ Parameters: []NamedType{{Type: "MessageCreate"}}, // contains filtered or unexported fields }, SetterMethod{ Parameters: []NamedType{{Type: "MessageUpdate"}}, // contains filtered or unexported fields }, SetterMethod{ Parameters: []NamedType{{Type: "MessageDelete"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` MessageHeader implements the minimum interface for any message event. `}, Name: "MessageHeader", Embeds: []EmbeddedInterface{{InterfaceName: "Identifier"}}, Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "time.Time"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` MessageCreate is the interface for an incoming message. `}, Name: "MessageCreate", Embeds: []EmbeddedInterface{ {Comment{""}, "MessageHeader"}, {Comment{"Noncer is optional."}, "Noncer"}, }, Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "User"}}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{ Type: MakeQual("text", "Rich"), }}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Type: "bool"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` MessageUpdate is the interface for a message update (or edit) event. It is only responsible for updating a message's content. The author's name should be updated using MessageCreate's Author. `}, Name: "MessageUpdate", Embeds: []EmbeddedInterface{{InterfaceName: "MessageHeader"}}, Methods: []Method{ GetterMethod{ Returns: []NamedType{{ Type: MakeQual("text", "Rich"), }}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` MessageDelete is the interface for a message delete event. `}, Name: "MessageDelete", Embeds: []EmbeddedInterface{{InterfaceName: "MessageHeader"}}, }, { Comment: Comment{` LabelContainer is a generic interface for any container that can hold texts. It's typically used for rich text labelling for usernames and server names. Methods that takes in a LabelContainer typically holds it in the state and may call SetLabel any time it wants. Thus, the frontend should synchronize calls with the main thread if needed. Labels given to the frontend may contain images or avatars, and the frontend has the choice to display them or not. `}, Name: "LabelContainer", Methods: []Method{ SetterMethod{ Parameters: []NamedType{{ Type: MakeQual("text", "Rich"), }}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` ReadContainer is an interface that a frontend container can implement to show the read bubbles on messages. This container typically implies the message container, but that is up to the frontend's implementation. `}, Name: "ReadContainer", Methods: []Method{ SetterMethod{ Parameters: []NamedType{{"", "[]ReadIndication"}}, // contains filtered or unexported fields }, SetterMethod{ Parameters: []NamedType{{"authorIDs", "[]ID"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` UnreadContainer is an interface that a single server container (such as a button or a tree node) can implement if it's capable of indicating the read and mentioned status for that channel. Server containers that implement this has to represent unread and mentioned differently. For example, a mentioned channel could have a red outline, while an unread channel could appear brighter. Server containers are expected to represent this information in their parent nodes as well. For example, if a server is unread, then its parent servers as well as the session node should indicate the same status. Highlighting the session and service nodes are, however, implementation details, meaning that this decision is up to the frontend to decide. `}, Name: "UnreadContainer", Methods: []Method{ SetterMethod{ Parameters: []NamedType{ {"unread", "bool"}, {"mentioned", "bool"}, }, // contains filtered or unexported fields }, }, }, { Comment: Comment{` TypingContainer is a generic interface for any container that can display users typing in the current chatbox. The typing indicator must adhere to the TypingTimeout returned from ServerMessageTypingIndicator. The backend should assume that to be the case and send events appropriately. For more documentation, refer to TypingIndicator. `}, Name: "TypingContainer", Methods: []Method{ SetterMethod{ Parameters: []NamedType{{"", "User"}}, // contains filtered or unexported fields }, SetterMethod{ Parameters: []NamedType{{Name: "authorID", Type: "ID"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` MemberListContainer is a generic interface for any container that can display a member list. This is similar to Discord's right-side member list or IRC's users list. Below is a visual representation of a typical member list container: +-MemberList-----------\ | +-Section------------| | | | | | Header - Total | | | | | | +-Member-----------| | | | Name | | | | Secondary | | | \__________________| | | | | | +-Member-----------| | | | Name | | | | Secondary | | | \__________________| \_\____________________/ `}, Name: "MemberListContainer", Methods: []Method{ SetterMethod{ Parameters: []NamedType{ {Name: "sections", Type: "[]MemberSection"}, }, // contains filtered or unexported fields }, SetterMethod{ Parameters: []NamedType{ {"sectionID", "ID"}, {"member", "ListMember"}, }, // contains filtered or unexported fields }, SetterMethod{ Parameters: []NamedType{ {"sectionID", "ID"}, {"memberID", "ID"}, }, // contains filtered or unexported fields }, }, }, { Comment: Comment{` ListMember represents a single member in the member list. Note that this interface should be treated as a static container: updating a member will involve a completely new ListMember instance with the same ID. Note that the frontend may give everyone an avatar regardless, or it may not show any avatars at all. `}, Name: "ListMember", Embeds: []EmbeddedInterface{ {InterfaceName: "Identifier"}, }, Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: MakeQual("text", "Rich")}}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{Type: "Status"}}, // contains filtered or unexported fields }, GetterMethod{ Returns: []NamedType{{ Type: MakeQual("text", "Rich"), }}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` MemberSection represents a member list section. The section name's content must be unique among other sections from the same list regardless of the rich segments. `}, Name: "MemberSection", Embeds: []EmbeddedInterface{ {InterfaceName: "Identifier"}, {InterfaceName: "Namer"}, }, Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "int"}}, // contains filtered or unexported fields }, AsserterMethod{ChildType: "MemberDynamicSection"}, }, }, { Comment: Comment{` MemberDynamicSection represents a dynamically loaded member list section. The section behaves similarly to MemberSection, except the information displayed will be considered incomplete until LoadMore returns false. LoadLess can be called by the client to mark chunks as stale, which the server can then unsubscribe from. `}, Name: "MemberDynamicSection", Methods: []Method{ IOMethod{ ReturnValue: NamedType{Type: "bool"}, // contains filtered or unexported fields }, IOMethod{ ReturnValue: NamedType{Type: "bool"}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` SendableMessage is the bare minimum interface of a sendable message, that is, a message that can be sent with SendMessage(). This allows the frontend to implement its own message data implementation. An example of extending this interface is MessageNonce, which is similar to IRCv3's labeled response extension or Discord's nonces. The frontend could implement this interface and check if incoming MessageCreate events implement the same interface. `}, Name: "SendableMessage", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "string"}}, // contains filtered or unexported fields }, AsserterMethod{ChildType: "Noncer"}, AsserterMethod{ChildType: "Replier"}, AsserterMethod{ChildType: "Attacher"}, }, }, { Comment: Comment{` Replier indicates that the message being sent is a reply to something. Frontends that support replies can assume that all messages in a Sender can be replied to, and the backend can choose to do nothing to the replied ID. `}, Name: "Replier", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "ID"}}, // contains filtered or unexported fields }, }, }, { Comment: Comment{` Attacher adds attachments into the message being sent. `}, Name: "Attacher", Methods: []Method{ GetterMethod{ Returns: []NamedType{{Type: "[]MessageAttachment"}}, // contains filtered or unexported fields }, }, }}, }, }
var TabWidth = 4
TabWidth is used to format comments.
Functions ¶
func MakeQual ¶
MakeQual returns a qualified identifier that is the full path and name of something.
Types ¶
type AsserterMethod ¶
type AsserterMethod struct { // ChildType is the children type that is returned. ChildType string }
AsserterMethod is a method that allows the parent interface to extend itself into children interfaces. These methods must not do IO.
func (AsserterMethod) Qual ¶
func (m AsserterMethod) Qual() (path, name string)
Qual returns what TypeQual returns with m.ChildType.
func (AsserterMethod) UnderlyingComment ¶
func (m AsserterMethod) UnderlyingComment() Comment
func (AsserterMethod) UnderlyingName ¶
func (m AsserterMethod) UnderlyingName() string
UnderlyingName returns the name of the method.
type Comment ¶
type Comment struct {
Raw string
}
Comment represents a raw comment string. Most use cases should use GoString() to get the comment's content.
func (Comment) GoString ¶
GoString formats the documentation string in 80 columns wide paragraphs and prefix each line with "// ". The ident argument controls the nested level. If less than or equal to zero, then it is changed to 1, which is the top level.
type ContainerMethod ¶
type ContainerMethod struct { // ContainerType is the name of the container interface. The name will // almost always have "Container" as its suffix. ContainerType string // contains filtered or unexported fields }
ContainerMethod is a method that uses a Container. These methods can do IO, and they must always take in a context and return an error. The context is used for both stopping an ongoing IO operation and disconnecting background handlers for the container.
func (ContainerMethod) Qual ¶
func (m ContainerMethod) Qual() (path, name string)
Qual returns what TypeQual returns with m.ContainerType.
func (ContainerMethod) UnderlyingComment ¶
func (m ContainerMethod) UnderlyingComment() Comment
func (ContainerMethod) UnderlyingName ¶
func (m ContainerMethod) UnderlyingName() string
type EmbeddedInterface ¶
type EnumValue ¶
func (EnumValue) IsPlaceholder ¶
IsPlaceholder returns true if the enumeration value is meant to be a placeholder. In Go, it would look like this:
const ( _ EnumType = iota // IsPlaceholder() == true V1 )
type Enumeration ¶
type Enumeration struct { Comment Comment Name string Values []EnumValue // Bitwise is true if the enumeration is a bitwise one. The type would then // be uint32 instead of uint8, allowing for 32 constants. As usual, the // first value of enum must be 0. Bitwise bool }
Enumeration returns a Go enumeration.
func (Enumeration) GoType ¶
func (e Enumeration) GoType() string
GoType returns uint8 for a normal enum and uint32 for a bitwise enum. It returns an empty string if the length of values is overbound.
The maximum number of values in a normal enum is math.MaxUint8 or 255. The maximum number of values in a bitwise enum is 32 for 32 bits in a uint32.
type ErrorStruct ¶
type ErrorStruct struct { Struct ErrorString TmplString // used for Error() }
ErrorStruct are structs that implement the "error" interface and starts with "Err".
func (ErrorStruct) Wrapped ¶
func (t ErrorStruct) Wrapped() (fieldName string)
Wrapped returns true if the error struct contains a field with the error type.
type GetterMethod ¶
type GetterMethod struct { // Parameters is the list of parameters in the function. Parameters []NamedType // Returns is the list of named types returned from the function. Returns []NamedType // ErrorType is non-empty if the function returns an error at the end of // returns. For the most part, this field should be "error" if that is the // case, but some methods may choose to extend the error base type. ErrorType string // contains filtered or unexported fields }
GetterMethod is a method that returns a regular value. These methods must not do any IO. An example of one would be ID() returning ID.
func (GetterMethod) ReturnError ¶
func (m GetterMethod) ReturnError() bool
ReturnError returns true if the method can error out.
func (GetterMethod) UnderlyingComment ¶
func (m GetterMethod) UnderlyingComment() Comment
func (GetterMethod) UnderlyingName ¶
func (m GetterMethod) UnderlyingName() string
type IOMethod ¶
type IOMethod struct { // Parameters is the list of parameters in the function. Parameters []NamedType // ReturnValue is the return value in the function. ReturnValue NamedType // ErrorType is non-empty if the function returns an error at the end of // returns. For the most part, this field should be "error" if that is the // case, but some methods may choose to extend the error base type. ErrorType string // Disposer indicates that this method signals the disposal of the interface // that implements it. This is used similarly to stop functions, except all // disposer functions can be synchronous, and the frontend should handle // indicating such. The frontend can also ignore the result and run the // method in a dangling goroutine, but it must gracefully wait for it to be // done on exit. // // Similarly to the stop function, the instance that the disposer method belongs // to will also be considered invalid and should be freed once the function // returns regardless of the error. Disposer bool // contains filtered or unexported fields }
IOMethod is a regular method that can do IO and thus is blocking. These methods usually always return errors. IOMethods must always have means of cancelling them in the API, but implementations don't have to use it; as such, the user should always have a timeout to gracefully wait.
func (IOMethod) ReturnError ¶
ReturnError returns true if the method can error out.
func (IOMethod) UnderlyingComment ¶
func (m IOMethod) UnderlyingComment() Comment
func (IOMethod) UnderlyingName ¶
func (m IOMethod) UnderlyingName() string
type Interface ¶
type Interface struct { Comment Comment Name string Embeds []EmbeddedInterface Methods []Method // actual methods }
func (Interface) IsContainer ¶
IsContainer returns true if the interface is a frontend container interface, that is when its name has "Container" at the end.
type NamedType ¶
type NamedType struct { Name string // optional Type string // import/path.Type OR (import/path).Type }
NamedType is an optionally named value with a type.
type Package ¶
type Package struct { Comment Comment Enums []Enumeration TypeAliases []TypeAlias Structs []Struct ErrorStructs []ErrorStruct Interfaces []Interface }
func (Package) Enum ¶ added in v0.3.20
func (p Package) Enum(name string) *Enumeration
Enum finds an enumeration. Nil is returned if none is found.
func (Package) FindType ¶ added in v0.3.20
FindType finds any type. Nil is returned if nothing is found; a pointer to any of the type is returned if name is found.
type SetterMethod ¶
type SetterMethod struct { // Parameters is the list of parameters in the function. These parameters // should be the parameters to set. Parameters []NamedType // ErrorType is non-empty if the function returns an error at the end of // returns. An error may be returned from the backend if the input is // invalid, but it must not do IO. Frontend setters must never error. ErrorType string // contains filtered or unexported fields }
SetterMethod is a method that sets values. These methods must not do IO, and they have to be non-blocking. They're used only for containers. Actual setter methods implemented by the backend belongs to IOMethods.
Clients should always keep track of the values given to setters and free them once the setters are called again with new values.
func (SetterMethod) UnderlyingComment ¶
func (m SetterMethod) UnderlyingComment() Comment
func (SetterMethod) UnderlyingName ¶
func (m SetterMethod) UnderlyingName() string
type Stringer ¶ added in v0.2.10
type Stringer struct { Comment TmplString }
type Struct ¶
type Struct struct { Comment Comment Name string Fields []StructField Stringer Stringer // used for String() }
type StructField ¶
type TmplString ¶
type TmplString struct { Format string // printf format syntax Fields []string // list of struct fields }
TmplString is a generation-time templated string. It is used for string concatenation.
Given the following TmplString:
TmplString{Format: "Hello, %s", Fields: []string{"Foo()"}}
The output should be the same as the output of
fmt.Sprintf("Hello, %s", v.Foo())
func (TmplString) IsEmpty ¶ added in v0.2.10
func (s TmplString) IsEmpty() bool
IsEmpty returns true if TmplString is zero.