transport.rs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. //! Transport types for NUT-18: Payment Requests
  2. use std::fmt;
  3. use std::str::FromStr;
  4. use bitcoin::base64::engine::{general_purpose, GeneralPurpose};
  5. use bitcoin::base64::{alphabet, Engine};
  6. use serde::{Deserialize, Serialize};
  7. use crate::nuts::nut18::error::Error;
  8. /// Transport Type
  9. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
  10. pub enum TransportType {
  11. /// Nostr
  12. #[serde(rename = "nostr")]
  13. Nostr,
  14. /// Http post
  15. #[serde(rename = "post")]
  16. HttpPost,
  17. }
  18. impl fmt::Display for TransportType {
  19. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  20. use serde::ser::Error;
  21. let t = serde_json::to_string(self).map_err(|e| fmt::Error::custom(e.to_string()))?;
  22. write!(f, "{t}")
  23. }
  24. }
  25. impl FromStr for TransportType {
  26. type Err = Error;
  27. fn from_str(s: &str) -> Result<Self, Self::Err> {
  28. match s.to_lowercase().as_str() {
  29. "nostr" => Ok(Self::Nostr),
  30. "post" => Ok(Self::HttpPost),
  31. _ => Err(Error::InvalidPrefix),
  32. }
  33. }
  34. }
  35. /// Transport
  36. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  37. pub struct Transport {
  38. /// Type
  39. #[serde(rename = "t")]
  40. pub _type: TransportType,
  41. /// Target
  42. #[serde(rename = "a")]
  43. pub target: String,
  44. /// Tags
  45. #[serde(rename = "g")]
  46. pub tags: Option<Vec<Vec<String>>>,
  47. }
  48. impl Transport {
  49. /// Create a new TransportBuilder
  50. pub fn builder() -> TransportBuilder {
  51. TransportBuilder::default()
  52. }
  53. }
  54. impl FromStr for Transport {
  55. type Err = Error;
  56. fn from_str(s: &str) -> Result<Self, Self::Err> {
  57. let decode_config = general_purpose::GeneralPurposeConfig::new()
  58. .with_decode_padding_mode(bitcoin::base64::engine::DecodePaddingMode::Indifferent);
  59. let decoded = GeneralPurpose::new(&alphabet::URL_SAFE, decode_config).decode(s)?;
  60. Ok(ciborium::from_reader(&decoded[..])?)
  61. }
  62. }
  63. /// Builder for Transport
  64. #[derive(Debug, Default, Clone)]
  65. pub struct TransportBuilder {
  66. _type: Option<TransportType>,
  67. target: Option<String>,
  68. tags: Option<Vec<Vec<String>>>,
  69. }
  70. impl TransportBuilder {
  71. /// Set transport type
  72. pub fn transport_type(mut self, transport_type: TransportType) -> Self {
  73. self._type = Some(transport_type);
  74. self
  75. }
  76. /// Set target
  77. pub fn target<S: Into<String>>(mut self, target: S) -> Self {
  78. self.target = Some(target.into());
  79. self
  80. }
  81. /// Add a tag
  82. pub fn add_tag(mut self, tag: Vec<String>) -> Self {
  83. self.tags.get_or_insert_with(Vec::new).push(tag);
  84. self
  85. }
  86. /// Set tags
  87. pub fn tags(mut self, tags: Vec<Vec<String>>) -> Self {
  88. self.tags = Some(tags);
  89. self
  90. }
  91. /// Build the Transport
  92. pub fn build(self) -> Result<Transport, &'static str> {
  93. let _type = self._type.ok_or("Transport type is required")?;
  94. let target = self.target.ok_or("Target is required")?;
  95. Ok(Transport {
  96. _type,
  97. target,
  98. tags: self.tags,
  99. })
  100. }
  101. }
  102. impl AsRef<String> for Transport {
  103. fn as_ref(&self) -> &String {
  104. &self.target
  105. }
  106. }