Note: This site is currently "Under construction". I'm migrating to a new version of my site building software. Lots of things are in a state of disrepair as a result (for example, footnote links aren't working). It's all part of the process of building in public. Most things should still be readable though.

Create An Atom Feed In Rust

Introduction

This is what I'm doing to make an Atom feed for my site. More notes below the code.

Code

//! ```cargo
//! [dependencies]
//! atom_syndication = "0.12.2"
//! chrono = { version = "0.4.26", features = ["std"] }
//! uuid = { version = "1.6.1", features = ["v5"] }
//! ```

use atom_syndication::*;
use chrono::prelude::*;
use std::collections::BTreeMap;
use std::fs;
use uuid::Uuid;


fn main() {

  let feed_constant_uuid4 = Uuid::try_parse(
    "b71fc261-fc1a-4220-b43f-2be4b216c0b2"
  ).unwrap();

  let generation_date = chrono::offset::Utc::now().fixed_offset();

  let author = Person {
    name: "Alan W. Smith".to_string(), 
    email: None,
    uri: Some("https://www.example.com/".to_string())
  };

  let entry_uuid5 = format!("urn:uuid:{}", Uuid::new_v5(
    &feed_constant_uuid4, b"unque_file_id_alfa")
      .as_hyphenated()
      .to_string()
  );

  let entry_title = Text {
    value: "This Is A Title".to_string(),
    base: None, 
    lang: None,
    r#type: TextType::Text
  };

  let tz_hour_offset = 5; 
  let tz = FixedOffset::west_opt(tz_hour_offset * 60 * 60).unwrap();
  let updated_date = NaiveDateTime::parse_from_str("2023-12-17 10:33:55", "%Y-%m-%d %H:%M:%S")
    .unwrap()
    .and_local_timezone(tz)
    .unwrap();

  let entry_content = Some(
    Content {
      base: None, 
      lang: None, 
      value: Some("<p>Hello, world</p>".to_string()),
      src: Some("https://www.example.com/some_path.html".to_string()),
      content_type: Some("html".to_string()),
    }
  );

  let entry = Entry {
      id: entry_uuid5,
      title: entry_title,
      authors: vec![author.clone()],
      categories: vec![],
      content: entry_content,
      summary: None, 
      source: None,
      rights: None, 
      published: None, 
      links: vec![],
      contributors: vec![],
      updated: updated_date,
      extensions: BTreeMap::new() 
  };

  let entries = vec![entry];

  let feed_id = format!(
    "urn:uuid:{}",
    feed_constant_uuid4.as_hyphenated()
  );

  let feed_link = Link{
    href: "https://www.example.com/atom.xml".to_string(),
    rel: "self".to_string(),
    hreflang: None, 
    length: None, 
    mime_type: None,
    title: None
  };

  let feed = Feed {
    title: "My Example Feed".into(),
    id: feed_id,
    updated: generation_date,
    authors: vec![author],
    links: vec![feed_link],
    entries,
    ..Default::default()
  };

  let config = WriteConfig {
    write_document_declaration: true,
    indent_size: Some(2),
  };

  let file = fs::OpenOptions::new()
    .create(true)
    .truncate(true)
    .write(true)
    .open("/Users/alan/Desktop/atom-test.xml").unwrap();

  // output the file
  feed.write_with_config(&file, config).unwrap();

  // print it for demo
  println!("{}", feed.to_string());
}

Results

<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>My Example Feed</title>
  <id>urn:uuid:b71fc261-fc1a-4220-b43f-2be4b216c0b2</id>
  <updated>2024-01-02T23:29:27.999342+00:00</updated>
  <author>
    <name>Alan W. Smith</name>
    <uri>https://www.example.com/</uri>
  </author>
  <link href="https://www.example.com/atom.xml" rel="self"/>
  <entry>
    <title>This Is A Title</title>
    <id>urn:uuid:b1507332-d46c-5bcf-929a-f89c59e01bac</id>
    <updated>2023-12-17T10:33:55+05:00</updated>
    <author>
      <name>Alan W. Smith</name>
      <uri>https://www.example.com/</uri>
    </author>
    <content type="html" src="https://www.example.com/some_path.html">&lt;p&gt;Hello, world&lt;/p&gt;</content>
  </entry>
</feed>

References