Skip to content

Rust

Simple: a minimal payload

use easeo_core::{SEOConfig, SEOEntity, EntityType, build_seo_payload};

let config = SEOConfig {
    canonical_host: "example.com".into(),
    public_base_url: "https://example.com".into(),
    ..Default::default()
};

let entity = SEOEntity {
    entity_type: EntityType::Post,
    title: Some("Hello World".into()),
    excerpt: Some("An example post.".into()),
    ..Default::default()
};

let payload = build_seo_payload(&entity, "/blog/hello", &config)?;
assert_eq!(payload.canonical, "https://example.com/blog/hello");

Simple: render and hash

println!("{}", payload.render_html()?);

let hash = easeo_core::hashing::hash_payload(&payload)?;
let etag = easeo_core::hashing::etag_payload(&payload)?;

Complex: full configuration

use easeo_core::{Robots, SEOConfig, SEOImage, URLPolicy, TrailingSlash};

let config = SEOConfig {
    canonical_host: "shop.example.com".into(),
    public_base_url: "https://shop.example.com".into(),
    url_policy: URLPolicy {
        enforce_https: true,
        lowercase_paths: true,
        trailing_slash: TrailingSlash::Never,
        collapse_duplicate_slashes: true,
        strip_tracking_params: true,
        allowed_query_params: vec!["page".into(), "q".into()],
    },
    site_name: Some("Example Shop".into()),
    title_template: Some("{title} - Example Shop".into()),
    default_robots: Robots { index: true, follow: true, ..Default::default() },
    default_og_image: Some(SEOImage {
        url: "https://shop.example.com/assets/og-image.png".into(),
        width: Some(1200),
        height: Some(630),
        alt: Some("Example Shop".into()),
    }),
    publisher_name: Some("Example Shop".into()),
    locale: Some("en_US".into()),
    search_url_template: Some(
        "https://shop.example.com/search?q={search_term_string}".into(),
    ),
    ..Default::default()
};

Complex: overrides

use easeo_core::{SEOOverrides, build_seo_payload_with_overrides};

let overrides = SEOOverrides {
    meta_title: Some("A one-off title".into()),
    skip_title_template: true,
    ..Default::default()
};

let payload = build_seo_payload_with_overrides(&entity, "/blog/hello", &config, &overrides)?;

Complex: a custom schema generator

use easeo_core::registry::SchemaRegistry;

let mut registry = SchemaRegistry::new();
registry.register("Podcast", |ctx| {
    serde_json::json!({
        "@context": "https://schema.org",
        "@type": "Podcast",
        "name": ctx.title,
        "url": ctx.canonical,
    })
});

The registry is consumed through the lower-level easeo_core::payload::build_seo_payload(entity, route, config, overrides, registry) function.

Complex: a data-driven build

use easeo_core::{SEOEntity, EntityType, build_seo_payload};

struct Product {
    name: String,
    slug: String,
    description: String,
    price: String,
    sku: String,
}

fn seo_for(product: &Product, config: &SEOConfig) -> Result<SEOPayload, EaseoError> {
    let entity = SEOEntity {
        entity_type: EntityType::Product,
        title: Some(product.name.clone()),
        excerpt: Some(product.description.clone()),
        slug: Some(product.slug.clone()),
        sku: Some(product.sku.clone()),
        price: Some(product.price.clone()),
        price_currency: Some("USD".into()),
        availability: Some("InStock".into()),
        ..Default::default()
    };

    build_seo_payload(&entity, &format!("/products/{}", product.slug), config)
}

Complex: tests

#[test]
fn seo_is_deterministic() {
    let a = build_seo_payload(&entity, "/blog/hello", &config).unwrap();
    let b = build_seo_payload(&entity, "/blog/hello", &config).unwrap();
    assert_eq!(a, b);
    assert_eq!(
        easeo_core::hashing::hash_payload(&a).unwrap(),
        easeo_core::hashing::hash_payload(&b).unwrap()
    );
}