#![no_std]
#![warn(missing_docs)]
#![warn(deprecated_in_future)]
#![doc(test(attr(warn(unused))))]
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "serde")]
pub extern crate serde;
use core::fmt;
use core::str::FromStr;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use internals::error::InputString;
#[cfg(feature = "serde")]
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum NetworkKind {
Main,
Test,
}
impl NetworkKind {
pub const fn is_mainnet(self) -> bool {
matches!(self, Self::Main)
}
}
impl From<Network> for NetworkKind {
fn from(network: Network) -> Self {
match network {
Network::Tidecoin => Self::Main,
Network::Testnet | Network::Regtest => Self::Test,
}
}
}
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
pub enum Network {
Tidecoin,
Testnet,
Regtest,
}
#[cfg(feature = "serde")]
impl Serialize for Network {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_display_str())
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Network {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct NetworkVisitor;
impl Visitor<'_> for NetworkVisitor {
type Value = Network;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid network identifier")
}
fn visit_str<E>(self, value: &str) -> Result<Network, E>
where
E: serde::de::Error,
{
Network::from_str(value).map_err(E::custom)
}
}
deserializer.deserialize_str(NetworkVisitor)
}
}
impl Network {
pub fn to_core_arg(self) -> &'static str {
match self {
Self::Tidecoin => "main",
Self::Testnet => "test",
Self::Regtest => "regtest",
}
}
pub fn from_core_arg(core_arg: &str) -> Result<Self, ParseNetworkError> {
let network = match core_arg {
"main" => Self::Tidecoin,
"test" => Self::Testnet,
"regtest" => Self::Regtest,
_ => return Err(ParseNetworkError(InputString::from(core_arg))),
};
Ok(network)
}
const fn as_display_str(self) -> &'static str {
match self {
Self::Tidecoin => "tidecoin",
Self::Testnet => "testnet",
Self::Regtest => "regtest",
}
}
}
impl fmt::Display for Network {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.as_display_str())
}
}
#[cfg(feature = "serde")]
pub mod as_core_arg {
#![allow(missing_docs)]
#![allow(clippy::missing_errors_doc)]
use crate::Network;
#[allow(clippy::trivially_copy_pass_by_ref)] pub fn serialize<S>(network: &Network, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(network.to_core_arg())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Network, D::Error>
where
D: serde::Deserializer<'de>,
{
struct NetworkVisitor;
impl serde::de::Visitor<'_> for NetworkVisitor {
type Value = Network;
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
Network::from_core_arg(s).map_err(|_| {
E::invalid_value(
serde::de::Unexpected::Str(s),
&"tidecoin network encoded as a string (either main, test, or regtest)",
)
})
}
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
formatter,
"tidecoin network encoded as a string (either main, test, or regtest)"
)
}
}
deserializer.deserialize_str(NetworkVisitor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ParseNetworkError(InputString);
impl fmt::Display for ParseNetworkError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.display_cannot_parse("network"))
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseNetworkError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl FromStr for Network {
type Err = ParseNetworkError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"tidecoin" | "main" => Ok(Self::Tidecoin),
"testnet" | "test" => Ok(Self::Testnet),
"regtest" => Ok(Self::Regtest),
_ => Err(ParseNetworkError(InputString::from(s))),
}
}
}
impl AsRef<Self> for Network {
fn as_ref(&self) -> &Self {
self
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for NetworkKind {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
match bool::arbitrary(u)? {
true => Ok(Self::Main),
false => Ok(Self::Test),
}
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "std")]
use std::string::ToString;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::Network;
#[test]
#[cfg(feature = "std")]
fn string() {
assert_eq!(Network::Tidecoin.to_string(), "tidecoin");
assert_eq!(Network::Testnet.to_string(), "testnet");
assert_eq!(Network::Regtest.to_string(), "regtest");
assert_eq!("tidecoin".parse::<Network>().unwrap(), Network::Tidecoin);
assert_eq!("main".parse::<Network>().unwrap(), Network::Tidecoin);
assert_eq!("testnet".parse::<Network>().unwrap(), Network::Testnet);
assert_eq!("test".parse::<Network>().unwrap(), Network::Testnet);
assert_eq!("regtest".parse::<Network>().unwrap(), Network::Regtest);
assert!("fakenet".parse::<Network>().is_err());
}
#[test]
#[cfg(feature = "serde")]
#[cfg(feature = "std")]
fn serde_roundtrip() {
use std::{format, vec};
use Network::*;
let tests = vec![(Tidecoin, "tidecoin"), (Testnet, "testnet"), (Regtest, "regtest")];
for tc in tests {
let network = tc.0;
let want = format!("\"{}\"", tc.1);
let got = serde_json::to_string(&tc.0).expect("failed to serialize network");
assert_eq!(got, want);
let back: Network = serde_json::from_str(&got).expect("failed to deserialize network");
assert_eq!(back, network);
}
}
#[test]
fn from_to_core_arg() {
let expected_pairs = [
(Network::Tidecoin, "main"),
(Network::Testnet, "test"),
(Network::Regtest, "regtest"),
];
for (net, core_arg) in &expected_pairs {
assert_eq!(Network::from_core_arg(core_arg), Ok(*net));
assert_eq!(net.to_core_arg(), *core_arg);
}
}
#[test]
#[cfg(feature = "serde")]
fn serde_as_core_arg() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct T {
#[serde(with = "crate::as_core_arg")]
pub network: Network,
}
serde_test::assert_tokens(
&T { network: Network::Tidecoin },
&[
serde_test::Token::Struct { name: "T", len: 1 },
serde_test::Token::Str("network"),
serde_test::Token::Str("main"),
serde_test::Token::StructEnd,
],
);
}
}