use crate::{
lexicon::com::atproto::repo::TypedStrongRef,
typed::{LexiconType, TypedLexicon},
};
use serde::{Deserialize, Serialize};
pub const NSID: &str = "community.lexicon.location";
pub const ADDRESS_NSID: &str = "community.lexicon.location.address";
pub const GEO_NSID: &str = "community.lexicon.location.geo";
pub const FSQ_NSID: &str = "community.lexicon.location.fsq";
pub const HTHREE_NSID: &str = "community.lexicon.location.hthree";
#[derive(Deserialize, Serialize, Clone, PartialEq)]
#[cfg_attr(debug_assertions, derive(Debug))]
#[serde(untagged)]
pub enum LocationOrRef {
Reference(TypedStrongRef),
InlineAddress(TypedAddress),
InlineGeo(TypedGeo),
InlineHthree(TypedHthree),
InlineFsq(TypedFsq),
}
pub type Locations = Vec<LocationOrRef>;
#[derive(Serialize, Deserialize, PartialEq, Clone)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Address {
pub country: String,
#[serde(
rename = "postalCode",
skip_serializing_if = "Option::is_none",
default
)]
pub postal_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub region: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub locality: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub street: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub name: Option<String>,
}
impl LexiconType for Address {
fn lexicon_type() -> &'static str {
ADDRESS_NSID
}
}
pub type TypedAddress = TypedLexicon<Address>;
#[derive(Serialize, Deserialize, PartialEq, Clone)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Geo {
pub latitude: String,
pub longitude: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub name: Option<String>,
}
impl LexiconType for Geo {
fn lexicon_type() -> &'static str {
GEO_NSID
}
}
pub type TypedGeo = TypedLexicon<Geo>;
#[derive(Serialize, Deserialize, PartialEq, Clone)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Fsq {
pub fsq_place_id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub name: Option<String>,
}
impl LexiconType for Fsq {
fn lexicon_type() -> &'static str {
FSQ_NSID
}
}
pub type TypedFsq = TypedLexicon<Fsq>;
#[derive(Serialize, Deserialize, PartialEq, Clone)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Hthree {
pub value: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub name: Option<String>,
}
impl LexiconType for Hthree {
fn lexicon_type() -> &'static str {
HTHREE_NSID
}
}
pub type TypedHthree = TypedLexicon<Hthree>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_typed_address() {
let address = Address {
country: "USA".to_string(),
postal_code: Some("12345".to_string()),
region: Some("California".to_string()),
locality: Some("San Francisco".to_string()),
street: Some("123 Main St".to_string()),
name: Some("Office Building".to_string()),
};
let typed_address = TypedLexicon::new(address.clone());
let json = serde_json::to_value(&typed_address).unwrap();
assert_eq!(json["$type"], "community.lexicon.location.address");
assert_eq!(json["country"], "USA");
assert_eq!(json["postalCode"], "12345");
assert_eq!(json["region"], "California");
let json_str = r#"{
"$type": "community.lexicon.location.address",
"country": "Canada",
"postalCode": "K1A 0B1",
"region": "Ontario",
"locality": "Ottawa"
}"#;
let deserialized: TypedAddress = serde_json::from_str(json_str).unwrap();
assert_eq!(deserialized.inner.country, "Canada");
assert_eq!(deserialized.inner.postal_code, Some("K1A 0B1".to_string()));
assert!(deserialized.has_type_field());
}
#[test]
fn test_typed_geo() {
let geo = Geo {
latitude: "37.7749".to_string(),
longitude: "-122.4194".to_string(),
name: Some("San Francisco".to_string()),
};
let typed_geo = TypedLexicon::new(geo);
let json = serde_json::to_value(&typed_geo).unwrap();
assert_eq!(json["$type"], "community.lexicon.location.geo");
assert_eq!(json["latitude"], "37.7749");
assert_eq!(json["longitude"], "-122.4194");
assert_eq!(json["name"], "San Francisco");
let json_str = r#"{
"$type": "community.lexicon.location.geo",
"latitude": "40.7128",
"longitude": "-74.0060",
"name": "New York"
}"#;
let deserialized: TypedGeo = serde_json::from_str(json_str).unwrap();
assert_eq!(deserialized.inner.latitude, "40.7128");
assert_eq!(deserialized.inner.longitude, "-74.0060");
assert_eq!(deserialized.inner.name, Some("New York".to_string()));
assert!(deserialized.has_type_field());
}
#[test]
fn test_typed_fsq() {
let fsq = Fsq {
fsq_place_id: "4a27f3d4f964a520a4891fe3".to_string(),
name: Some("Empire State Building".to_string()),
};
let typed_fsq = TypedLexicon::new(fsq);
let json = serde_json::to_value(&typed_fsq).unwrap();
assert_eq!(json["$type"], "community.lexicon.location.fsq");
assert_eq!(json["fsq_place_id"], "4a27f3d4f964a520a4891fe3");
assert_eq!(json["name"], "Empire State Building");
let json_str = r#"{
"$type": "community.lexicon.location.fsq",
"fsq_place_id": "5642aef9498e51025cf4a7a5"
}"#;
let deserialized: TypedFsq = serde_json::from_str(json_str).unwrap();
assert_eq!(deserialized.inner.fsq_place_id, "5642aef9498e51025cf4a7a5");
assert_eq!(deserialized.inner.name, None);
assert!(deserialized.has_type_field());
}
#[test]
fn test_typed_hthree() {
let hthree = Hthree {
value: "8a2a1072b59ffff".to_string(),
name: Some("Downtown Area".to_string()),
};
let typed_hthree = TypedLexicon::new(hthree);
let json = serde_json::to_value(&typed_hthree).unwrap();
assert_eq!(json["$type"], "community.lexicon.location.hthree");
assert_eq!(json["value"], "8a2a1072b59ffff");
assert_eq!(json["name"], "Downtown Area");
let json_str = r#"{
"$type": "community.lexicon.location.hthree",
"value": "8928308280fffff"
}"#;
let deserialized: TypedHthree = serde_json::from_str(json_str).unwrap();
assert_eq!(deserialized.inner.value, "8928308280fffff");
assert_eq!(deserialized.inner.name, None);
assert!(deserialized.has_type_field());
}
#[test]
fn test_optional_fields() {
let address = Address {
country: "USA".to_string(),
postal_code: None,
region: None,
locality: None,
street: None,
name: None,
};
let typed_address = TypedLexicon::new(address);
let json = serde_json::to_value(&typed_address).unwrap();
assert_eq!(json["$type"], "community.lexicon.location.address");
assert_eq!(json["country"], "USA");
assert!(!json.as_object().unwrap().contains_key("postalCode"));
assert!(!json.as_object().unwrap().contains_key("region"));
assert!(!json.as_object().unwrap().contains_key("locality"));
assert!(!json.as_object().unwrap().contains_key("street"));
assert!(!json.as_object().unwrap().contains_key("name"));
let geo = Geo {
latitude: "0.0".to_string(),
longitude: "0.0".to_string(),
name: None,
};
let typed_geo = TypedLexicon::new(geo);
let json = serde_json::to_value(&typed_geo).unwrap();
assert_eq!(json["$type"], "community.lexicon.location.geo");
assert_eq!(json["latitude"], "0.0");
assert_eq!(json["longitude"], "0.0");
assert!(!json.as_object().unwrap().contains_key("name"));
}
}