use ordinary_config::{Content, ContentDefinition, OrdinaryConfig};
use ordinary_types::{ContentObject, Field};
use std::io::Write;
use std::path::Path;
use tracing::instrument;
#[instrument(err)]
pub fn add_def(path: &str, name: &String, json_fields: &str) -> anyhow::Result<()> {
let proj_path = Path::new(path);
let mut app_config = OrdinaryConfig::get(path)?;
let mut file_path = "./content.json".to_string();
let mut content_defs = if let Some(d) = app_config.content {
file_path = d.file_path;
d.definitions
} else {
let mut file = fs_err::File::create(proj_path.join("content.json"))?;
file.write_all(b"[]")?;
vec![]
};
if content_defs.len() < 255
&& let Ok(fields) = serde_json::from_str::<Vec<Field>>(json_fields)
{
let next_idx = u8::try_from(content_defs.len())?;
content_defs.push(ContentDefinition {
idx: next_idx,
name: name.clone(),
fields,
lifecycle: None,
});
app_config.content = Some(Content {
definitions: content_defs,
file_path,
update: None,
});
let ordinary_json = serde_json::to_string_pretty(&app_config)?;
let mut file = fs_err::File::create(proj_path.join("ordinary.json"))?;
file.write_all(ordinary_json.as_bytes())?;
} else {
tracing::error!("cannot support more than 255 content definitions for a single project.");
}
Ok(())
}
#[instrument(err)]
pub fn edit_def(
path: &str,
idx: u8,
name: Option<String>,
json_fields: &str,
) -> anyhow::Result<()> {
let proj_path = Path::new(path);
let mut app_config = OrdinaryConfig::get(path)?;
if let Some(content) = app_config.content.as_mut()
&& let Some(def) = content.definitions.iter_mut().find(|v| v.idx == idx)
&& let Ok(fields) = serde_json::from_str::<Vec<Field>>(json_fields)
{
def.fields = fields;
if let Some(name) = name {
def.name = name;
}
}
let ordinary_json = serde_json::to_string_pretty(&app_config)?;
let mut file = fs_err::File::create(proj_path.join("ordinary.json"))?;
file.write_all(ordinary_json.as_bytes())?;
Ok(())
}
#[instrument(skip_all, err)]
fn before_all(
proj_path: &Path,
content_def: &ContentDefinition,
config: &OrdinaryConfig,
) -> anyhow::Result<()> {
if let Some(lifecycle_config) = &config.lifecycle
&& let Some(before_all) = &lifecycle_config.before_all
{
OrdinaryConfig::exec_lifecycle_script(proj_path, &None, "all", "before", before_all)?;
}
if let Some(lifecycle_config) = &content_def.lifecycle
&& let Some(before_all) = &lifecycle_config.before_all
{
OrdinaryConfig::exec_lifecycle_script(
proj_path,
&None,
&content_def.name,
"before_all",
before_all,
)?;
}
Ok(())
}
#[instrument(skip_all, err)]
pub fn add_obj(path: &str, object_json: &str) -> anyhow::Result<()> {
let proj_path = Path::new(path);
let app_config = OrdinaryConfig::get(path)?;
let new_obj: ContentObject = serde_json::from_str(object_json)?;
if let Some(content) = &app_config.content
&& let Some(content_def) = content
.definitions
.iter()
.find(|d| d.name == new_obj.instance_of)
{
let content_json = fs_err::read_to_string(proj_path.join(&content.file_path))?;
let mut content_objects: Vec<ContentObject> = serde_json::from_str(&content_json)?;
before_all(proj_path, content_def, &app_config)?;
if let Some(lifecycle_config) = &content_def.lifecycle
&& let Some(on_add) = &lifecycle_config.on_add
&& let Some(before) = &on_add.before
{
OrdinaryConfig::exec_lifecycle_script(
proj_path,
&None,
&content_def.name,
"before",
before,
)?;
}
content_objects.push(new_obj.clone());
let new_objects = serde_json::to_string_pretty(&content_objects)?;
let mut file = fs_err::File::create(proj_path.join(&content.file_path))?;
file.write_all(new_objects.as_bytes())?;
if let Some(lifecycle_config) = &content_def.lifecycle
&& let Some(on_add) = &lifecycle_config.on_add
&& let Some(after) = &on_add.after
{
OrdinaryConfig::exec_lifecycle_script(
proj_path,
&Some(serde_json::to_string(&new_obj)?),
&content_def.name,
"after",
after,
)?;
}
}
Ok(())
}
#[instrument(skip_all, err)]
pub fn edit_obj(path: &str, object_json: &str) -> anyhow::Result<()> {
let proj_path = Path::new(path);
let app_config = OrdinaryConfig::get(path)?;
let new_obj: ContentObject = serde_json::from_str(object_json)?;
if let Some(content) = &app_config.content
&& let Some(content_def) = content
.definitions
.iter()
.find(|d| d.name == new_obj.instance_of)
{
let content_json = fs_err::read_to_string(proj_path.join(&content.file_path))?;
let mut content_objects: Vec<ContentObject> = serde_json::from_str(&content_json)?;
before_all(proj_path, content_def, &app_config)?;
if let Some(obj) = content_objects
.iter_mut()
.find(|obj| obj.uuid == new_obj.uuid && obj.instance_of == new_obj.instance_of)
{
if let Some(lifecycle_config) = &content_def.lifecycle
&& let Some(on_edit) = &lifecycle_config.on_edit
&& let Some(before) = &on_edit.before
{
OrdinaryConfig::exec_lifecycle_script(
proj_path,
&None,
&content_def.name,
"before",
before,
)?;
}
obj.fields.clone_from(&new_obj.fields);
}
let new_objects = serde_json::to_string_pretty(&content_objects)?;
let mut file = fs_err::File::create(proj_path.join(&content.file_path))?;
file.write_all(new_objects.as_bytes())?;
if let Some(lifecycle_config) = &content_def.lifecycle
&& let Some(on_edit) = &lifecycle_config.on_edit
&& let Some(after) = &on_edit.after
{
OrdinaryConfig::exec_lifecycle_script(
proj_path,
&Some(serde_json::to_string(&new_obj)?),
&content_def.name,
"after",
after,
)?;
}
}
Ok(())
}
#[instrument(skip_all, err)]
pub fn delete_obj(path: &str, instance_of: &str, uuid: &str) -> anyhow::Result<()> {
let proj_path = Path::new(path);
let app_config = OrdinaryConfig::get(path)?;
if let Some(content) = &app_config.content
&& let Some(content_def) = content.definitions.iter().find(|d| d.name == instance_of)
{
let content_json = fs_err::read_to_string(proj_path.join(&content.file_path))?;
let mut content_objects: Vec<ContentObject> = serde_json::from_str(&content_json)?;
before_all(proj_path, content_def, &app_config)?;
let mut removed = None;
if let Some(pos) = content_objects
.iter()
.position(|obj| obj.uuid == uuid && obj.instance_of == instance_of)
{
if let Some(lifecycle_config) = &content_def.lifecycle
&& let Some(on_delete) = &lifecycle_config.on_delete
&& let Some(before) = &on_delete.before
{
OrdinaryConfig::exec_lifecycle_script(
proj_path,
&None,
&content_def.name,
"before",
before,
)?;
}
removed = Some(content_objects.remove(pos));
}
let new_objects = serde_json::to_string_pretty(&content_objects)?;
let mut file = fs_err::File::create(proj_path.join(&content.file_path))?;
file.write_all(new_objects.as_bytes())?;
if let Some(removed) = removed
&& let Some(lifecycle_config) = &content_def.lifecycle
&& let Some(on_delete) = &lifecycle_config.on_delete
&& let Some(after) = &on_delete.after
{
OrdinaryConfig::exec_lifecycle_script(
proj_path,
&Some(serde_json::to_string(&removed)?),
&content_def.name,
"after",
after,
)?;
}
}
Ok(())
}