macro_rules! linear_id {
($name:ident, $doc:literal) => {
#[doc = $doc]
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct $name(String);
impl $name {
#[doc = concat!("Creates a new `", stringify!($name), "` from a string-like value.")]
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl From<String> for $name {
fn from(id: String) -> Self {
Self(id)
}
}
impl From<&str> for $name {
fn from(id: &str) -> Self {
Self(id.to_owned())
}
}
impl AsRef<str> for $name {
fn as_ref(&self) -> &str {
&self.0
}
}
};
}
linear_id!(IssueId, "UUID of a Linear issue.");
linear_id!(TeamId, "UUID of a Linear team.");
linear_id!(ProjectId, "UUID of a Linear project.");
linear_id!(UserId, "UUID of a Linear user.");
linear_id!(LabelId, "UUID of a Linear issue label.");
linear_id!(WorkflowStateId, "UUID of a Linear workflow state.");
linear_id!(ProjectMilestoneId, "UUID of a Linear project milestone.");
linear_id!(CommentId, "UUID of a Linear comment.");
linear_id!(IssueRelationId, "UUID of a Linear issue relation.");
linear_id!(CycleId, "UUID of a Linear cycle.");
linear_id!(OrganizationId, "UUID of a Linear organization (workspace).");
linear_id!(ProjectStatusId, "UUID of a Linear project status.");
linear_id!(TemplateId, "UUID of a Linear template.");
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IssueRef {
Id(IssueId),
Identifier(String),
}
impl IssueRef {
pub fn identifier(identifier: impl Into<String>) -> Self {
Self::Identifier(identifier.into())
}
pub fn api_string(&self) -> &str {
match self {
Self::Id(id) => id.as_str(),
Self::Identifier(identifier) => identifier,
}
}
}
impl std::fmt::Display for IssueRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.api_string())
}
}
impl From<IssueId> for IssueRef {
fn from(id: IssueId) -> Self {
Self::Id(id)
}
}
impl From<&IssueId> for IssueRef {
fn from(id: &IssueId) -> Self {
Self::Id(id.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn id_roundtrips_through_serde_as_a_plain_string() {
let id = IssueId::new("abc-123");
let json = serde_json::to_string(&id).unwrap();
assert_eq!(json, "\"abc-123\"");
let back: IssueId = serde_json::from_str(&json).unwrap();
assert_eq!(back, id);
}
#[test]
fn issue_ref_api_string() {
let by_id = IssueRef::from(IssueId::new("uuid-1"));
assert_eq!(by_id.api_string(), "uuid-1");
let by_identifier = IssueRef::identifier("ENG-123");
assert_eq!(by_identifier.api_string(), "ENG-123");
assert_eq!(by_identifier.to_string(), "ENG-123");
}
}