trustfall 0.8.1

The trustfall query engine, empowering you to query everything.
Documentation
use feed_rs::model::{Content, Entry, Feed, FeedType, Image, Link, Text};
use trustfall::{
    provider::{
        field_property, resolve_neighbors_with as neighbors, resolve_property_with as property,
        BasicAdapter, ContextIterator, ContextOutcomeIterator, EdgeParameters, TrustfallEnumVertex,
        VertexIterator,
    },
    FieldValue,
};
use trustfall_core::interpreter::AsVertex;

#[derive(Debug)]
pub(crate) struct FeedAdapter<'a> {
    data: &'a [Feed],
}

impl<'a> FeedAdapter<'a> {
    pub(crate) fn new(data: &'a [Feed]) -> Self {
        Self { data }
    }
}

#[derive(Debug, Clone, TrustfallEnumVertex)]
pub(crate) enum Vertex<'a> {
    Feed(&'a Feed),
    FeedText(&'a Text),
    ChannelImage(&'a Image),
    FeedLink(&'a Link),
    FeedEntry(&'a Entry),
    FeedContent(&'a Content),
}

macro_rules! iterable {
    ($conversion:ident, $field:ident, $neighbor_variant:path) => {
        |vertex| -> VertexIterator<'a, Self::Vertex> {
            let vertex = vertex.$conversion().expect("conversion failed");
            let neighbors = vertex.$field.iter().map($neighbor_variant);
            Box::new(neighbors)
        }
    };
}

impl<'a> BasicAdapter<'a> for FeedAdapter<'a> {
    type Vertex = Vertex<'a>;

    fn resolve_starting_vertices(
        &self,
        edge_name: &str,
        _parameters: &EdgeParameters,
    ) -> VertexIterator<'a, Self::Vertex> {
        match edge_name {
            "Feed" => Box::new(self.data.iter().map(Vertex::Feed)),
            "FeedAtUrl" => {
                todo!()
            }
            _ => unimplemented!("{}", edge_name),
        }
    }

    fn resolve_property<V: AsVertex<Self::Vertex> + 'a>(
        &self,
        contexts: ContextIterator<'a, V>,
        type_name: &str,
        property_name: &str,
    ) -> ContextOutcomeIterator<'a, V, FieldValue> {
        match type_name {
            "Feed" => match property_name {
                "id" => property(contexts, field_property!(as_feed, id)),
                "updated" => property(
                    contexts,
                    field_property!(as_feed, updated, { updated.map(|t| t.timestamp()).into() }),
                ),
                "language" => property(contexts, field_property!(as_feed, language)),
                "published" => property(
                    contexts,
                    field_property!(as_feed, published, {
                        published.map(|t| t.timestamp()).into()
                    }),
                ),
                "ttl" => property(contexts, field_property!(as_feed, ttl)),
                "feed_type" => property(
                    contexts,
                    field_property!(as_feed, feed_type, {
                        let value = match feed_type {
                            FeedType::Atom => "Atom",
                            FeedType::JSON => "JSON",
                            FeedType::RSS0 => "RSS0",
                            FeedType::RSS1 => "RSS1",
                            FeedType::RSS2 => "RSS2",
                        };
                        value.to_owned().into()
                    }),
                ),
                _ => unreachable!("type {type_name} property {property_name} not found"),
            },
            "FeedText" => match property_name {
                "content" => property(contexts, field_property!(as_feed_text, content)),
                "src" => property(contexts, field_property!(as_feed_text, src)),
                "content_type" => property(
                    contexts,
                    field_property!(as_feed_text, content_type, {
                        content_type.essence_str().to_owned().into()
                    }),
                ),
                _ => unreachable!("type {type_name} property {property_name} not found"),
            },
            "FeedEntry" => match property_name {
                "id" => property(contexts, field_property!(as_feed_entry, id)),
                "source" => property(contexts, field_property!(as_feed_entry, source)),
                "updated" => property(
                    contexts,
                    field_property!(as_feed_entry, updated, {
                        updated.map(|t| t.timestamp()).into()
                    }),
                ),
                "published" => property(
                    contexts,
                    field_property!(as_feed_entry, published, {
                        published.map(|t| t.timestamp()).into()
                    }),
                ),
                _ => unreachable!("type {type_name} property {property_name} not found"),
            },
            "FeedContent" => match property_name {
                "body" => property(contexts, field_property!(as_feed_content, body)),
                "length" => property(contexts, field_property!(as_feed_content, length)),
                "content_type" => property(
                    contexts,
                    field_property!(as_feed_content, content_type, {
                        content_type.essence_str().to_owned().into()
                    }),
                ),
                _ => unreachable!("type {type_name} property {property_name} not found"),
            },
            "FeedLink" => match property_name {
                "href" => property(contexts, field_property!(as_feed_link, href)),
                "rel" => property(contexts, field_property!(as_feed_link, rel)),
                "media_type" => property(contexts, field_property!(as_feed_link, media_type)),
                "href_lang" => property(contexts, field_property!(as_feed_link, href_lang)),
                "title" => property(contexts, field_property!(as_feed_link, title)),
                "length" => property(contexts, field_property!(as_feed_link, length)),
                _ => unreachable!("type {type_name} property {property_name} not found"),
            },
            "ChannelImage" => match property_name {
                "uri" => property(contexts, field_property!(as_channel_image, uri)),
                "title" => property(contexts, field_property!(as_channel_image, title)),
                "width" => property(contexts, field_property!(as_channel_image, width)),
                "height" => property(contexts, field_property!(as_channel_image, height)),
                "description" => property(contexts, field_property!(as_channel_image, description)),
                _ => unreachable!("type {type_name} property {property_name} not found"),
            },
            _ => unreachable!("type {type_name} not found"),
        }
    }

    fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
        &self,
        contexts: ContextIterator<'a, V>,
        type_name: &str,
        edge_name: &str,
        _parameters: &EdgeParameters,
    ) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
        match type_name {
            "Feed" => match edge_name {
                "title" => neighbors(contexts, iterable!(as_feed, title, Vertex::FeedText)),
                "description" => {
                    neighbors(contexts, iterable!(as_feed, description, Vertex::FeedText))
                }
                "rights" => neighbors(contexts, iterable!(as_feed, rights, Vertex::FeedText)),
                "icon" => neighbors(contexts, iterable!(as_feed, icon, Vertex::ChannelImage)),
                "links" => neighbors(contexts, iterable!(as_feed, links, Vertex::FeedLink)),
                "entries" => neighbors(contexts, iterable!(as_feed, entries, Vertex::FeedEntry)),
                _ => unreachable!("type {type_name} edge {edge_name} not found"),
            },
            "FeedEntry" => match edge_name {
                "title" => neighbors(contexts, iterable!(as_feed_entry, title, Vertex::FeedText)),
                "content" => {
                    neighbors(contexts, iterable!(as_feed_entry, content, Vertex::FeedContent))
                }
                "links" => neighbors(contexts, iterable!(as_feed_entry, links, Vertex::FeedLink)),
                "summary" => {
                    neighbors(contexts, iterable!(as_feed_entry, summary, Vertex::FeedText))
                }
                "rights" => neighbors(contexts, iterable!(as_feed_entry, rights, Vertex::FeedText)),
                _ => unreachable!("type {type_name} edge {edge_name} not found"),
            },
            "FeedContent" => match edge_name {
                "src" => neighbors(contexts, iterable!(as_feed_content, src, Vertex::FeedLink)),
                _ => unreachable!("type {type_name} edge {edge_name} not found"),
            },
            "ChannelImage" => match edge_name {
                "link" => neighbors(contexts, iterable!(as_channel_image, link, Vertex::FeedLink)),
                _ => unreachable!("type {type_name} edge {edge_name} not found"),
            },
            _ => unreachable!("type {type_name} not found"),
        }
    }

    fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
        &self,
        _contexts: ContextIterator<'a, V>,
        type_name: &str,
        coerce_to_type: &str,
    ) -> ContextOutcomeIterator<'a, V, bool> {
        unimplemented!("{type_name} -> {coerce_to_type}")
    }
}