typestar

api_models.rs in Rust

A complete model set for an API payload: parse, validate, summarize.

use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Payload {
    lang: String,
    #[serde(default)]
    generated: bool,
    tours: Vec<Tour>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Tour {
    slug: String,
    title: String,
    sections: Vec<Section>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Section {
    title: String,
    steps: Vec<Step>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "lowercase")]
enum Step {
    Snippet { file: String, lines: u32 },
    Script { file: String, lines: u32 },
}

impl Step {
    fn file(&self) -> &str {
        match self {
            Step::Snippet { file, .. } | Step::Script { file, .. } => file,
        }
    }

    fn lines(&self) -> u32 {
        match self {
            Step::Snippet { lines, .. } | Step::Script { lines, .. } => *lines,
        }
    }
}

fn summarize(payload: &Payload) -> BTreeMap<&str, u32> {
    let mut totals = BTreeMap::new();
    for tour in &payload.tours {
        let steps: u32 =
            tour.sections.iter().map(|s| s.steps.len() as u32).sum();
        totals.insert(tour.slug.as_str(), steps);
    }
    totals
}

fn main() -> Result<(), serde_json::Error> {
    let raw = r#"{
      "lang": "rust",
      "tours": [{
        "slug": "traits",
        "title": "Traits & generics",
        "sections": [{
          "title": "Trait objects",
          "steps": [
            {"kind": "snippet", "file": "rs_trait_objects.rs", "lines": 38},
            {"kind": "script", "file": "shapes.rs", "lines": 96}
          ]
        }]
      }]
    }"#;

    let payload: Payload = serde_json::from_str(raw)?;
    println!("{} generated={}", payload.lang, payload.generated);
    println!("{:?}", summarize(&payload));

    let longest = payload
        .tours
        .iter()
        .flat_map(|t| &t.sections)
        .flat_map(|s| &s.steps)
        .max_by_key(|step| step.lines());
    if let Some(step) = longest {
        println!("longest: {} at {} lines", step.file(), step.lines());
    }

    println!("{}", serde_json::to_string(&payload)?.len());
    Ok(())
}

How it works

  1. The models mirror the JSON with renames and defaults.
  2. A tagged enum covers the two kinds of step.
  3. The summary walks the parsed tree, not the raw text.

Keywords and builtins used here

The run, in numbers

Lines
89
Characters to type
1855
Tokens
465
Three-star pace
115 tpm

At the three-star pace of 115 tokens a minute, this run takes about 243 seconds.

Type this snippet

Step 1 of 1 in Encore, step 11 of 11 in Serde & JSON.

← Previous