use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use automerge::{ChangeHash, ObjId, ObjType, Prop, ReadDoc, Value, transaction::Transactable};
use crate::{Automorph, Error, PrimitiveChanged, Result, ScalarCursor};
impl Automorph for Ipv4Addr {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
self.to_string().save(doc, obj, prop)
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
let s = String::load(doc, obj, prop)?;
s.parse()
.map_err(|e| Error::invalid_value(format!("invalid IPv4 address: {}", e)))
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
let s = String::load_at(doc, obj, prop, heads)?;
s.parse()
.map_err(|e| Error::invalid_value(format!("invalid IPv4 address: {}", e)))
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let doc_value = Self::load(doc, obj, prop)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let doc_value = Self::load_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let new_value = Self::load(doc, obj, prop)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let new_value = Self::load_at(doc, obj, prop, heads)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
}
impl Automorph for Ipv6Addr {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
self.to_string().save(doc, obj, prop)
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
let s = String::load(doc, obj, prop)?;
s.parse()
.map_err(|e| Error::invalid_value(format!("invalid IPv6 address: {}", e)))
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
let s = String::load_at(doc, obj, prop, heads)?;
s.parse()
.map_err(|e| Error::invalid_value(format!("invalid IPv6 address: {}", e)))
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let doc_value = Self::load(doc, obj, prop)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let doc_value = Self::load_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let new_value = Self::load(doc, obj, prop)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let new_value = Self::load_at(doc, obj, prop, heads)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
}
impl Automorph for IpAddr {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
let map_id = match doc.get(obj, prop.clone())? {
Some((Value::Object(ObjType::Map), id)) => id,
_ => doc.put_object(obj, prop, ObjType::Map)?,
};
match self {
IpAddr::V4(addr) => {
"v4".to_string().save(doc, &map_id, "version")?;
addr.save(doc, &map_id, "addr")?;
}
IpAddr::V6(addr) => {
"v6".to_string().save(doc, &map_id, "version")?;
addr.save(doc, &map_id, "addr")?;
}
}
Ok(())
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
match doc.get(obj, prop)? {
Some((Value::Object(ObjType::Map), map_id)) => {
let version = String::load(doc, &map_id, "version")?;
match version.as_str() {
"v4" => Ok(IpAddr::V4(Ipv4Addr::load(doc, &map_id, "addr")?)),
"v6" => Ok(IpAddr::V6(Ipv6Addr::load(doc, &map_id, "addr")?)),
_ => Err(Error::invalid_value(format!(
"unknown IP version: {}",
version
))),
}
}
Some((v, _)) => Err(Error::type_mismatch(
"IpAddr (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
match doc.get_at(obj, prop, heads)? {
Some((Value::Object(ObjType::Map), map_id)) => {
let version = String::load_at(doc, &map_id, "version", heads)?;
match version.as_str() {
"v4" => Ok(IpAddr::V4(Ipv4Addr::load_at(doc, &map_id, "addr", heads)?)),
"v6" => Ok(IpAddr::V6(Ipv6Addr::load_at(doc, &map_id, "addr", heads)?)),
_ => Err(Error::invalid_value(format!(
"unknown IP version: {}",
version
))),
}
}
Some((v, _)) => Err(Error::type_mismatch(
"IpAddr (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let doc_value = Self::load(doc, obj, prop)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let doc_value = Self::load_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let new_value = Self::load(doc, obj, prop)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let new_value = Self::load_at(doc, obj, prop, heads)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
}
impl Automorph for SocketAddrV4 {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
let map_id = match doc.get(obj, prop.clone())? {
Some((Value::Object(ObjType::Map), id)) => id,
_ => doc.put_object(obj, prop, ObjType::Map)?,
};
self.ip().save(doc, &map_id, "ip")?;
self.port().save(doc, &map_id, "port")?;
Ok(())
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
match doc.get(obj, prop)? {
Some((Value::Object(ObjType::Map), map_id)) => {
let ip = Ipv4Addr::load(doc, &map_id, "ip")?;
let port = u16::load(doc, &map_id, "port")?;
Ok(SocketAddrV4::new(ip, port))
}
Some((v, _)) => Err(Error::type_mismatch(
"SocketAddrV4 (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
match doc.get_at(obj, prop, heads)? {
Some((Value::Object(ObjType::Map), map_id)) => {
let ip = Ipv4Addr::load_at(doc, &map_id, "ip", heads)?;
let port = u16::load_at(doc, &map_id, "port", heads)?;
Ok(SocketAddrV4::new(ip, port))
}
Some((v, _)) => Err(Error::type_mismatch(
"SocketAddrV4 (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let doc_value = Self::load(doc, obj, prop)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let doc_value = Self::load_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let new_value = Self::load(doc, obj, prop)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let new_value = Self::load_at(doc, obj, prop, heads)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
}
impl Automorph for SocketAddrV6 {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
let map_id = match doc.get(obj, prop.clone())? {
Some((Value::Object(ObjType::Map), id)) => id,
_ => doc.put_object(obj, prop, ObjType::Map)?,
};
self.ip().save(doc, &map_id, "ip")?;
self.port().save(doc, &map_id, "port")?;
self.flowinfo().save(doc, &map_id, "flowinfo")?;
self.scope_id().save(doc, &map_id, "scope_id")?;
Ok(())
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
match doc.get(obj, prop)? {
Some((Value::Object(ObjType::Map), map_id)) => {
let ip = Ipv6Addr::load(doc, &map_id, "ip")?;
let port = u16::load(doc, &map_id, "port")?;
let flowinfo = u32::load(doc, &map_id, "flowinfo")?;
let scope_id = u32::load(doc, &map_id, "scope_id")?;
Ok(SocketAddrV6::new(ip, port, flowinfo, scope_id))
}
Some((v, _)) => Err(Error::type_mismatch(
"SocketAddrV6 (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
match doc.get_at(obj, prop, heads)? {
Some((Value::Object(ObjType::Map), map_id)) => {
let ip = Ipv6Addr::load_at(doc, &map_id, "ip", heads)?;
let port = u16::load_at(doc, &map_id, "port", heads)?;
let flowinfo = u32::load_at(doc, &map_id, "flowinfo", heads)?;
let scope_id = u32::load_at(doc, &map_id, "scope_id", heads)?;
Ok(SocketAddrV6::new(ip, port, flowinfo, scope_id))
}
Some((v, _)) => Err(Error::type_mismatch(
"SocketAddrV6 (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let doc_value = Self::load(doc, obj, prop)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let doc_value = Self::load_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let new_value = Self::load(doc, obj, prop)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let new_value = Self::load_at(doc, obj, prop, heads)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
}
impl Automorph for SocketAddr {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
let map_id = match doc.get(obj, prop.clone())? {
Some((Value::Object(ObjType::Map), id)) => id,
_ => doc.put_object(obj, prop, ObjType::Map)?,
};
match self {
SocketAddr::V4(addr) => {
"v4".to_string().save(doc, &map_id, "version")?;
addr.save(doc, &map_id, "addr")?;
}
SocketAddr::V6(addr) => {
"v6".to_string().save(doc, &map_id, "version")?;
addr.save(doc, &map_id, "addr")?;
}
}
Ok(())
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
match doc.get(obj, prop)? {
Some((Value::Object(ObjType::Map), map_id)) => {
let version = String::load(doc, &map_id, "version")?;
match version.as_str() {
"v4" => Ok(SocketAddr::V4(SocketAddrV4::load(doc, &map_id, "addr")?)),
"v6" => Ok(SocketAddr::V6(SocketAddrV6::load(doc, &map_id, "addr")?)),
_ => Err(Error::invalid_value(format!(
"unknown socket addr version: {}",
version
))),
}
}
Some((v, _)) => Err(Error::type_mismatch(
"SocketAddr (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
match doc.get_at(obj, prop, heads)? {
Some((Value::Object(ObjType::Map), map_id)) => {
let version = String::load_at(doc, &map_id, "version", heads)?;
match version.as_str() {
"v4" => Ok(SocketAddr::V4(SocketAddrV4::load_at(
doc, &map_id, "addr", heads,
)?)),
"v6" => Ok(SocketAddr::V6(SocketAddrV6::load_at(
doc, &map_id, "addr", heads,
)?)),
_ => Err(Error::invalid_value(format!(
"unknown socket addr version: {}",
version
))),
}
}
Some((v, _)) => Err(Error::type_mismatch(
"SocketAddr (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let doc_value = Self::load(doc, obj, prop)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let doc_value = Self::load_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(*self != doc_value))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let new_value = Self::load(doc, obj, prop)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let new_value = Self::load_at(doc, obj, prop, heads)?;
let changed = *self != new_value;
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
}
#[cfg(test)]
mod tests {
use super::*;
use automerge::{AutoCommit, ROOT};
#[test]
fn test_ipv4() {
let mut doc = AutoCommit::new();
let addr = Ipv4Addr::new(192, 168, 1, 1);
addr.save(&mut doc, &ROOT, "addr").unwrap();
let restored = Ipv4Addr::load(&doc, &ROOT, "addr").unwrap();
assert_eq!(restored, Ipv4Addr::new(192, 168, 1, 1));
}
#[test]
fn test_ipv6() {
let mut doc = AutoCommit::new();
let addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
addr.save(&mut doc, &ROOT, "addr").unwrap();
let restored = Ipv6Addr::load(&doc, &ROOT, "addr").unwrap();
assert_eq!(restored, Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
}
#[test]
fn test_ipaddr_v4() {
let mut doc = AutoCommit::new();
let addr = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1));
addr.save(&mut doc, &ROOT, "addr").unwrap();
let restored = IpAddr::load(&doc, &ROOT, "addr").unwrap();
assert_eq!(restored, IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)));
}
#[test]
fn test_ipaddr_v6() {
let mut doc = AutoCommit::new();
let addr = IpAddr::V6(Ipv6Addr::LOCALHOST);
addr.save(&mut doc, &ROOT, "addr").unwrap();
let restored = IpAddr::load(&doc, &ROOT, "addr").unwrap();
assert_eq!(restored, IpAddr::V6(Ipv6Addr::LOCALHOST));
}
#[test]
fn test_socket_addr_v4() {
let mut doc = AutoCommit::new();
let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
addr.save(&mut doc, &ROOT, "addr").unwrap();
let restored = SocketAddrV4::load(&doc, &ROOT, "addr").unwrap();
assert_eq!(
restored,
SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080)
);
}
#[test]
fn test_socket_addr() {
let mut doc = AutoCommit::new();
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 443));
addr.save(&mut doc, &ROOT, "addr").unwrap();
let restored = SocketAddr::load(&doc, &ROOT, "addr").unwrap();
assert_eq!(
restored,
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 443))
);
}
}