shrike 0.1.1

AT Protocol library for Rust
Documentation
// Code generated by lexgen. DO NOT EDIT.

#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UnspeccedGetPostThreadV2Params {
    /// Whether to include parents above the anchor.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub above: Option<bool>,
    /// Reference (AT-URI) to post record. This is the anchor post, and the thread will be built around it. It can be any post in the tree, not necessarily a root post.
    pub anchor: String,
    /// How many levels of replies to include below the anchor.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub below: Option<i64>,
    /// Maximum of replies to include at each level of the thread, except for the direct replies to the anchor, which are (NOTE: currently, during unspecced phase) all returned (NOTE: later they might be paginated).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub branching_factor: Option<i64>,
    /// Sorting for the thread replies.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sort: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UnspeccedGetPostThreadV2Output {
    /// Whether this thread has additional replies. If true, a call can be made to the `getPostThreadOtherV2` endpoint to retrieve them.
    pub has_other_replies: bool,
    /// A flat list of thread items. The depth of each item is indicated by the depth property inside the item.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub thread: Vec<UnspeccedGetPostThreadV2ThreadItem>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub threadgate: Option<crate::api::app::bsky::FeedDefsThreadgateView>,
    /// Extra fields not defined in the schema.
    #[serde(flatten)]
    pub extra: std::collections::HashMap<String, serde_json::Value>,
}

/// UnspeccedGetPostThreadV2 — (NOTE: this endpoint is under development and WILL change without notice. Don't use it until it is moved out of `unspecced` or your application WILL break) Get posts in a thread. It is based in an anchor post at any depth of the tree, and returns posts above it (recursively resolving the parent, without further branching to their replies) and below it (recursive replies, with branching to their replies). Does not require auth, but additional metadata and filtering will be applied for authed requests.
pub async fn unspecced_get_post_thread_v2(
    client: &crate::xrpc::Client,
    params: &UnspeccedGetPostThreadV2Params,
) -> Result<UnspeccedGetPostThreadV2Output, crate::xrpc::Error> {
    client
        .query("app.bsky.unspecced.getPostThreadV2", params)
        .await
}

/// UnspeccedGetPostThreadV2ThreadItem object from app.bsky.unspecced.getPostThreadV2.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UnspeccedGetPostThreadV2ThreadItem {
    /// The nesting level of this item in the thread. Depth 0 means the anchor item. Items above have negative depths, items below have positive depths.
    pub depth: i64,
    pub uri: crate::syntax::AtUri,
    pub value: UnspeccedGetPostThreadV2ThreadItemValueUnion,
    /// Extra fields not defined in the schema (JSON).
    #[serde(flatten)]
    pub extra: std::collections::HashMap<String, serde_json::Value>,
    /// Extra fields not defined in the schema (CBOR).
    #[serde(skip)]
    pub extra_cbor: Vec<(String, Vec<u8>)>,
}

/// UnspeccedGetPostThreadV2ThreadItemValueUnion is a union type.
#[derive(Debug, Clone)]
pub enum UnspeccedGetPostThreadV2ThreadItemValueUnion {
    UnspeccedDefsThreadItemPost(Box<crate::api::app::bsky::UnspeccedDefsThreadItemPost>),
    UnspeccedDefsThreadItemNoUnauthenticated(
        Box<crate::api::app::bsky::UnspeccedDefsThreadItemNoUnauthenticated>,
    ),
    UnspeccedDefsThreadItemNotFound(Box<crate::api::app::bsky::UnspeccedDefsThreadItemNotFound>),
    UnspeccedDefsThreadItemBlocked(Box<crate::api::app::bsky::UnspeccedDefsThreadItemBlocked>),
    Unknown(crate::api::UnknownUnionVariant),
}

