use super::{Edits, MODULES_MARKER, insert_after_marker};
use crate::{CliResult, names::is_identifier, output::CliError, project::Project};
const DATA_MARKER: &str = "// ocre:data";
pub fn data(project: &Project, name: &str) -> CliResult {
if !is_identifier(name) {
return Err(CliError::new(format!("invalid data name `{name}`"))
.hint("use snake_case starting with a letter, e.g. `ocre g data countries`"));
}
let mut edits = Edits::new(project);
let registry = match edits.read("src/data/mod.rs")? {
Some(source) => source,
None => {
let lib = edits.read("src/lib.rs")?.unwrap_or_default();
let lib = insert_after_marker(&lib, MODULES_MARKER, "mod data;").ok_or_else(|| {
CliError::new(format!("src/lib.rs is missing the `{MODULES_MARKER}` marker"))
.hint(format!("put `{MODULES_MARKER}` on its own line where `mod` declarations go"))
})?;
edits.update("src/lib.rs", lib);
format!(
"//! Read-only data compiled into the Worker. `ocre g data` adds modules below.\n\n// An app rarely reads every loader it has.\n#![allow(dead_code)]\n\n{DATA_MARKER}\n"
)
}
};
let registry = insert_after_marker(®istry, DATA_MARKER, &format!("pub mod {name};"))
.ok_or_else(|| CliError::new(format!("src/data/mod.rs is missing the `{DATA_MARKER}` marker")))?;
edits.update("src/data/mod.rs", registry);
edits.create(&format!("data/{name}.json"), "[\n { \"name\": \"Example\" }\n]\n".to_owned())?;
edits.create(&format!("src/data/{name}.rs"), loader_rs(name))?;
let mut report = edits.apply("generate data")?;
report.next = vec![
format!("put the entries in data/{name}.json and their fields in `Entry` (src/data/{name}.rs)"),
format!("read them with `crate::data::{name}::all()`"),
];
Ok(report)
}
fn loader_rs(name: &str) -> String {
format!(
r#"//! `data/{name}.json`, compiled into the Worker and parsed on first use (once
//! per Worker instance). Generated by `ocre g data {name}`.
use std::sync::LazyLock;
use serde::Deserialize;
/// One entry of data/{name}.json: declare the fields it holds.
#[derive(Debug, Clone, Deserialize)]
pub struct Entry {{
pub name: String,
}}
static ENTRIES: LazyLock<Vec<Entry>> = LazyLock::new(|| {{
ocre::serde_json::from_str(include_str!("../../data/{name}.json"))
.unwrap_or_else(|err| panic!("data/{name}.json does not match `Entry`: {{err}}"))
}});
/// Every entry of data/{name}.json, in file order.
pub fn all() -> &'static [Entry] {{
&ENTRIES
}}
#[cfg(test)]
mod tests {{
#[test]
fn the_file_matches_entry() {{
assert!(!super::all().is_empty());
}}
}}
"#
)
}