Skip to main content

coven_merge/
audience_sql.rs

1//! An audience's one text spelling, `store` or the circle's canonical id,
2//! which app rows, coven's tables, object paths and saved state share.
3
4use crate::Audience;
5use coven_foundation::id_source::CircleId;
6use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, Value, ValueRef};
7use rusqlite::ToSql;
8
9impl std::fmt::Display for Audience {
10    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11        match self {
12            Self::Store => f.write_str("store"),
13            Self::Circle(circle) => std::fmt::Display::fmt(circle, f),
14        }
15    }
16}
17
18impl std::str::FromStr for Audience {
19    type Err = InvalidAudience;
20    /// Only the spelling [`Audience`]'s `Display` writes: any other spelling
21    /// of the same circle id names no audience.
22    fn from_str(text: &str) -> Result<Self, Self::Err> {
23        if text == "store" {
24            return Ok(Self::Store);
25        }
26        coven_foundation::id_source::canonical_uuid(text)
27            .map(|id| Self::Circle(CircleId(id)))
28            .map_err(|_not_canonical| InvalidAudience)
29    }
30}
31
32/// Text that is neither `store` nor a circle's canonical id.
33#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
34#[error("not an audience: `store` or a circle's canonical id")]
35pub struct InvalidAudience;
36
37impl From<&Audience> for Value {
38    fn from(audience: &Audience) -> Self {
39        Self::Text(audience.to_string())
40    }
41}
42
43impl ToSql for Audience {
44    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
45        Ok(ToSqlOutput::Owned(self.into()))
46    }
47}
48
49impl FromSql for Audience {
50    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
51        value.as_str()?.parse().map_err(|error| FromSqlError::Other(Box::new(error)))
52    }
53}
54
55#[cfg(test)]
56#[path = "audience_sql_tests.rs"]
57mod tests;