impl serde::Serialize for UnspeccedGetPostThreadV2ThreadItemValueUnion {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match self {
            UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemPost(inner) => {
                let mut map = serde_json::to_value(inner.as_ref()).map_err(serde::ser::Error::custom)?;
                if let serde_json::Value::Object(ref mut m) = map {
                    m.insert("$type".to_string(), serde_json::Value::String("app.bsky.unspecced.defs#threadItemPost".to_string()));
                }
                map.serialize(serializer)
            }
            UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemNoUnauthenticated(inner) => {
                let mut map = serde_json::to_value(inner.as_ref()).map_err(serde::ser::Error::custom)?;
                if let serde_json::Value::Object(ref mut m) = map {
                    m.insert("$type".to_string(), serde_json::Value::String("app.bsky.unspecced.defs#threadItemNoUnauthenticated".to_string()));
                }
                map.serialize(serializer)
            }
            UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemNotFound(inner) => {
                let mut map = serde_json::to_value(inner.as_ref()).map_err(serde::ser::Error::custom)?;
                if let serde_json::Value::Object(ref mut m) = map {
                    m.insert("$type".to_string(), serde_json::Value::String("app.bsky.unspecced.defs#threadItemNotFound".to_string()));
                }
                map.serialize(serializer)
            }
            UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemBlocked(inner) => {
                let mut map = serde_json::to_value(inner.as_ref()).map_err(serde::ser::Error::custom)?;
                if let serde_json::Value::Object(ref mut m) = map {
                    m.insert("$type".to_string(), serde_json::Value::String("app.bsky.unspecced.defs#threadItemBlocked".to_string()));
                }
                map.serialize(serializer)
            }
            UnspeccedGetPostThreadV2ThreadItemValueUnion::Unknown(v) => {
                if let Some(ref j) = v.json {
                    j.serialize(serializer)
                } else {
                    Err(serde::ser::Error::custom("no JSON data for unknown union variant"))
                }
            }
        }
    }
}

impl<'de> serde::Deserialize<'de> for UnspeccedGetPostThreadV2ThreadItemValueUnion {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let value = serde_json::Value::deserialize(deserializer)?;
        let type_str = value
            .get("$type")
            .and_then(|v| v.as_str())
            .unwrap_or_default();
        match type_str {
            "app.bsky.unspecced.defs#threadItemPost" => {
                let inner: crate::api::app::bsky::UnspeccedDefsThreadItemPost =
                    serde_json::from_value(value).map_err(serde::de::Error::custom)?;
                Ok(
                    UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemPost(
                        Box::new(inner),
                    ),
                )
            }
            "app.bsky.unspecced.defs#threadItemNoUnauthenticated" => {
                let inner: crate::api::app::bsky::UnspeccedDefsThreadItemNoUnauthenticated =
                    serde_json::from_value(value).map_err(serde::de::Error::custom)?;
                Ok(UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemNoUnauthenticated(Box::new(inner)))
            }
            "app.bsky.unspecced.defs#threadItemNotFound" => {
                let inner: crate::api::app::bsky::UnspeccedDefsThreadItemNotFound =
                    serde_json::from_value(value).map_err(serde::de::Error::custom)?;
                Ok(
                    UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemNotFound(
                        Box::new(inner),
                    ),
                )
            }
            "app.bsky.unspecced.defs#threadItemBlocked" => {
                let inner: crate::api::app::bsky::UnspeccedDefsThreadItemBlocked =
                    serde_json::from_value(value).map_err(serde::de::Error::custom)?;
                Ok(
                    UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemBlocked(
                        Box::new(inner),
                    ),
                )
            }
            _ => Ok(UnspeccedGetPostThreadV2ThreadItemValueUnion::Unknown(
                crate::api::UnknownUnionVariant {
                    r#type: type_str.to_string(),
                    json: Some(value),
                    cbor: None,
                },
            )),
        }
    }
}

impl UnspeccedGetPostThreadV2ThreadItemValueUnion {
    pub fn to_cbor(&self) -> Result<Vec<u8>, crate::cbor::CborError> {
        let mut buf = Vec::new();
        self.encode_cbor(&mut buf)?;
        Ok(buf)
    }

    pub fn encode_cbor(&self, buf: &mut Vec<u8>) -> Result<(), crate::cbor::CborError> {
        match self {
            UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemPost(inner) => inner.encode_cbor(buf),
            UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemNoUnauthenticated(inner) => inner.encode_cbor(buf),
            UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemNotFound(inner) => inner.encode_cbor(buf),
            UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemBlocked(inner) => inner.encode_cbor(buf),
            UnspeccedGetPostThreadV2ThreadItemValueUnion::Unknown(v) => {
                if let Some(ref data) = v.cbor {
                    buf.extend_from_slice(data);
                    Ok(())
                } else {
                    Err(crate::cbor::CborError::InvalidCbor("no CBOR data for unknown union variant".into()))
                }
            }
        }
    }

