// The few questions worth asking that a field cannot answer.
//
// Deliberately short. Every field on these structs is public, so an accessor
// has to earn its place by collapsing an option chain a caller would otherwise
// get subtly wrong -- which name Slack's own clients show, which timestamp
// identifies a thread. Anything that is just `self.field` is not here.
///|
/// The name to show for a user.
///
/// `display_name`, then `real_name`, then `name`, then the id. That is the
/// order Slack's own clients fall back in, and the reason a bot that shows
/// `user.name` looks wrong: `name` is the handle, which for many workspaces has
/// not been the thing anyone recognises since Slack made display names
/// editable. An empty string counts as absent, because Slack sends `""` rather
/// than omitting the field when a user has not set one.
pub fn User::display(self : User) -> String {
for candidate in [self.display_name(), self.real_name, self.name] {
if candidate is Some(name) && name != "" {
return name
}
}
self.id
}
///|
/// The user's `display_name`, which lives on the profile rather than the user.
fn User::display_name(self : User) -> String? {
match self.profile {
Some(profile) => profile.display_name
None => None
}
}
///|
/// The name to show for a conversation, falling back to its id.
///
/// A DM has no `name` at all, so the id is not a defensive fallback here -- it
/// is the answer for every `is_im` conversation Slack returns.
pub fn Channel::display(self : Channel) -> String {
if self.name is Some(name) && name != "" {
name
} else {
self.id
}
}
///|
/// A topic or purpose's text, which is absent and empty often enough that
/// unwrapping it at every call site is noise.
pub fn ChannelText::text(self : ChannelText) -> String {
self.value.unwrap_or("")
}
///|
/// The `ts` that identifies the thread a message belongs to.
///
/// `thread_ts` on a reply, `ts` on anything else -- including the message that
/// STARTED the thread, which carries both and whose two values are equal. Using
/// `thread_ts` alone loses every message that is not in a thread; using `ts`
/// alone scatters a thread across its replies.
pub fn Message::thread_root(self : Message) -> String? {
match self.thread_ts {
Some(ts) => Some(ts)
None => self.ts
}
}
///|
/// Whether this message is a reply inside a thread, as opposed to the parent
/// that started one.
///
/// The parent carries `thread_ts == ts`, so testing for `thread_ts` alone
/// counts it as its own reply.
pub fn Message::is_thread_reply(self : Message) -> Bool {
match (self.thread_ts, self.ts) {
(Some(thread), Some(ts)) => thread != ts
(Some(_), None) => true
_ => false
}
}