use std::collections::{BTreeMap, HashMap};
use serde::{Deserialize, Serialize};
use crate::citation::Reference;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SurfDoc {
pub front_matter: Option<FrontMatter>,
pub blocks: Vec<Block>,
pub source: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct FrontMatter {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub doc_type: Option<DocType>,
#[serde(rename = "format", skip_serializing_if = "Option::is_none")]
pub format: Option<Format>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<DocStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<Scope>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<Confidence>,
#[serde(skip_serializing_if = "Option::is_none")]
pub related: Option<Vec<Related>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub contributors: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub workspace: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub decision: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, serde_yaml::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Related {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub relationship: Option<Relationship>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Relationship {
Produces,
Consumes,
References,
Supersedes,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DocType {
Doc,
Guide,
Conversation,
Plan,
Agent,
Preference,
Report,
Proposal,
Incident,
Review,
App,
Manifest,
Website,
Web,
Deck,
Slides,
Presentation,
Paper,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Format {
#[serde(alias = "IEEE", alias = "Ieee")]
Ieee,
#[serde(alias = "ACM", alias = "Acm")]
Acm,
#[serde(alias = "Article", alias = "ARTICLE")]
Article,
#[serde(alias = "MLA", alias = "Mla")]
Mla,
#[serde(alias = "APA", alias = "Apa")]
Apa,
#[serde(alias = "Chicago", alias = "CHICAGO")]
Chicago,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RenderProfile {
Document,
Web,
Presentation,
Paper(Format),
Report(Format),
}
pub fn render_profile(doc_type: Option<DocType>, format: Option<Format>) -> RenderProfile {
match doc_type {
None => RenderProfile::Document,
Some(dt) => match dt {
DocType::Website | DocType::Web => RenderProfile::Web,
DocType::Deck | DocType::Slides | DocType::Presentation => {
RenderProfile::Presentation
}
DocType::Paper => RenderProfile::Paper(format.unwrap_or(Format::Article)),
DocType::Report => RenderProfile::Report(format.unwrap_or(Format::Mla)),
DocType::Doc
| DocType::Guide
| DocType::Conversation
| DocType::Plan
| DocType::Agent
| DocType::Preference
| DocType::Proposal
| DocType::Incident
| DocType::Review
| DocType::App
| DocType::Manifest => RenderProfile::Document,
},
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DocStatus {
Draft,
Active,
Closed,
Archived,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Scope {
Personal,
WorkspacePrivate,
Workspace,
Repo,
Public,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Confidence {
Low,
Medium,
High,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum Block {
Unknown {
name: String,
attrs: Attrs,
content: String,
span: Span,
},
Markdown {
content: String,
span: Span,
},
Callout {
callout_type: CalloutType,
title: Option<String>,
content: String,
span: Span,
},
Data {
id: Option<String>,
format: DataFormat,
sortable: bool,
headers: Vec<String>,
rows: Vec<Vec<String>>,
raw_content: String,
span: Span,
},
Code {
lang: Option<String>,
file: Option<String>,
highlight: Vec<String>,
content: String,
span: Span,
},
Tasks {
items: Vec<TaskItem>,
span: Span,
},
Decision {
status: DecisionStatus,
date: Option<String>,
deciders: Vec<String>,
content: String,
span: Span,
},
Metric {
label: String,
value: String,
trend: Option<Trend>,
unit: Option<String>,
span: Span,
},
Summary {
content: String,
span: Span,
},
Cite {
reference: Reference,
span: Span,
},
Bibliography {
style: Option<Format>,
span: Span,
},
Figure {
src: String,
caption: Option<String>,
alt: Option<String>,
width: Option<String>,
span: Span,
},
Diagram {
diagram_type: String,
title: Option<String>,
content: String,
span: Span,
},
Tabs {
tabs: Vec<TabPanel>,
span: Span,
},
Columns {
columns: Vec<ColumnContent>,
span: Span,
},
Quote {
content: String,
attribution: Option<String>,
cite: Option<String>,
span: Span,
},
Cta {
label: String,
href: String,
primary: bool,
icon: Option<String>,
span: Span,
},
Nav {
items: Vec<NavItem>,
logo: Option<String>,
#[serde(default)]
groups: Vec<NavGroup>,
#[serde(default)]
brand: Option<String>,
#[serde(default)]
brand_reg: bool,
#[serde(default)]
cta: Option<NavItem>,
#[serde(default)]
drawer: bool,
#[serde(default)]
minimal: bool,
span: Span,
},
HeroImage {
src: String,
alt: Option<String>,
span: Span,
},
Testimonial {
content: String,
author: Option<String>,
role: Option<String>,
company: Option<String>,
span: Span,
},
Style {
properties: Vec<StyleProperty>,
span: Span,
},
Faq {
items: Vec<FaqItem>,
span: Span,
},
PricingTable {
headers: Vec<String>,
rows: Vec<Vec<String>>,
span: Span,
},
Site {
domain: Option<String>,
properties: Vec<StyleProperty>,
span: Span,
},
Page {
route: String,
layout: Option<String>,
title: Option<String>,
sidebar: bool,
content: String,
children: Vec<Block>,
span: Span,
},
Deck {
properties: Vec<StyleProperty>,
span: Span,
},
Slide {
layout: Option<SlideLayout>,
kicker: Option<String>,
notes: Option<String>,
content: String,
children: Vec<Block>,
span: Span,
},
Embed {
src: String,
embed_type: Option<EmbedType>,
width: Option<String>,
height: Option<String>,
title: Option<String>,
span: Span,
},
Form {
fields: Vec<FormField>,
submit_label: Option<String>,
action: Option<String>,
method: Option<String>,
honeypot: bool,
span: Span,
},
Banner {
headline: Option<String>,
subtitle: Option<String>,
buttons: Vec<HeroButton>,
id: Option<String>,
content: String,
span: Span,
},
ProductGrid {
groups: Vec<ProductGroup>,
tiles: bool,
span: Span,
},
PostGrid {
title: Option<String>,
subtitle: Option<String>,
items: Vec<PostItem>,
span: Span,
},
Gate {
title: Option<String>,
subtitle: Option<String>,
action: String,
field_label: Option<String>,
submit_label: Option<String>,
error: Option<String>,
span: Span,
},
Gallery {
items: Vec<GalleryItem>,
columns: Option<u32>,
span: Span,
},
Footer {
sections: Vec<FooterSection>,
copyright: Option<String>,
social: Vec<SocialLink>,
#[serde(default)]
brand: Option<String>,
#[serde(default)]
brand_reg: bool,
#[serde(default)]
brand_logo: Option<String>,
#[serde(default)]
tagline: Option<String>,
span: Span,
},
Details {
title: Option<String>,
open: bool,
content: String,
span: Span,
},
Divider {
label: Option<String>,
span: Span,
},
Hero {
headline: Option<String>,
subtitle: Option<String>,
badge: Option<String>,
align: String,
image: Option<String>,
image_alt: Option<String>,
layout: Option<String>,
transparent: bool,
buttons: Vec<HeroButton>,
content: String,
span: Span,
},
Features {
cards: Vec<FeatureCard>,
cols: Option<u32>,
span: Span,
},
Steps {
steps: Vec<StepItem>,
span: Span,
},
Stats {
items: Vec<StatItem>,
span: Span,
},
Comparison {
headers: Vec<String>,
rows: Vec<Vec<String>>,
highlight: Option<String>,
span: Span,
},
Logo {
src: String,
alt: Option<String>,
size: Option<u32>,
span: Span,
},
Toc {
depth: u32,
entries: Vec<TocEntry>,
span: Span,
},
BeforeAfter {
before_items: Vec<BeforeAfterItem>,
after_items: Vec<BeforeAfterItem>,
transition: Option<String>,
span: Span,
},
Pipeline {
steps: Vec<PipelineStep>,
span: Span,
},
Section {
bg: Option<String>,
headline: Option<String>,
subtitle: Option<String>,
content: String,
children: Vec<Block>,
span: Span,
},
ProductCard {
title: String,
subtitle: Option<String>,
badge: Option<String>,
badge_color: Option<String>,
body: String,
features: Vec<String>,
cta_label: Option<String>,
cta_href: Option<String>,
span: Span,
},
List {
source: String,
display: ListDisplay,
item_template: String,
filters: Vec<ListFilter>,
sort: Option<SortSpec>,
preload: bool,
span: Span,
},
Board {
source: String,
columns: Vec<String>,
card_template: Option<String>,
preload: bool,
span: Span,
},
Action {
method: HttpMethod,
target: String,
label: String,
fields: Vec<FormField>,
confirm: Option<String>,
span: Span,
},
FilterBar {
target_selector: String,
fields: Vec<FilterField>,
span: Span,
},
Search {
source: String,
placeholder: Option<String>,
span: Span,
},
Dashboard {
source: String,
refresh: Option<u32>,
span: Span,
},
ChatInput {
action: String,
placeholder: Option<String>,
modes: Vec<String>,
span: Span,
},
Feed {
source: String,
stream: bool,
span: Span,
},
Store {
title: Option<String>,
currency: Option<String>,
items: Vec<StoreItem>,
span: Span,
},
Booking {
title: Option<String>,
service_label: Option<String>,
services: Vec<BookingService>,
days: Vec<BookingDay>,
span: Span,
},
Editor {
source: Option<String>,
lang: Option<String>,
preview: bool,
span: Span,
},
Chart {
chart_type: ChartType,
source: String,
period: Option<String>,
title: Option<String>,
data: Option<ChartData>,
span: Span,
},
SplitPane {
ratio: String,
span: Span,
},
App {
name: String,
binary: Option<String>,
region: Option<String>,
port: Option<u32>,
platform: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
auth: Option<String>,
content: String,
children: Vec<Block>,
span: Span,
},
Build {
base: Option<String>,
runtime: Option<String>,
edition: Option<String>,
properties: Vec<StyleProperty>,
span: Span,
},
InfraDatabase {
name: Option<String>,
shared_auth: bool,
volume_gb: Option<u32>,
properties: Vec<StyleProperty>,
span: Span,
},
Deploy {
env: Option<String>,
app: Option<String>,
machines: Option<u32>,
memory: Option<u32>,
auto_stop: Option<String>,
min_machines: Option<u32>,
strategy: Option<String>,
properties: Vec<StyleProperty>,
span: Span,
},
InfraEnv {
tier: Option<String>,
entries: Vec<EnvEntry>,
span: Span,
},
Health {
path: Option<String>,
method: Option<String>,
grace: Option<String>,
interval: Option<String>,
timeout: Option<String>,
span: Span,
},
Concurrency {
concurrency_type: Option<String>,
hard_limit: Option<u32>,
soft_limit: Option<u32>,
force_https: bool,
span: Span,
},
Cicd {
provider: Option<String>,
properties: Vec<StyleProperty>,
span: Span,
},
Smoke {
script: Option<String>,
checks: Vec<SmokeCheck>,
span: Span,
},
Domains {
entries: Vec<DomainEntry>,
span: Span,
},
Crates {
entries: Vec<CrateEntry>,
span: Span,
},
DeployUrls {
entries: Vec<StyleProperty>,
span: Span,
},
Volumes {
entries: Vec<VolumeEntry>,
span: Span,
},
Model {
name: String,
fields: Vec<ModelField>,
span: Span,
},
Route {
method: HttpMethod,
path: String,
auth: Option<String>,
returns: Option<String>,
body: Option<String>,
handler: Option<String>,
content: String,
span: Span,
},
Auth {
provider: AuthProvider,
session: Option<String>,
roles: Vec<String>,
default_role: Option<String>,
span: Span,
},
Binding {
source: String,
target: String,
events: Vec<BindingEvent>,
span: Span,
},
Schema {
name: String,
fields: Vec<SchemaField>,
span: Span,
},
Use {
crates: Vec<CrateDep>,
span: Span,
},
AppEnv {
vars: Vec<EnvVar>,
span: Span,
},
AppDeploy {
region: Option<String>,
scale: Option<u32>,
domain: Option<String>,
memory: Option<String>,
properties: Vec<(String, String)>,
span: Span,
},
Row {
icon: String,
title: String,
description: String,
href: Option<String>,
state: RowState,
span: Span,
},
InfoCard {
intent: String,
title: String,
subtitle: String,
summary: String,
image: Option<String>,
facts: Vec<[String; 2]>,
steps: Vec<String>,
state: RowState,
span: Span,
},
AppShell {
layout: String,
children: Vec<Block>,
span: Span,
},
Sidebar {
position: String,
collapsible: bool,
width: Option<u32>,
children: Vec<Block>,
span: Span,
},
Panel {
position: String,
resizable: bool,
height: Option<u32>,
desktop_only: bool,
children: Vec<Block>,
span: Span,
},
TabBar {
active: Option<String>,
items: Vec<TabBarItem>,
span: Span,
},
TabContent {
tab: String,
children: Vec<Block>,
span: Span,
},
Toolbar {
items: Vec<ToolbarItem>,
span: Span,
},
Drawer {
name: String,
position: String,
width: Option<u32>,
trigger: Option<String>,
children: Vec<Block>,
span: Span,
},
Modal {
name: String,
title: Option<String>,
children: Vec<Block>,
span: Span,
},
CommandPalette {
trigger: Option<String>,
items: Vec<CommandItem>,
span: Span,
},
CodeEditor {
lang: Option<String>,
source: Option<String>,
line_numbers: bool,
content: String,
span: Span,
},
BlockEditor {
source: Option<String>,
span: Span,
},
Terminal {
shell: Option<String>,
cwd: Option<String>,
span: Span,
},
NavTree {
source: Option<String>,
on_select: Option<String>,
on_rename: Option<String>,
on_delete: Option<String>,
span: Span,
},
Badge {
value: String,
color: Option<String>,
span: Span,
},
SuggestionChips {
source: Option<String>,
max: Option<u32>,
dismissible: bool,
span: Span,
},
ChatThread {
source: Option<String>,
on_action: Option<String>,
span: Span,
},
ChatInputSimple {
placeholder: Option<String>,
action: Option<String>,
span: Span,
},
Progress {
source: Option<String>,
steps: Vec<ProgressStep>,
span: Span,
},
LogStream {
source: Option<String>,
tail: Option<u32>,
span: Span,
},
ProblemList {
source: Option<String>,
span: Span,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RowState {
Default,
Loading,
Empty,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TabBarItem {
pub id: String,
pub label: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ToolbarItem {
Button {
label: Option<String>,
action: Option<String>,
icon: Option<String>,
style: Option<String>,
disabled: bool,
},
Separator,
Spacer,
Badge {
value: String,
color: Option<String>,
},
Dropdown {
label: String,
options: Option<String>,
action: Option<String>,
},
Text {
value: String,
editable: bool,
action: Option<String>,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CommandItem {
pub label: String,
pub description: Option<String>,
pub action: Option<String>,
pub icon: Option<String>,
pub group: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProgressStep {
pub label: String,
pub status: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CalloutType {
Info,
Warning,
Danger,
Tip,
Note,
Success,
Context,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DataFormat {
Table,
Csv,
Json,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskItem {
pub done: bool,
pub text: String,
pub assignee: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DecisionStatus {
Proposed,
Accepted,
Rejected,
Superseded,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Trend {
Up,
Down,
Flat,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TabPanel {
pub label: String,
pub content: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColumnContent {
pub content: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StyleProperty {
pub key: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FaqItem {
pub question: String,
pub answer: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NavItem {
pub label: String,
pub href: String,
pub icon: Option<String>,
#[serde(default)]
pub image: Option<String>,
#[serde(default)]
pub external: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NavGroup {
pub label: Option<String>,
pub items: Vec<NavItem>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EmbedType {
Map,
Video,
Audio,
Generic,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum SlideLayout {
Cover,
Title,
Section,
#[default]
Bullets,
Cards,
Compare,
Stat,
Quote,
Demo,
Image,
Two,
Code,
Blank,
}
impl SlideLayout {
pub fn from_name(s: &str) -> Option<SlideLayout> {
match s.trim().to_ascii_lowercase().as_str() {
"cover" => Some(SlideLayout::Cover),
"title" => Some(SlideLayout::Title),
"section" => Some(SlideLayout::Section),
"bullets" | "default" => Some(SlideLayout::Bullets),
"cards" => Some(SlideLayout::Cards),
"compare" | "comparison" => Some(SlideLayout::Compare),
"stat" | "stats" => Some(SlideLayout::Stat),
"quote" => Some(SlideLayout::Quote),
"demo" => Some(SlideLayout::Demo),
"image" => Some(SlideLayout::Image),
"two" | "split" | "two-column" | "two-col" | "twocolumn" => Some(SlideLayout::Two),
"code" => Some(SlideLayout::Code),
"blank" => Some(SlideLayout::Blank),
_ => None,
}
}
pub fn css_class(self) -> &'static str {
match self {
SlideLayout::Cover => "cover",
SlideLayout::Title => "title",
SlideLayout::Section => "section",
SlideLayout::Bullets => "bullets",
SlideLayout::Cards => "cards",
SlideLayout::Compare => "compare",
SlideLayout::Stat => "stat",
SlideLayout::Quote => "quote",
SlideLayout::Demo => "demo",
SlideLayout::Image => "image",
SlideLayout::Two => "two",
SlideLayout::Code => "code",
SlideLayout::Blank => "blank",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FormField {
pub label: String,
pub name: String,
pub field_type: FormFieldType,
pub required: bool,
pub placeholder: Option<String>,
pub options: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FormFieldType {
Text,
Email,
Tel,
Date,
Number,
Password,
Select,
Textarea,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GalleryItem {
pub src: String,
pub caption: Option<String>,
pub alt: Option<String>,
pub category: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FooterSection {
pub heading: String,
pub links: Vec<NavItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SocialLink {
pub platform: String,
pub href: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HeroButton {
pub label: String,
pub href: String,
pub primary: bool,
#[serde(default)]
pub external: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductGroup {
pub label: Option<String>,
pub items: Vec<ProductItem>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cols: Option<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductItem {
pub name: String,
pub href: String,
pub emblem: Option<String>,
pub tagline: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cta1_label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cta1_href: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cta2_label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cta2_href: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bg: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostItem {
pub title: String,
pub href: String,
pub meta: Option<String>,
pub excerpt: Option<String>,
pub image: Option<String>,
pub external: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeatureCard {
pub title: String,
pub icon: Option<String>,
pub body: String,
pub link_label: Option<String>,
pub link_href: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepItem {
pub title: String,
pub time: Option<String>,
pub body: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatItem {
pub value: String,
pub label: String,
pub color: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TocEntry {
pub text: String,
pub id: String,
pub level: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BeforeAfterItem {
pub label: String,
pub detail: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PipelineStep {
pub label: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvEntry {
pub name: String,
pub default_value: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SmokeCheck {
pub method: String,
pub path: String,
pub expected: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DomainEntry {
pub domain: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrateEntry {
pub name: String,
pub source: Option<String>,
pub features: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VolumeEntry {
pub name: String,
pub mount: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ListDisplay {
Card,
Table,
Compact,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListFilter {
pub field: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortSpec {
pub field: String,
pub descending: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum HttpMethod {
Get,
Post,
Put,
Patch,
Delete,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilterField {
pub label: String,
pub name: String,
pub options: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StoreItem {
pub name: String,
pub price: String,
pub blurb: Option<String>,
pub badge: Option<String>,
pub category: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BookingService {
pub name: String,
pub duration: Option<String>,
pub price: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BookingDay {
pub date: String,
pub slots: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ChartType {
Line,
Bar,
Pie,
Area,
Scatter,
Donut,
StackedBar,
Radar,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChartSeries {
pub name: String,
pub values: Vec<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChartData {
pub categories: Vec<String>,
pub series: Vec<ChartSeries>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelField {
pub name: String,
pub field_type: ModelFieldType,
pub constraints: Vec<FieldConstraint>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ModelFieldType {
Uuid,
String,
Int,
Float,
Bool,
Datetime,
Text,
Json,
Money,
Image,
Email,
Url,
Enum(Vec<String>),
Ref(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FieldConstraint {
Primary,
Auto,
Required,
Optional,
Unique,
Max(u32),
Min(u32),
Default(String),
Index,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AuthProvider {
Email,
OAuth,
ApiKey,
Token,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BindingEvent {
pub event: String,
pub action: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SchemaField {
pub name: String,
pub field_type: ModelFieldType,
pub constraints: Vec<FieldConstraint>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CrateDep {
pub name: String,
pub version: Option<String>,
pub features: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EnvVar {
pub name: String,
pub description: Option<String>,
pub required: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum InlineExt {
Evidence {
tier: Option<u8>,
source: Option<String>,
text: String,
},
Status {
value: String,
},
}
pub type Attrs = BTreeMap<String, AttrValue>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AttrValue {
String(String),
Number(f64),
Bool(bool),
Null,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Span {
pub start_line: usize,
pub end_line: usize,
pub start_offset: usize,
pub end_offset: usize,
}
impl Span {
pub const SYNTHETIC: Span = Span {
start_line: 0,
end_line: 0,
start_offset: 0,
end_offset: 0,
};
}
#[cfg(test)]
mod doc_type_format_tests {
use super::*;
fn parse_fm(yaml: &str) -> FrontMatter {
serde_yaml::from_str::<FrontMatter>(yaml).expect("front matter should parse")
}
#[test]
fn new_doc_types_deserialize() {
assert_eq!(parse_fm("type: presentation").doc_type, Some(DocType::Presentation));
assert_eq!(parse_fm("type: web").doc_type, Some(DocType::Web));
assert_eq!(parse_fm("type: website").doc_type, Some(DocType::Website));
assert_eq!(parse_fm("type: paper").doc_type, Some(DocType::Paper));
assert_eq!(parse_fm("type: report").doc_type, Some(DocType::Report));
}
#[test]
fn existing_doc_types_unchanged() {
assert_eq!(parse_fm("type: deck").doc_type, Some(DocType::Deck));
assert_eq!(parse_fm("type: slides").doc_type, Some(DocType::Slides));
assert_eq!(parse_fm("type: doc").doc_type, Some(DocType::Doc));
}
#[test]
fn format_values_deserialize() {
for (s, want) in [
("ieee", Format::Ieee),
("acm", Format::Acm),
("article", Format::Article),
("mla", Format::Mla),
("apa", Format::Apa),
("chicago", Format::Chicago),
] {
assert_eq!(parse_fm(&format!("format: {s}")).format, Some(want));
}
}
#[test]
fn format_aliases_are_case_insensitive() {
assert_eq!(parse_fm("format: IEEE").format, Some(Format::Ieee));
assert_eq!(parse_fm("format: ACM").format, Some(Format::Acm));
assert_eq!(parse_fm("format: MLA").format, Some(Format::Mla));
assert_eq!(parse_fm("format: APA").format, Some(Format::Apa));
assert_eq!(parse_fm("format: Chicago").format, Some(Format::Chicago));
assert_eq!(parse_fm("format: Article").format, Some(Format::Article));
}
#[test]
fn format_missing_defaults_to_none() {
assert_eq!(parse_fm("type: paper").format, None);
assert_eq!(parse_fm("title: Hello").format, None);
}
#[test]
fn every_format_maps_to_a_render_profile() {
for f in [
Format::Ieee,
Format::Acm,
Format::Article,
Format::Mla,
Format::Apa,
Format::Chicago,
] {
assert_eq!(
render_profile(Some(DocType::Paper), Some(f)),
RenderProfile::Paper(f)
);
assert_eq!(
render_profile(Some(DocType::Report), Some(f)),
RenderProfile::Report(f)
);
}
}
#[test]
fn render_profile_mapping_is_total_and_stable() {
assert_eq!(render_profile(None, None), RenderProfile::Document);
assert_eq!(render_profile(Some(DocType::Web), None), RenderProfile::Web);
assert_eq!(render_profile(Some(DocType::Website), None), RenderProfile::Web);
assert_eq!(
render_profile(Some(DocType::Deck), None),
RenderProfile::Presentation
);
assert_eq!(
render_profile(Some(DocType::Slides), None),
RenderProfile::Presentation
);
assert_eq!(
render_profile(Some(DocType::Presentation), None),
RenderProfile::Presentation
);
assert_eq!(
render_profile(Some(DocType::Paper), None),
RenderProfile::Paper(Format::Article)
);
assert_eq!(
render_profile(Some(DocType::Report), None),
RenderProfile::Report(Format::Mla)
);
for dt in [DocType::Doc, DocType::Guide, DocType::Plan, DocType::App] {
assert_eq!(render_profile(Some(dt), None), RenderProfile::Document);
}
}
}