    pub fn from_cbor(data: &[u8]) -> Result<Self, crate::cbor::CborError> {
        let mut decoder = crate::cbor::Decoder::new(data);
        let result = Self::decode_cbor(&mut decoder)?;
        if !decoder.is_empty() {
            return Err(crate::cbor::CborError::InvalidCbor("trailing data".into()));
        }
        Ok(result)
    }

    pub fn decode_cbor(decoder: &mut crate::cbor::Decoder) -> Result<Self, crate::cbor::CborError> {
        // Save position, decode the value, look for $type key.
        let start = decoder.position();
        let val = decoder.decode()?;
        let end = decoder.position();
        let raw = &decoder.raw_input()[start..end];
        let entries = match val {
            crate::cbor::Value::Map(entries) => entries,
            _ => {
                return Err(crate::cbor::CborError::InvalidCbor(
                    "expected map for union".into(),
                ));
            }
        };
        let type_str = entries
            .iter()
            .find(|(k, _)| *k == "$type")
            .and_then(|(_, v)| match v {
                crate::cbor::Value::Text(s) => Some(*s),
                _ => None,
            })
            .unwrap_or_default();
        match type_str {
            "app.bsky.unspecced.defs#threadItemPost" => {
                let mut dec = crate::cbor::Decoder::new(raw);
                let inner =
                    crate::api::app::bsky::UnspeccedDefsThreadItemPost::decode_cbor(&mut dec)?;
                Ok(
                    UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemPost(
                        Box::new(inner),
                    ),
                )
            }
            "app.bsky.unspecced.defs#threadItemNoUnauthenticated" => {
                let mut dec = crate::cbor::Decoder::new(raw);
                let inner =
                    crate::api::app::bsky::UnspeccedDefsThreadItemNoUnauthenticated::decode_cbor(
                        &mut dec,
                    )?;
                Ok(UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemNoUnauthenticated(Box::new(inner)))
            }
            "app.bsky.unspecced.defs#threadItemNotFound" => {
                let mut dec = crate::cbor::Decoder::new(raw);
                let inner =
                    crate::api::app::bsky::UnspeccedDefsThreadItemNotFound::decode_cbor(&mut dec)?;
                Ok(
                    UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemNotFound(
                        Box::new(inner),
                    ),
                )
            }
            "app.bsky.unspecced.defs#threadItemBlocked" => {
                let mut dec = crate::cbor::Decoder::new(raw);
                let inner =
                    crate::api::app::bsky::UnspeccedDefsThreadItemBlocked::decode_cbor(&mut dec)?;
                Ok(
                    UnspeccedGetPostThreadV2ThreadItemValueUnion::UnspeccedDefsThreadItemBlocked(
                        Box::new(inner),
                    ),
                )
            }
            _ => Ok(UnspeccedGetPostThreadV2ThreadItemValueUnion::Unknown(
                crate::api::UnknownUnionVariant {
                    r#type: type_str.to_string(),
                    json: None,
                    cbor: Some(raw.to_vec()),
                },
            )),
        }
    }
}

impl UnspeccedGetPostThreadV2ThreadItem {
    pub fn to_cbor(&self) -> Result<Vec<u8>, crate::cbor::CborError> {
        let mut buf = Vec::new();
        self.encode_cbor(&mut buf)?;
        Ok(buf)
    }

    pub fn encode_cbor(&self, buf: &mut Vec<u8>) -> Result<(), crate::cbor::CborError> {
        if self.extra_cbor.is_empty() {
            // Fast path: no extra fields to merge.
            let count = 3u64;
            crate::cbor::Encoder::new(&mut *buf).encode_map_header(count)?;
            crate::cbor::Encoder::new(&mut *buf).encode_text("uri")?;
            crate::cbor::Encoder::new(&mut *buf).encode_text(self.uri.as_str())?;
            crate::cbor::Encoder::new(&mut *buf).encode_text("depth")?;
            crate::cbor::Encoder::new(&mut *buf).encode_i64(self.depth)?;
            crate::cbor::Encoder::new(&mut *buf).encode_text("value")?;
            self.value.encode_cbor(buf)?;
        } else {
            // Slow path: merge known fields with extra_cbor, sort, encode.
            let mut pairs: Vec<(&str, Vec<u8>)> = Vec::new();
            {
                let mut vbuf = Vec::new();
                crate::cbor::Encoder::new(&mut vbuf).encode_text(self.uri.as_str())?;
                pairs.push(("uri", vbuf));
            }
            {
                let mut vbuf = Vec::new();
                crate::cbor::Encoder::new(&mut vbuf).encode_i64(self.depth)?;
                pairs.push(("depth", vbuf));
            }
            {
                let mut vbuf = Vec::new();
                self.value.encode_cbor(&mut vbuf)?;
                pairs.push(("value", vbuf));
            }
            for (k, v) in &self.extra_cbor {
                pairs.push((k.as_str(), v.clone()));
            }
            pairs.sort_by(|a, b| crate::cbor::cbor_key_cmp(a.0, b.0));
            crate::cbor::Encoder::new(&mut *buf).encode_map_header(pairs.len() as u64)?;
            for (k, v) in &pairs {
                crate::cbor::Encoder::new(&mut *buf).encode_text(k)?;
                buf.extend_from_slice(v);
            }
        }
        Ok(())
    }

    pub fn from_cbor(data: &[u8]) -> Result<Self, crate::cbor::CborError> {
        let mut decoder = crate::cbor::Decoder::new(data);
        let result = Self::decode_cbor(&mut decoder)?;
        if !decoder.is_empty() {
            return Err(crate::cbor::CborError::InvalidCbor("trailing data".into()));
        }
        Ok(result)
    }

    pub fn decode_cbor(decoder: &mut crate::cbor::Decoder) -> Result<Self, crate::cbor::CborError> {
        let val = decoder.decode()?;
        let entries = match val {
            crate::cbor::Value::Map(entries) => entries,
            _ => return Err(crate::cbor::CborError::InvalidCbor("expected map".into())),
        };

        let mut field_uri: Option<crate::syntax::AtUri> = None;
        let mut field_depth: Option<i64> = None;
        let mut field_value: Option<UnspeccedGetPostThreadV2ThreadItemValueUnion> = None;
        let mut extra_cbor: Vec<(String, Vec<u8>)> = Vec::new();

        for (key, value) in entries {
            match key {
                "uri" => {
                    if let crate::cbor::Value::Text(s) = value {
                        field_uri = Some(
                            crate::syntax::AtUri::try_from(s)
                                .map_err(|e| crate::cbor::CborError::InvalidCbor(e.to_string()))?,
                        );
                    } else {
                        return Err(crate::cbor::CborError::InvalidCbor("expected text".into()));
                    }
                }
                "depth" => match value {
                    crate::cbor::Value::Unsigned(n) => {
                        field_depth = Some(n as i64);
                    }
                    crate::cbor::Value::Signed(n) => {
                        field_depth = Some(n);
                    }
                    _ => {
                        return Err(crate::cbor::CborError::InvalidCbor(
                            "expected integer".into(),
                        ));
                    }
                },
                "value" => {
                    let raw = crate::cbor::encode_value(&value)?;
                    let mut dec = crate::cbor::Decoder::new(&raw);
                    field_value = Some(UnspeccedGetPostThreadV2ThreadItemValueUnion::decode_cbor(
                        &mut dec,
                    )?);
                }
                _ => {
                    let raw = crate::cbor::encode_value(&value)?;
                    extra_cbor.push((key.to_string(), raw));
                }
            }
        }

        Ok(UnspeccedGetPostThreadV2ThreadItem {
            uri: field_uri.ok_or_else(|| {
                crate::cbor::CborError::InvalidCbor("missing required field 'uri'".into())
            })?,
            depth: field_depth.ok_or_else(|| {
                crate::cbor::CborError::InvalidCbor("missing required field 'depth'".into())
            })?,
            value: field_value.ok_or_else(|| {
                crate::cbor::CborError::InvalidCbor("missing required field 'value'".into())
            })?,
            extra: std::collections::HashMap::new(),
            extra_cbor,
        })
    }
}